Summary

1)#pragma 用于 批示编译器 实现一些特定的动作;#pragma 所定义的很多批示字是编译器特有的,所以在不同的编译器间是不可移植

  • 预处理器疏忽它不意识的#pragma指令
  • 不同的编译器可能以不同的形式解释同一条#pragma指令
  • 个别用法:#pragma parameter ,不同的parameter参数语法和意义各不相同。

2)#pragma message在编译时输入信息到编译输入窗口;和#error、#warning不同,#pragma message仅仅示意一条提示信息,不代表谬误。(vc、bcc和gcc三款编译器行为不同)

3)#pragma once用于保障头文件只被编译一次#pragma once编译器相干的,不肯定被反对。(vc和gcc反对,bcc不反对)

4)#ifndef...#define...#endif也能够用来避免头文件被反复蕴含,和#pragma once有何不同?

  • #ifndef形式是C语言反对的,用在各个编译器都能够;如果一个.h被include了n次预编译器就会判断n次这个头文件是否曾经蕴含了。
  • #pragma once形式是编译器相干的,不肯定所有编译器都反对;预编译器只会解决一次,前面不再判断。

5)什么是内存对齐?

  • 不同类型的数据在内存中依照肯定的规定排列不肯定是程序的一个接一个的排列

6)为什么须要内存对齐?

  • CPU对内存的读取不是间断的,而是分成块读取的,块的大小只能是2的幂,1、2、4、8...字节
  • 当读取操作的数据未对齐,则须要两次总线周期来拜访内存,因而性能会大打折扣
  • 某些硬件平台只能从规定的绝对地址处读取特定类型的数据,否则会产生硬件异样

7)编译器默认的对齐形式为4字节对齐, #pragma pack(n) 能够调整编译器的默认对齐形式(vc和bcc编译器反对8字节对齐,然而gcc不反对)

8)内存对齐的规定

·#pragma剖析

#pragma 用于 批示编译器 实现一些特定的动作;
#pragma 所定义的很多批示字是编译器特有的,所以在不同的编译器间是不可移植

  • 预处理器疏忽它不意识的#pragma指令
  • 不同的编译器可能以不同的形式解释同一条#pragma指令
  • 个别用法:#pragma parameter ,不同的parameter参数语法和意义各不相同。

1、#pragma message

  • message参数在大多数的编译器中都有类似的实现
  • message参数在编译时输入音讯到编译输入窗口中
  • message用于条件编译中可提醒代码的版本信息

    #include <stdio.h>#if defined(ANDROID20)  #pragma message("Compile Android SDK 2.0...")  #define VERSION "ANDROID 2.0"#elif defined(ANDROID30)  #pragma message("Compile Android SDK 3.0...")  #define VERSION "Android 3.0"#elif defined(ANDROID40)  #pragma message("Compile Android SDK 4.0...")  #define VERSION "Amdroid 4.0"#else   #error Compile version is not provided! #endifint main(){  printf("%s\n", VERSION);  return 0;}
    不同编译器的编译后果:  bcc编译器:bcc32 -DANDROID30 test.c  编译输入:Compile Android SDK 3.0...  vc编译器:cl -DANDROID30 test.c  编译输入:Compile Android SDK 3.0...  gcc编译器:gcc -DANDROID30 test.c  编译输入:note: #pragma message: Compile Android SDK 4.0...三款编译器的输入阐明:#pragma message会输入提示信息,然而行为类似,可能具体实现不同
    单步编译的两头文件: gcc -DANDROID30 -E test.c -o test.i# 12 "test.c"#pragma message("Compile Android SDK 3.0...")# 12 "test.c"# 20 "test.c"int main(){  printf("%s\n", "Android 3.0");  return 0;}

    留神:和#error、#warning不同,#pragma message仅仅代表一条编译信息,不代表程序谬误。

2、#pragmae once

  • #pragma once用于保障头文件只被编译一次
  • #pragma once编译器相干的,不肯定被反对

