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! #endif int 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 once int 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 和 24
gcc:8 和 20
bcc: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 语言进阶课程》。
如有错漏之处,恳请斧正。