#pragma预处理分析

编译预处理

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

  • 编译过程简介
  • 宏定义与宏使用分析
  • 条件编译使用分析
  • #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!
#endif

int 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;
}

结果:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值