数据类型的意义
定义了数据占用的内存空间大小;
定义了数据的取值范围;
定义了数据在内存中的存储格式;
决定了数据的运算规则;
为编译器提供了检查依据,编译器需要知道某个特定值是属于哪个数据类型,以便正确解释数据。
编译完成的结果——机器码,仅仅是CPU运行的指令集,此时不再作任何判断,就是执行而已。
数据对齐的端模式(Endian)
与处理器有关,分为两种:
1. 小端对齐,将低位存放在低地址。Intel 80X86系列。2. 大端对齐,将高位存放在低地址。PowerPC处理器。
比如对于一个32位的数0x12345678,小端对齐:高地址12 34 56 78低地址;大端对齐
结构体所占内存大小总结:
(1)对于结构体的大小,声明的顺序是很重要的比如:
structA{char c1;int a;float f; char c2;};大小为16 而structB{char c1; char c2;inta;float f;};大小为12
原因是32位的CPU因为字长是32位的,所以读内存数据时,一次会读取4字节的内容,且读取的4个字节数据的首地址是被4整除的(0、4、8、c)!即一次读取0,1,2,3这样的连续的地址,而2,3,4,5这样的地址只能读取两次!为了加快CPU的存取速度,C编译器在处理数据时,把结构体变量中的成员的摆放按照某个对齐原则规划,这就叫边界对齐。
总之就是用空间来换效率,所以计算大小的时候要先看最宽的基本数据类型,然后根据对齐原则,大小一般是最宽基本数据类型大小的整数倍
(2)结构体中含有结构体
比如
struct A struct B
{ {
int a; char c1;
char b; struct A a;
}; double d;
}B;
B的大小为24,计算的时候最宽的数据类型不是以A为整体,而是依然看基本数据类型。
再举个例子:
structA{char c1; double d; }; struct B {int i; struct A a; char ch;}b; b的大小为32当然学以致用的话B最好这样定义struct B {int i; char ch; struct A a; }b;这样大小为24了
其他修饰变量的关键字
volatile const extern register static
(1)volatile
首先举个例子 假定x为全局变量
int x = 0,val1,val2;
val1 = x;
…/*一些不使用x的代码*/
val2 = x;
编译器可能将x优化存到寄存器当中。而x是易变的变量,在两次访问中可能x的值变化了,则访问x应该使用内存中的“原版”
应该这样定义x : volatile intx;//对该变量不进行寄存器优化
(2)const
可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。
int* const p = &i; 常指针,指针永远指向同一个内存地址
const int* p =&i; 指向常量的指针,不能通过指针p来改变变量i
(3)register
寄存器变量--可以被存储在cpu寄存器中,这仅仅是个请求,未必成功
(4)static
如果是静态局部变量,影响变量的生存期(和普通局部变量比较起来更“长命”),作用域不变,仍然由函数块域限制(即“{}”限制)。
如果是全局静态变量,生存期不变(和全局变量比较起来一样“长命”),但将变量访问本地化(作用域在本.C文件)。
如果是函数,将函数的调用本地化,只可以由本.C中的其他函数调用。
(5)extern
修饰变量, 变量声明,不是定义,随后可能要使用该变量,无论该变量在其他.C中实现(定义),还是在本.C中实现(定义)都可以继续访问变量。
修饰函数, 要引用某函数的声明,不是实现(定义)。引用后可以调用该函数,无论该函数在其他.C中实现(定义),还是在本.C中实现(定义)
这两个结论的前提是变量定义时或者函数实现时没有static修饰!也就是说static比extern强硬!