预处理指令
预处理指令以#开头,编译前执行
include指令
作用
文件包含命令,引入对应头文件
处理过程
将头文件的内容(.h)插入到该命令所在的位置,从而把头文件和当前源文件连接成一个源文件,等同复制粘贴效果。
include用法及区别
#include
#include "myHeader.h"
使用尖括号<
>,编译器会到系统路径下查找头文件;而使用双引号"
",编译器首先在当前目录下查找头文件,如果没有找到,再到系统路径下查找。
最佳习惯:使用尖括号来引入标准头文件,使用双引号来引入自定义头文件,禁用绝对路径作为搜索路径(如开发移植到生产路径发生变化出问题)
<>搜索顺序
在编译器设置的include路径内搜索;
如果找不到,则在系统的include环境变量内搜索;
linux下标准路径
/usr/include,/usr/local/include
可以在/usr/include/sys目录下发现io.h
指定路径下检索头文件
#/home/Desktop目录下有个头文件local1.h,编译包含local1.h的test.c文件时
gcc test.c -o test -I /root/Desktop
pragma指令
作用
设定编译器的状态或者是指示编译器完成一些特定的动作。编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。
命令格式
#pragma para
常用参数
#pragma message("消息文本")
在编译信息输出窗口中输出相应的信息
eg:#ifdef _X86
#pragma message("_X86 macro
activated!")
#endif
编译的时候就进行检查宏的设置
#开发驱动程序常用
#pragma code_seg( ["section-name" [,
"section-class"] ] )
#pragma once
在头文件的最开始加入这条指令就能够保证头文件被编译一次
#pragma warning( disable: 4507 34;
once: 4385; error: 164 )
// 不显示4507和34号警告信息
// 4385号警告信息仅报告一次
// 把164号警告信息作为一个错误。
#pragma warning( push
)保存所有警告信息的现有的警告状态。
#pragma warning( push, n
)保存所有警告信息的现有的警告状态,并且把全局警告等级设定为n。
#pragma warning( pop
)向栈中弹出最后一个警告信息,在入栈和出栈之间所作的一切改动取消
#将一个注释记录放入一个对象文件或可执行文件
#pragma comment(lib,
"wsock32.lib")
#pragma pack(n)
内存对齐
许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,而这个k则被称为该数据类型的对齐模数(alignment
modulus)。任何基本数据类型T的对齐模数就是T的大小,即sizeof(T)。
Win32平台下的微软C编译器(cl.exe for 80x86)
n字节边界对齐的意思是说,一个成员的地址必须安排在成员的尺寸的整数倍地址上或者是n的整数倍地址上,取它们中的最小值。
c/c++中的对齐选项:/Zp[1|2|4|8|16]
min ( sizeof ( member ), n)
栈内存对齐
注:在vc6中栈的对齐方式不受结构成员对齐选项的影响。它总保持对齐,而且对齐在4字节边界。
宏定义
作用
将宏名替换为字符串(宏替换)
格式
#define 宏名 字符串 #宏名大写
用法
eg1.常量替换,增加程序可读性
#define PI 3.14159
eg2.宏定义表示数据类型,方便书写
#define UINT unsigned int
UINT a, b; => unsigned int
a,b;
与typedef自定义数据类型的区别
1.宏定义只是简单的字符串替换,由预处理器来处理;
2. typedef
是在编译阶段由编译器处理的,给原有的数据类型起一个新的名字,将它作为一种新的数据类型。
#define PIN1 int *
typedef int *PIN2;
//也可以写作typedef int (*PIN2);
PIN1 a, b; => int * a,
b;
PIN2 a,b; => int *a,*b;
eg3.定义表达式 (替换变量字符串括号不能少)
#define SUM(a,b) ((a)*(b))
调试宏(编译器内置)
__FILE__ 当前程序文件名
__LINE__ 当前行号
__FUNCTION__ 函数名称
__DATE__ 当前日期
__STDC__ 编译器遵循ANSI C标准,非零
__TIME__ 当前时间
预处理运算符
1.字符串化运算符 —— #
用于创建字符串,#运算符后面应该跟一个形参。
#define Dump_Str(s) printf("%s =
%s\n",#s,s);
const char * pchName = "Gui xue";
Dump_Str(pchName);
//输出 pchName = Gui xue
2.连接运算符 —— ##
用于将两个Token连接成一个Token;
#define __CONCAT(x,y) x ## y
int n1 =15;
int n2 =200;
__CONCAT(n,1); // n1
__CONCAT(n,2); // n2
3.字符化运算符—— #@
用于创建一个字符,类似 ## ,注: 非
ANSI-C中的特性,GCC不支持,VC可以;
#define Dump_Char(c) #@c
printf("%c/n",Dump_Char(g)); //g
printf("%c/n",Dump_Char(guix)); //x 可以输入
4个长度的字符,但只输出最后一位
printf("%c/n",Dump_Char( guix )); //x
默认去除前后空格,保留中间空格
printf("%c/n",Dump_Char(guixu));
//error C2015: too many characters in
constant