C语言学习笔记 By Ethan
1
1. 整型强制类型转换 A.相同位宽的类型相互转换时,比特不变,只是按照不同规则解释 B.由小位宽转换为大位宽类型时,由转换之前的数据类型决定填补什么 a.unsigned转换为unsigned类型,前面补0 b.unsigned转换为 signed类型,前面补0 c.signed转换为unsigned类型,前面补符号 d.signed转换为 signed类型,前面补符号
2.Volatile 修饰符 在本次线程内,当读取一个变量时,为提高存取速度,编译器优化时会先把变量读取到一个寄存器,以后再取变量时,直接从寄存器读取。当变量在本线程改变时,会同时把变量的新值Copy到该寄存器,以便保持一致。当变量/寄存器因别的线程而改变时,该寄存器/变量的值不会响应改变,从而造成应用程序读取的值和实际的变量值不一致。
Volatile指出变量时随时可能发生变化的,每次使用它的时候必须从i的地址中读取。比如一个寄存器变量或者表示一个端口数据就容易出错,所以Volatile保证对特殊地址的稳定访问。
a. 终端服务程序中修改的供其它程序检测的变量需要加Volatile修饰 b. 多任务环境下个任务共享的标志应该加Volatile c. 存储器映射的硬件寄存器通常也要加Volatile
3.Register 修饰符 Register变量必须是能被CPU接受的类型,一般为小于等于整型的长度。现在的C编译环境能比程序员做出更好的决定,实际上,许多编译程序都会忽略register修饰符,因为尽管它完全合法,但它仅仅是暗示而不是命令。
4. Struct存储空间的对齐方式 默认对齐方式 struct MyStruct { double da1; //偏移量为0,为8的整数倍,满足对齐方式,da1占用8个字节 char da2;//偏移量为8,为1的整数倍,满足对齐方式, da2占用1个字节 int da3;//偏移量为9,不是4的整数倍,不满足对齐方式,先补充3个字节,da3再占用4个字节 float da4;//偏移量为16,为4的倍数,满足对齐方式,da4占用4个字节 };//所有成员变量都分配了空间,空间大小为20,不是结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)8的倍数,所以需要补充4个字节。故最终,结构占用的空间为24个字节。 VC中提供了#pragma pack(n)来设定变量n以n字节对齐方式。 如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式;如果n小于该变量的类型所占用的字节数,那么偏移量必须满足为n的倍数,不用满足默认的对齐方式。
结构体最终的大小,如果n大于所有成员变量占用的字节数,那么总大小必须为占用空间最大的变量占
C语言学习笔记 By Ethan
用的字节数的倍数,否则为n的倍数 #pragma pack(push) //保存对齐状态 #pragma pack(4) //设定为4字节对齐 struct MyStruct { char da1; //偏移量为0,是1(1<对齐字节数4)的倍数,da1占用1个字节 double da2; //偏移量为1,不是对齐字节数4(对齐字节数4<8)的倍数,先补充3个字节,da2占用8个字节 int da3; //偏移量为12,是4(4=对齐字节数4)的倍数,da3占用4个字节 2
};//所有成员变量都分配了空间,空间大小为16,是对齐字节数4的倍数。 #pragma pack(pop)//恢复对齐状态 #pragma pack(6) //设定为6字节对齐 struct MyStruct { char da1; //偏移量为0,是1(1<对齐字节数6)的倍数,da1占用1个字节 double da2; //偏移量为1,不是对齐字节数6(对齐字节数6<8)的倍数,先补充5个字节,da2占用8个字节 int da3; //偏移量为14,不是4(4<对齐字节数6)的倍数,先补充2个字节,da3占用4个字节 };//所有成员变量都分配了空间,空间大小为20,因为20大于对齐字节数6,所以最后结构大小必须为6的倍数,于是先补充4个字节,最终结构体大小为24。 5.union 共同体 共用体表示几个变量共用一个内存位置,在不同的时间保存不同的数据类型和不同长度的变量,在同一时间只能存储其中一个成员变量的值。共用体的内存空间为联合中最大的变量长度的整数倍。
typedef union foo { char s[10]; //占内存空间10个字节 double i; //占内存空间8个字节 }; //占用最大内存空间为10字节,不是最大变量长度8字节的整数倍,补充6字节,最终占用16字节 6.Void 类型 a. 无返回值的函数最好标记为void型 b. 若函数无参数,应声明为void型 c. void* pvoid; (char*)pvoid++;
7.指针的强制类型转换 char a[]={0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88}; int *p; p=(int*)a; printf(\,*p,*(p+1),*(p+2)); 输出结果:44332211 88776655 cccccccc C语言学习笔记 By Ethan
3
int a[]={0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88}; char *p; p=(char*)a;
printf(\,*p,*(p+1),*(p+2));
输出结果:11 00 00
8.位域操作
1)与结构定义相仿,其形式为: struct bs
{
int a :8; int b :2; int c :6; };
2)几点说明:
a. 一个位域必须存储在同一个字节中,不能跨两个字节; b. 位域的长度不能大于指定类型固有长度;
c. 位域可以无位域名,这时它只用来作填充或调整位置。
struct k
{
int a :1;
int int b int c
:2; /*该2位不能使用*/ :3 :2
};
3)VC中内存对齐的准则是:
a. 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
b. 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍; c. 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式(系统会先为结构体成员按照对齐方式分配空间和填塞(padding),然后对变量进行位域操作)。
9.二维数组指针
int a[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}} 设数组a的首地址为1000
*a==>a[0]是第一个一维数组的数组名和首地址
从二维数组的角度来看,a是二维数组名,a代表整个二维数组的首地址,也是二维数组0行的首地址a+1代表第一行的首地址。
C语言学习笔记 By Ethan
4
a[0]是第一个一维数组的数组名和首地址,因此也为1000。*(a+0)或*a是与a[0]等效的, 它表示一维数组a[0] 0号元素的首地址,也为1000。&a[0][0]是二维数组a的0行0列元素首地址,同样是1000。因此,a,a[0],*(a+0),*a,&a[0][0]是相等的。
同理,a+1是二维数组1行的首地址,等于1008。a[1]是第二个一维数组的数组名和首地址,因此也为1008。&a[1][0]是二维数组a的1行0列元素地址,也是1008。因此a+1,a[1],*(a+1),&a[1][0]是等同的。
由此可得出:a+i,a[i],*(a+i),&a[i][0]是等同的。
此外,&a[i]和a[i]也是等同的。因为在二维数组中不能把&a[i]理解为元素a[i]的地址,不存在元素a[i]。C语言规定,它是一种地址计算方法,表示数组a第i行首地址。由此,我们得出:a[i],&a[i],*(a+i)和a+i也都是等同的。
另外,a[0]也可以看成是a[0]+0,是一维数组a[0]的0号元素的首地址,而a[0]+1则是a[0]的1号元素首地址,由此可得出a[i]+j则是一维数组a[i]的j号元素首地址,它等于&a[i][j]。
由a[i]=*(a+i)得a[i]+j=*(a+i)+j。由于*(a+i)+j是二维数组a的i行j列元素的首地址,所以,该元素的值等于*(*(a+i)+j)
9.函数指针
int max(int x,int y) { } typedef int(FUNC1)(int, int); void print(FUNC1 func, int mm, int nn) { } void main() { } print(max,1,2); int (*myMax)(int,int); myMax = max; printf(\,myMax(3,4)); printf(\,func(mm, nn)); return(x>y?x:y);
其中typedef int(FUNC1)(int, int) 定义了一个函数指针的同义字,用法比较特殊
10.sizeof与strlen取值
char a[] = {'A','B','C','D','E'}; char b[] = \; char * c = \; printf(\ %d %d\\n\,sizeof(a), sizeof(b), sizeof(c)); // 5 6 4 printf(\ %d %d\\n\,strlen(a), strlen(b), strlen(c)); // 19(原因是大括号内共19个字符?) 5 5
相关推荐: