• 2015-11-25 #pragma预处理分析听课笔记


    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的对齐方式是选它里面最长的成员的对齐方式
    所以计算结构体大小要走三步,首先确定是当前程序按照几对齐,接着计算每个结构体变量的大小和偏移,最后计算结构体总大小。

     

       

  • 相关阅读:
    The Tamworth Two chapter 2.4
    USACO Controlling Companies chapter 2.3 已跪
    非递归快排
    链表二路归并
    Money Systems chapter 2.3 dp
    #pragma pack与sizeof union
    快慢指针
    12
    11
    10
  • 原文地址:https://www.cnblogs.com/wan0807/p/4995023.html
Copyright © 2020-2023  润新知