注:博客中内容主要来自《狄泰软件学院》,博客仅当私人笔记使用。
测试环境:Ubuntu 10.10
GCC版本:4.4.5
一、#pragme简介
1)#pragme用于指示编译器完成一些特定的动作
2)#pragme所定义的很多指示字是编译器特有的
3)#pragme在不同的编译器间是不可抑制的
- 预处理器将忽略它不认识的#pragma指令
- 不同的编译器可能以不同的方式解释同一条#pragma指令
一般用法
#pragma parameter
注:不同的parameter参数语法和意义各不相同
二、#pragma message
1)message参数在大多数的编译器中都有相似的实现
2)message参数在编译时输出消息到编译输出窗口中
3)message用于条件编译中可提示代码的版本信息
#if defined(ANDROID20)
#pragma message("Compile Android SDK 2.0...")
#define VERSION "Android 2.0"
#endif
与#error和#warning不同,#pragma message仅仅代表一条编译消息,不代表程序错误。
实例分析
#pragma message使用示例
24-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;
}
操作:
1)gcc 24-1.c -0 24-1.out编译报错
2)gcc -DANDROID20 24-1.c -o 24-1.out编译有提示:
24-1.c:4:10: note: #pragma message: Compile Android SDK 2.0...
#pragma message("Compile Android SDK 2.0...")
提示:编译安卓SDK2.0......
运行结果:
Android 2.0
知识点:
#if defined(val)--有val定义,defined返回1,预编译条件判断正确。
三、#pragma once
1)#pragma once用于保证头文件只被编译一次
2)#pragma once是编译器相关的,不一定被支持
实例分析
#pragma once使用分析
24-2.c
#include <stdio.h>
#include "global.h"
#include "global.h"
int main()
{
printf("g_value = %d\n", g_value);
return 0;
}
global.h
#ifndef _GLOBAL_H_
#define _GLOBAL_H_
#pragma once
int g_value = 1;
#endif
操作:
1)gcc 24-2.c -o 24-2.out编译正确,打印结果:
g_value = 1
分析:
#pragma once是编译器相关的,比如bcc编译器不支持。
四、#pragma pack
1)什么是内存对齐?
- 不同类型的数据在内存中按照一定的规则排列
- 而不一定是顺序的一个接一个的排列
2)为什么需要内存对齐?
- CPU对内存的读取不是连续的,而是分成块读取的,块的大小只能是
1、2、4、8、16……字节
- 当读取操作的数据未对齐,则需要两次总线周期来访问内存,因此性能会大打折扣
- 某些硬件平台只能从规定的相对地址处读取特定类型的数据,否则产生硬件异常
3)#pragma pack用于指定内存对齐方式
4)#pragma pack能够改变编译器的默认对齐方式
5)struct占用的内存大小
- 第一个成员起始于0偏移处
- 每个成员按其类型大小和pack参数中较小的一个进行对齐
* 偏移地址必须能被对齐参数整除
* 结构体成员的大小取其内部长度最大的数据成员作为对齐
- 结构体总长度必须为所有对齐参数的整数倍
编译器在默认情况下按照4字节对齐
24-3.c
#include <stdio.h>
#pragma pack(4)
struct Test1
{ //对齐参数 偏移地址 大小
char c1; //1 0 1
short s; //2 2 2
char c2; //1 4 1
int i; //4 8 4
};
#pragma pack()
#pragma pack(4)
struct Test2
{ //对齐参数 偏移地址 大小
char c1; //1 0 1
char c2; //1 1 1
short s; //2 2 2
int i; //4 4 4
};
#pragma pack()
int main()
{
printf("sizeof(Test1) = %d\n", sizeof(struct Test1));
printf("sizeof(Test2) = %d\n", sizeof(struct Test2));
return 0;
}
操作:
1)#pragma pack(4): gcc 24-3.c -o 24-3.out编译正确,打印结果:
sizeof(Test1) = 12
sizeof(Test2) = 8
2)#pragma pack(1):gcc 24-3.c -o 24-3.out编译正确,打印结果:
sizeof(Test1) = 8
sizeof(Test2) = 8
24-4.c
#include <stdio.h>
#pragma pack(8)
struct S1
{ //对齐参数 偏移地址 大小
short a; //2 0 2
long b; //4 4 4
};
struct S2
{ //对齐参数 偏移地址 大小
char c; //1 0 1
struct S1 d; //4 4 8
double e; //4 16 8
};
#pragma pack()
int main()
{
printf("%d\n", sizeof(struct S1));
printf("%d\n", sizeof(struct S2));
return 0;
}
操作:
1)使用默认的4字节对齐:gcc 24-4.c -o 24-4.out编译正确,打印结果:
8
20
2)使用8字节对齐:gcc 24-4.c -o 24-4.out编译正确,打印结果:
8
20
bcc编译器:
8
24
分析:
使用#pragma用于指示编译器完成一些特定的动作,编译器对支持的动作进行处理,不支持的动作没有任何反馈信息。不能过度依赖编译器进行优化。
小结:
1)#pragma用于指示编译器完成一些特定的动作
2)#pragma所定义的很多指示字是编译器特有的
- #pragma message用于定义编译消息
- #pragma once用于保证头文件只被编译一次
- #pragma pack用于指定内存对齐方式