#ifndef...#define...#endif也能够用来避免头文件被反复蕴含,和#pragma once有何不同?

  • #ifndef形式是C语言反对的,用在各个编译器都能够;如果一个.h被include了n次预编译器就会判断n次这个头文件是否曾经蕴含了。
  • #pragma once形式是编译器相干的,不肯定所有编译器都反对;预编译器只会解决一次,前面不再判断。

    // test.h#pragma onceint aa = 1;// test.c#include <stdio.h>#include "test.h"#include "test.h"int main(){  return 0;}
    不同编译器的编译后果:  bcc编译器:bcc32 test.c  编译输入:Variable 'aa' is initialized more than once  vc编译器:cl test.c  编译输入:胜利  gcc编译器:gcc test.c  编译输入:胜利剖析:#pragma once预处理批示字,vc和gcc都能够辨认,然而bcc就无奈辨认,间接疏忽了,而后aa就会被定义2次,造成编译谬误

因为#ifndef会被判断n次,然而#pragma once又不是所有编译器都反对的,为了提高效率,能够这两种形式同时应用:

#ifndef _TEST_H_#define _TEST_H_#pragma once// code#endif

3、#pragma pack和内存对齐

3.1 什么是内存对齐?

  • 不同类型的数据在内存中依照肯定的规定排列不肯定是程序的一个接一个的排列

3.2 为什么须要内存对齐?

  • CPU对内存的读取不是间断的,而是分成块读取的,块的大小只能是2的幂,1、2、4、8...字节
  • 当读取操作的数据未对齐,则须要两次总线周期来拜访内存,因而性能会大打折扣
  • 某些硬件平台只能从规定的绝对地址处读取特定类型的数据,否则会产生硬件异样

3.3 #pragma pack

编译器默认的对齐形式为4字节对齐
#pragama pack能够调整编译器的默认对齐形式

  • #include <stdio.h>#pragma pack(1)struct Test1{  char c1;  short s;  char c2;  int i;};#pragma pack()#pragma pack(1)struct Test2{  char c1;  char c2;  short s;  int i;};#pragma pack()int main(){  printf("sizeof(Test1) = %d\n", sizeof(struct Test1));    printf("sizeof(Test2) = %d\n", sizeof(struct Test2));       return 0;}

    雷同的两个struct,在调整了内存对齐形式后,Test1占用的内存大小产生了变动。

3.3 struct内存对齐的规定:

  • 第一个成员起始于0偏移处
  • 每个成员依照对齐参数进行对齐(类型大小pack参数中较小的一个)

    • 偏移地址必须能被对齐参数 整除(上一个成员的offset+size地位,偏移地址 / 对齐参数 == 0)
    • 构造体类型成员的类型大小取其外部长度最大的数据成员作为其大小
  • struct的总长度必须为所有对齐参数的整数倍
#include <stdio.h>#pragma pack(8)struct Test1{    short a;    long b;};struct Test2{    char c;    struct Test1 st;    double d;};#pragma pack()int main(){    printf("sizeof(Test1) = %d\n", sizeof(struct Test1));        printf("sizeof(Test2) = %d\n", sizeof(struct Test2));           return 0;}
不同编译器下的输入:vc:8 和 24gcc:8 和 20bcc:8 和 24

剖析:vc和bcc的输入后果合乎咱们的算法,为什么gcc不一样的后果呢?
答案:#pragma定义的很多编译器批示字是特有的,不同编译器间不可移植gcc编译器不反对8字节对齐,所以gcc编译器看到'#pragma pack(8)'就间接删掉、疏忽了,依然依照4字节对齐,失去后果为20:

struct Test2{                            pack    offset     memberSize    char c;                    1        0        1    struct Test1 st;           4        4        8    double d;                  4        12       8}                                                    总大小:20

本文总结自“狄泰软件学院”唐佐林老师《C语言进阶课程》。
如有错漏之处,恳请斧正。