pragma 简介
- #pragma 用于指示编译器完成一些特定的动作
- #pragma 所定义的很多指示字是编译器特有的
#pragma 在不同的编译器间是不可移植的
- 预处理器将忽略它不认识的#pragma指令
- 不同的编译器可能以不同的方式解析同一条#pragma指令
一般用法:
'#pragma parameter'
注: 不同的 parameter 参数语法和意义各不相同
C 语言预留给编译器厂商的扩展指示字
pragma message
- message 参数在大多数的编译器中都有相似的实现
- message 参数在编译时输出消息到编译输出窗口中
- message 用于条件编译中可提示代码的版本信息
#if defined(ANDROID20)
#pragma message("Compile Android SDK 2.0 ...")
#define VERSION "Android 2.0"
#endif
与 #error 和 #warning 不同,#pragma message 仅仅代表一条编译消息,不代表程序错误。
实例分析: #pragma message 使用示例
#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;
}
编译输出:
[GCC] test.c:10: note: #pragma message: Compile Android SDK 4.0...
[VC] Compile Android SDK 4.0...
运行输出:
[GCC] Android 4.0
[VC] Android 4.0
pragma once
- #pragma once 用于保证头文件只被编译一次
- #pragma once 是编译器相关的,不一定被支持
#ifndef _HEADER_H_
#define _HEADER_H_
// source code
#endif
与
#pragma once
这两种方式有什么区别呢?
- #ifndef 为 C 语言支持,实质包含了多次,通过宏来决定是否选择是否嵌入到源代码中,预处理器处理多次,保证只“嵌入一次”。
- #pragma once 为编译器支持,是真正的只编译一次,之后遇到此文件,直接不做处理。
- #pragma once 具有更高的编译效率。
示例分析: #pragma once 使用分析
Test.c
#include <stdio.h>
#include "global.h"
#include "global.h"
int main()
{
printf("g_value = %d\n", g_value);
return 0;
}
global.h
#pragma once
int g_value = 1;
输出:
g_value = 1
VC GCC : 无警告,无错误 【主持】
BCC : 编译出错, g_value多次定义 【不支持】
- 兼顾移植性与编译效率的方法
#ifndef _HEADER_H_
#define _HEADER_H_
#pragma once
// source code
#endif
pragma pack
什么是内存对齐
- 不同类型的数据在内存中按照一定的规则排列
- 而不一定是顺序的一个接一个的排列
struct Test1
{
char c1;
short s2;
char c2;
int i;
};
struct Test2
{
char c1;
char c2;
short s2;
int i;
};
Test1 和 Test2 所占的内存空间是否相同?
sizeof(struct Test1) = 12
sizeof(struct Test2) = 8
为什么需要内存对齐?
CPU 对内存的读取不是连续的,而是分成块读取的,块的大小只能是1、2、4、8、16...字节
- 当读取操作的数据未对齐,则需要两次总线来访问内存,因此性能会大打折扣
- 某些硬件平台只能从规定的相对地址读取特定类型的数据,否则产生硬件异常
- #pragm pack 用于指定内存对齐方式
未对齐造成两次内存读取【32位机器的读写最小粒度4字节】
#pragma pack 能够改变编译器的默认对齐方式
#pragma pack(1)
struct Test1
{
char c1;
short s2;
char c2;
int i;
};
#pragma pack()
#pragma pack(1)
struct Test2
{
char c1;
char c2;
short s2;
int i;
};
#pragma pack()
sizeof(struct Test1) = 8
sizeof(struct Test2) = 8
struct 占用的内存大小
- 第一个成员起始于 0 偏移处
每个成员按其类型大小和pack参数中较小的一个进行对齐
- 偏移地址必须能被对齐参数整除
- 结构体成员的对齐参数大小取其内部pack参数与内部长度最大的数据成员之间较小的作为其对齐参数大小
- 结构体总长度必须为所有对齐参数的整数倍
编译器默认情况下按照 4 字节对齐
编程实验: 结构体大小计算
Test_1.c
#include <stdio.h>
#pragma pack(2)
struct Test1
{ // 对齐参数 偏移地址 大小
char c1; // 1 0 1
short s2; // 2 2 2
char c2; // 1 5 2
int i; // 2 6 4
};
#pragma pack()
#pragma pack(4)
struct Test2
{ // 对齐参数 偏移地址 大小
char c1; // 1 0 1
char c2; // 1 1 1
short s2; // 2 2 2
int i; // 4 4 4
};
#pragma pack()
int main()
{
printf("%d\n", sizeof(struct Test1));
printf("%d\n", sizeof(struct Test2));
}
输出:
10
8
Test_2.c
#include <stdio.h>
#pragma pack(8)
struct S1
{ // 对齐参数 偏移地址 大小
short s; // 2 0 2
long b; // 4 4 4
}; // 整体长度为所有对齐参数的整数倍, len = 4 + 4 = 8
struct S2
{ // 对齐参数 偏移地址 大小
char c; // 1 0 1
struct S1 d; // 4 4 8
double e; // 8 16 8
}; // 整体长度为所有对齐参数的整数倍 , len = 8 + 16 = 24
#pragma pack()
int main()
{
printf("%d\n", sizeof(struct S1));
printf("%d\n", sizeof(struct S2));
}
输出:[GCC]
8
20 【截至2018/12/04,GCC 暂不支持8字节对齐,忽略pack(8),默认4字节对齐】
输出:[VC]
8
24
小结
- #pragma 用于指示编译器完成一些特定的动作
#pragma 所定义的很多指示字都是编译器特有的
- #pragma message 用于自定义编译消息
- #pragma once 用于保证头文件只被编译一次
- #pragma pack 用于指定内存对齐方式
以上内容参考狄泰软件学院系列课程,请大家保护原创!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。