#pragma预处理分析

来源:互联网 发布:win2008端口转发 编辑:程序博客网 时间:2024/06/06 00:10

编译预处理

专题三:编译预处理。包括以下章节:

  • 编译过程简介
  • 宏定义与宏使用分析
  • 条件编译使用分析
  • #error和#line
  • #pragma预处理分析
  • #和##运算符使用解析

#pragma

  • #pragma是编译器指示字,用于指示编译器完成一些特定的动作
  • #pragma所定义的很多指示字是编译器和操作系统特有的
  • #pragma在不同的编译器间是不可移植的
    • 预处理器将忽略它不认识的#pragma指令
    • 两个不同的编译器可能以两种不同的方式解释同一条#pragma指令

#pragma message

  • message参数在大多数的编译器中都有相似的实现
  • message参数在编译时输出消息到编译输出窗口中
  • message可用于代码的版本控制
  • message是VC特有的编译 器指示字,GCC中将其忽略。(低版本的gcc忽略,但是高版本的gcc会打印附注信息)

实例分析5-1:#pragma message在不同编译器下不同实现

5-1.c

#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!#endifint main(){    printf("%s\n", VERSION);    return 0;}

结果:
这里写图片描述

#pragma pack

  • 不同类型的数据在内存中按照一定的规则排列;而不是顺序的一个接一个的排放,这就是对齐。

  • 为什么需要内存对齐?

    • CPU对内存的读取不是连续的,而是分成块读取的,块的大小只能是1、2、4、8、16字节
    • 当读取操作的数据未对齐,则需要两次总线周期来访问内存,因此性能会大打折扣
    • 某些硬件平台只能从规定的地址处取某些特定类型的数据,否则抛出硬件异常(比如某些硬件只能读取偶数内存地址的值)
  • #pragma pack能够改变编译器的默认对齐方式

  • struct占用的内存大小

    • 第一个成员起始于0偏移处
    • 每个成员按其类型大小和指定对齐参数n中较小的一个进行对齐
      偏移地址和成员占用大小均需对齐
      结构体成员的对齐参数为其所有成员使用的对齐参数的最大值
    • 结构体总长度必须为所有对齐参数的整数倍

5-2.c

#include<stdio.h>#pragma pack(2)struct Test1{    //假如首地址是0x0000    char c1;//大小1Byte,1和2比较1小,所以起始地址:0,内存地址:0x0000     short s;//大小2Byte,2和2比较等同,所以起始地址:2,内存地址:0x0002 0x0003    char c2;//大小1Byte,1和2比较1小,所以起始地址:4,内存地址:0x0004     int i;  //大小4Byte,4和2比较2小,所以起始地址:6,内存地址:0x0006 0x0007 0x0008 0x0009};#pragma pack()#pragma pack(4)struct Test2{    //假如首地址是0x0000    char c1;//大小1Byte,1和4比较1小,所以起始地址:0,内存地址:0x0000    char c2;//大小1Byte,1和4比较1小,所以起始地址:1,内存地址:0x0001    short s;//大小2Byte,2和4比较2小,所以起始地址:2,内存地址:0x0002 0x0003    int i;  //大小4Byte,4和4比较等同,所以起始地址:4,内存地址:0x0004 0x0005 0x0006 0x0007 };#pragma pack()#pragma pack(4)struct Test3{    //假如首地址是0x0000    char c1;//大小1Byte,1和4比较1小,所以起始地址:0,内存地址:0x0000    short s;//大小2Byte,2和4比较2小,所以起始地址:2,内存地址:0x0002 0x0003    char c2;//大小1Byte,1和4比较1小,所以起始地址:4,内存地址:0x0004    int i;  //大小4Byte,4和4比较等同,所以起始地址:8,内存地址:0x0008 0x0009 0x000A 0x000B};#pragma pack()int main(){    printf("%lu\n", sizeof(struct Test1));    printf("%lu\n", sizeof(struct Test2));    printf("%lu\n", sizeof(struct Test3));    return 0;}

结果:
这里写图片描述

实例分析5-2:Intel和微软面试题

5-3.c

#include<stdio.h>//结构体中的成员大小与对齐参数进行比较,小的那个数是对齐数,不一定必须是8#pragma pack(8)struct S1{    //假如首地址是0x0000    short a;//大小2Byte,2和8比较2小,所以起始地址:0    long b; //大小8Byte,8和8比较等同,所以起始地址:8    //所以,struct S1内存中占16字节(0x0000~0x000F)};struct S2{    //假如首地址是0x0000    char c;     //大小1Byte,1和8比较1小,所以起始地址:0    struct S1 d;//大小16Byte,16和8比较8小,所以起始地址:8    double e;   //大小8Byte,8和8比较等同,所以起始地址:(24)0x0018    //所以,struct S2内存中占32字节(0x0000~0x001F)};#pragma pack()int main(){    struct S2 s2;    printf("%lu\n", sizeof(struct S1));    printf("%lu\n", sizeof(struct S2));    //s2.c的内存地址和s2.d的内存地址相差8字节    //s2.c占1字节,而剩余7字节(8字节对齐)无法装下s2.d(16字节)。所以剩余7字节为空,s2.c地址偏移8个字节开始装s2.d。    printf("%d\n", (int)&(s2.d) - (int)&(s2.c));         return 0;}

结果:
这里写图片描述

然而在VC上编译结果却不同
5-3.c

#include<stdio.h>#pragma pack(8)struct S1{    //假如首地址是0x0000    short a;//大小2Byte,2和8比较2小,所以起始地址:0    long b; //大小4Byte,4和8比较4小,所以起始地址:4    //所以,struct S1内存中占8字节(0x0000~0x0007)};struct S2{    //假如首地址是0x0000    char c;     //大小1Byte,1和8比较1小,所以起始地址:0    struct S1 d;//大小8Byte,(结构体成员的对齐参数为其所有成员使用的对齐参数的最大值,是4)4和8比较4小,所以起始地址:4    double e;   //大小8Byte,8和8比较等同,所以起始地址:(16)0x0010    //所以,struct S2内存中占24字节(0x0000~0x0017)};#pragma pack()int main(){    struct S2 s2;    printf("%lu\n", sizeof(struct S1));    printf("%lu\n", sizeof(struct S2));    printf("%d\n", (int)&(s2.d) - (int)&(s2.c));         return 0;}

结果:
这里写图片描述

原创粉丝点击