Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1351904
  • 博文数量: 370
  • 博客积分: 10654
  • 博客等级: 中将
  • 技术积分: 4396
  • 用 户 组: 普通用户
  • 注册时间: 2010-08-07 15:44
文章分类

全部博文(370)

文章存档

2012年(36)

2011年(195)

2010年(139)

分类: C/C++

2010-11-06 10:17:20

一些关于C语言中常用的知识点,

一,
1)return语句不可返回指向“栈内存”的“指针”,因为该内存在函数体结束的时候被自动销毁。 如果需要返回指针变量的话,需要在堆上位指针分配空间
2)const修饰的变量属性为只读,编译器通常不为普通const变量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的值,没有了存储与读内存的操作,效率较高。
   从汇编角度看,const定义的只读变量只是给出了对应的内存地址,而不是像#define一样给出立即数,所以,const定义的只读变量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干拷贝。
 
补充,const修饰的为只读变量,仍属于变量的意义,不是常量,不能用于定义数组的维数,也不能放在case关键字的后面
 
3)C99中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员,但结构中的柔性数组成员前面必须至少一个其他成员。
typedef struct st_type               typedef struct st_type
{                                    { 
  int i;                                int i;
  int a[0];                  或者        int a[];
}type_a;                              }type_a;
此时sizeof(type_a) = 4,0元素数组没有占用空间。
type_a *p = (type_a *)malloc(sizeof(type_a) + 100*sizeof(int));
此时即可通过p->item[n]访问变长的数组,注意此时sizeof(type_a) = 4.
4)union结构,用于字符大小端的判断与转换,一个很有技巧的方法。
5)int main()
   {
     int a[5] = {1,2,3,4,5};
     int *ptr1 = (int *)(&a + 1);
     int *ptr2 = ((int)a + 1);
     printf("%x,%x",ptr1[-1],*ptr2);
     return 0;
   }
   我感觉第一个语句打印5,第二个打印2,有待验证。。
 
   结论:第一个打印为5,正确,因为&a代表着指向一个包含5个元素的数组的指针,每次加1会加整个数组的长度。
   第二个打印为2000000,这个有点复杂,(int)a+1把a的地址加1,这里只是单纯的在数值上加1,即原来地址为0x00000001,现在地址 为0x00000002,打印之后仍然按照int类型取4个字节求值打印,又涉及上字符排列的大小端,因此此时结果为2000000,证明电脑为小端存储 方式,若为大端,结果应为00000100
 
6)typedef struct student
   {
    //code
   }stu_st,*stu_pst;
   此时,const stu_pst stu3,stu_pst const stu4,这里的const分别修饰哪个对象?
   答案貌似是都用来修饰指针而非对象本身,有待确认
 
结论:在考虑const修饰哪个变量时,编译器是这样做的,首先忽略类型名,之后const离哪 个最近既是修饰哪个变量。在这里,stu_pst是struct student{..} *的别名,在做类型名的时候是一个整体,所以在解析的时候忽略到类型名,即可知const修饰的为指针
 

二,

1) y=x/*p,这条指令并不表示成这样:x除以p指向的内存里的值,把结果赋值给y。 在编译器上测试一下,会提示出错,它会把/*当作是一段注释的开始,把其后面的内容都当作注释,直到出现*/为止。
    可以修改为 y=x/(*p),甚至 y=x/ *p都可,最后在编译阶段去除注释内容,并不是单纯的全部清除,而是通过“空格”替换,因此诸如int/*...*/i;并不会出错,而in/*..*/t i;则会出错
 
2) 关于自加与自减运算,++和--。 ++、--作为前缀,必然是先自加或自减,然后再做别的运算;但是作为后缀时的自加、自减则与自己想象的不同。
i = 3; k =(i++) + (i++) + (i++);   那么printf("%d\n",k)的值是多少,本来以为会是12呢,测试才知道k = 9,这是因为对于后缀的自加和自减运算都是在本计算单位计算结束后再自加或自减,在例子中也就是当i遇到最后一个分号的时候才进行自加或自减的
 
3)移位运算符的优先级低于算术运算符,因此0x01<<2+3的结果不为7,而是32,先进行算术加法,再进行移位
 
第三章阅读笔记:
 
4) #define BSC //
    #define BMC /*
    #define EMC */
则,BSC my single-line comment
    BMC my multi-line comment EMC
都会出错,因为在编译器的处理流程中,注释先于预处理指令被处理,当这两行宏被展开时,注释语句已经处理完毕,此时再出现//或者/**/自然出错,因此试图用宏开始或结束一段注释是不行的
 
5)#define SUM(x) (x) + (x) 与 #define SUM (x) (x) + (x)不同,SUM与(x)间的空格只在定义时有用,在使用的时候,会自动被编译器给忽略
 
6)关于字节对齐,字、双字、四字在自然边界上不需要在内存中对齐(对字,双字,和四字来说,自然边界分别是偶数地址,可以被4整除的地址,和 可以被8整除的地址)。一个字或双字操作数跨越了4字节边界,或者一个四字操作数跨越了8字节边界,被认为是未对齐的,从而需要两次总线周期来访问内存。 一个字起始地址是奇数但却没有跨越字边界被认为是对齐的,能够在一个总线周期中被访问
struct my_struct1                        struct my_struct2
{                                        {
    char c1;                                 char c1;
    short s;                                 char c2;
    char  c2;                                short s;
    int i;                                   int i;
}                                         }
在这里sizeof(my_struct1) = 12; sizeof(my_struct2) = 8;
宏#pragma pack()可以改变编译器的默认对齐方式,指令#pragma pack(n),编译器将按照n个字节对齐,使用指令#pragma pack(),编译器将取消自定义对齐方式,在两个宏定义之间的代码将按照n字节对齐
对于结构来说,它的默认对齐方式就是它的所有成员使用的对齐参数中最大的一个
 
7)#define SQR(x) printf("The square of x is %d.\n",((x)*(x)));
则使用宏SQR(8);输出为The square of x is 64.
#define SQR(x) printf("The square of "#x" is %d.\n",((x)*(x)));
则使用宏SQR(8);输出为The square of 8 is 64.

 
阅读(717) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~