#pragma 简介
#pragma是编译器指示字,用于指示编译器完成一些特定的动作。
#pragma所定义的很多指示字是编译器和操作系统特有的。
#pragma在不同的编译器间是不可移植的。
预处理器将忽略它不认识的#pragma指令。
两个不同的编译器可能以两种不同的方式解释同一条#pragma指令。
#pragma message 使用分析
message参数在大多数的编译器中都有相似的实现
message参数在编译时输出消息到编译输出窗口中
message可用于代码的版本控制
注意message是VC特有的编译器指示字,GCC中将其忽略。
举例说明:
#include <stdio.h>
#if defined(ANDROID20)
#pragma message("Compile Android SDK 2.0...")
#define VERSION "Android 2.0"
#elif defined(ANDROID23)
#pragma message("Compile Android SDK 2.3...")
#define VERSION "Android 2.3"
#elif defined(ANDROID40)
#pragma message("Compile Android SDK 4.0...")
#define VERSION "Android 4.0"
#else
#error Compile Version is not provided!
#endif
int main()
{
printf("%s\n", VERSION);
return 0;
}
pragma pack 和 内存对齐
#pragma pack能够改变编译器的默认对齐方式
不同类型的数据在内存中按照一定的规则排列,数据之间会存在一定的间隔;不是顺序的一个挨一个的排放,这就是对齐。默认情况下,编译器按照4个字节对齐。
上图两个结构体所占用的内存大小不同。
为什么需要内存对齐?
- CPU对内存的读取不是连续的,而是分成块读取的,块的大小只能是1、2、4、8、16字节。
- 当读取操作的数据未对齐,则需要两次总线周期来访问内存,因此性能会大打折扣。
- 某些硬件平台只能从规定的地址处取某些特定类型的数据,如只能读取偶地址,否则抛出硬件异常。
计算下面的例子:
sizeof( Test1 ) = 2+2+2+4=10字节,sizeof( Test2 ) = 1+1+2+4=8字节。
struct占用的内存大小计算法则:
- 第一个成员起始于0偏移处。
- 每个成员按其类型大小和pragma指定对齐参数n中较小的一个,作为该成员的对齐参数,进行对齐。
- 每个成员的偏移地址和所占用的内存大小都是是该成员的对齐参数的整数倍。
- 结构体作为成员时,它的对齐参数为该结构体中所有成员使用的对齐参数的最大值
- 结构体总长度必须为各个成员的对齐参数的公倍数。
如上面的test1结构体中,c1起始地址为0,它对齐参数为1,0是1 的整数倍。s起始地址为2,因为地址1不是对齐参数2的整数倍。c2起始地址为4,占1个字节,空1个字节。i的起始地址为6,占4个字节。最后的总长度为10字节。
test2结构体中,c1起始地址为0,它占1字节。c2起始地址为1,占1个字节。s起始地址为2,占2个字节。i的起始地址为4,占4个字节。最后的总长度为8字节。
在有数组的情况下,例如 char a[3],数据类型以char来算,对齐参数为1。
对于 union联合体 的大小的计算:
各成员共享一段内存,占用大小采用成员最大长度来作为对齐参数。
举例:
union
{
char flag[3];
short value;
} sampleUnion;
//sizeof(sampleUnion) = 4 按照short大小2对齐
Intel和微软的面试题
#include <stdio.h>
#pragma pack(8)
struct S1
{
short a;
long b;
};
struct S2
{
char c;
struct S1 d;
double e;
};
#pragma pack()
int main()
{
struct S2 s2;
printf("%d\n", sizeof(struct S1));
printf("%d\n", sizeof(struct S2));
printf("%d\n", (int)&(s2.d) - (int)&(s2.c));
return 0;
}
计算上面程序中打印出的3个值。
解答:三个数值分别为8,24,4。
s1和s2的大小分别为8和24,可以参考上面的计算,不再赘述。
结构体作为成员时,它的对齐参数为该结构体中所有成员使用的对齐参数的最大值,因此s1的对齐参数不能选择8而应该是4,所以s2中d的起始地址为4。这样第三个数值就为4。