1、#pragma是编译器指示字,用于指示编译器完成一些特定的动作。
2、#pragma定义的很多指示字都是编译器和系统特有的。
3、#pragma在不同的编译器之间是不可移植的。
3.1 预处理器将忽略它所不认识的#pragma指令
3.2两个不同的编译器可能会以两种不同的方式解释同一条#pragma指令,这是由编译器厂商决定的。
4、一般用法: #pragma parameter //不同的parameter参数语法和意义各不相同。
4.1 #pragma message
4.1.1 message参数在大多数的编译器中都有相似的实现。//注意:message参数是VC编译器特有的,GCC是没有的。
4.1.2 message参数在编译时输出消息到编译输出窗口中。
4.1.3 message参数可以用于版本控制。
4.1.4 使用实例:
1 #include <stdio.h> 2 3 #define ANDROID20 4 5 #if defined(ANDROID20) 6 #pragma message("Compile Android SDK 2.0...") 7 #define VERSION "Android 2.0" 8 #elif defined(ANDROID23) 9 #pragma message("Compile Android SDK 2.3...") 10 #define VERSION "Android 2.3" 11 #elif defined(ANDROID40) 12 #pragma message("Compile Android SDK 4.0...") 13 #define VERSION "Android 4.0" 14 #else 15 #error Compile Version is not provided! 16 #endif 17 18 int main() 19 { 20 printf("%s ", VERSION); 21 22 return 0; 23 }
在VC的环境下,编译时在输出窗口将显示我们设定的版本信息。
4.2 #pragma pack
4.2.1 关于内存对齐
不同类型的数据在内存中按照一定的规则排列,而不是顺序的一个接一个的排放,这就是内存对齐。
struct T1
{
char c1;
short s1;
char c2;
int i1;
};
和
struct T2
{
char c1;
char c2;
short s1;
int i1;
}
两者所占的空间大小一样吗?我们在VS下写了一个测试代码如下:
1 #include<stdio.h> 2 3 struct T1 4 5 { 6 7 char c1; 8 9 short s1; 10 11 char c2; 12 13 int i1; 14 15 }; 16 17 18 struct T2 19 20 { 21 22 char c1; 23 24 char c2; 25 26 short s1; 27 28 int i1; 29 30 }; 31 32 33 void main() 34 { 35 printf("%d,%d",sizeof(struct T1),sizeof(struct T2)); 36 getchar(); 37 }
测试结果为:12,8
4.2.2 为什么要内存对齐?
a,CPU读取内存时不是连续读取的而是分块读取的,而块的大小只能是1,2,4,8,16字节。
b,当读取操作的数据未对齐的时候,则需要两次总线周期来访问内存,因此性能会大打折扣。
eg:
struct T
{
char c;
short s;
}
假如不进行内存对齐的话,则需要先读出c,然后读s,假如进行了4字节内存对齐的话,则可以一次性的将c和s读取出来,这样将大大节省读写时间。
c,某些硬件平台只能从规定的地址处取某些特定类型的数据,否则将抛出硬件异常。例如某些硬件只能进行偶地址访问。
4.2.3#pragma pack能够改变编译器默认的对齐方式。
实例程序如下:
1 #include <stdio.h> 2 3 #pragma pack(4) 4 5 struct S1 6 { 7 short a; 8 long b; 9 }; 10 #pragma pack() 11 12 13 #pragma pack(8) 14 struct S2 15 { 16 char c; 17 struct S1 d; 18 double e; 19 }; 20 21 #pragma pack() 22 23 int main() 24 { 25 struct S2 s2; 26 27 printf("%d ", sizeof(struct S1)); 28 printf("%d ", sizeof(struct S2)); 29 printf("%d ", (int)&(s2.d) - (int)&(s2.c)); 30 getchar(); 31 32 return 0; 33 }
运行结果是8,24,4
第三行用于告诉编译器以下内容,进行4字节对齐,
第十三行用于告诉编译器以下内容,进行8字节对齐。编译器默认的对齐方式一般是4字节对齐方式。
具体结果为什么是这样,我们先不说。
4.2.4 关于struct所占用的内存大小的计算:
个人总结的几点:
1、结构体的第一个成员的地址偏移量(offset)为0;
2、在没有用#pragma pack(n)指令指定对齐参数n时,对齐参数n=结构体中占用内存最大的那个成员变量所占内存的大小m;在用#pragma pack(n)指令指定对齐参数n后,系统将min(n,m)作为对齐参数。
3、每个成员变量都要对齐,如果对齐参数为n,该成员变量所占字节数为p,则该成员变量的偏移地址%min(n,p)=0。也就是最小化长度规则。
4、结构体总大小: 对齐后的长度必须是对齐参数n的整数倍。
5、补充:如果结构体A中还有结构体B,那么B的对齐方式是选它里面最长的成员的对齐方式
所以计算结构体大小要走三步,首先确定是当前程序按照几对齐,接着计算每个结构体变量的大小和偏移,最后计算结构体总大小。