预处理指令包括宏定义,文件包含,条件编译等
#define ,#undef,#include,#ifdef,#else,#endif,#ifndef,#if,#elif,#line,#error,#pragma
1.#define 宏定义,在预处理阶段,会进行简单的文本替换
#include<stdio.h> #define TWO 2 #define SAY "Any problem in computer science can be solbed by\ another layer of indirection." #define FOUR TWO*TWO #define PX printf("X = %d\n",x) #define FMT "X = %d\n" int main(void) { int x =TWO; PX; x=FOUR; printf(FMT,x); printf("%s\n",SAY); printf("TWO:SAY\n"); return 0; }
result: x is 2
x is 4
Any problem in computer science can be solbed by another layer of indirection.
TWO:SAY
(1)在双引号中的宏没有被替换,宏也存在一些缺陷
#include<stdio.h> #define N 2+3 #define CHARP char * int main(void) { CHARP p,q; int num =N*2; printf("%d\n",num); printf("p=%d,q=%d\n",sizeof(p),sizeof(q)); return 0; } 32位机子编译结果 gcc define.c -m32 result : 8 p=4,q=1 N被 简单的替换成了 2+3*2 //(2+3)*2 指针的大小为 4个字节,char 大小为 1个字节
(2)利用宏创建字符串 #运算符
#include<stdio.h> #define str(x) "aaaaa"#x"aaaaa" #define PTR(x) printf("the result is "#x"=%d\n",(x)*(x)) int main(void ) { int y=5; printf("%s\n",str(10)); PTR(y); PTR(2+4); return 0; }
(3)预处理的粘合剂 ##运算符(解决了,参数变量与宏展开,无法一一对应的问题)
#include<stdio.h> #define XNAME(n) x##n #define sum(a,b) (a##a+b##b) int main(void) { int XNAME(1) = 14; int XNAME(2) = 20; printf("%d\n",sum(2,3)); printf("x1=%d,x2=%d\n",x1,x2); return 0; } result : 55 x1=14,x2=20
(4)可变宏 ... 和 __VA_ARGS__
可变参数的宏可以实现参数的可变性,如printf(...) , ...前面可以有参数,后面不能有参数
#include<stdio.h> #define PF(...) printf(__VA_ARGS__) #define PT(x,...) printf("message "#x" :" __VA_ARGS__) int main(void) { PF("china"); PF("weight = %d height = %f\n",100,120.0) PT(1,"china\n"); PT(2,"weight =%d heith =%f\n",100,120.0); return 0; } result: china weight = 100 height =120.000000 message 1:china message 2:weight = 100 height = 120.000000
(5)宏定义一年有多少秒,并且打印输出
#include <stdio.h> #define SECOND_PER_YEAR (60*60*24*356UL) int main(void) { unsigned int a; a=SECOND_PER_YEAR+1; printf("%u\n",a); printf("Hello World!\n"); return 0; }
2.条件编译 依据条件,判断哪些程序段参与编译
(1)#ifdef(#ifndef) #else #endif
#include<stdio.h> #define x86 int main(void) { #ifdef x86 printf("**********\n"); #else printf("$$$$$$$$$$\n"); #endif return 0; }
(2)#if #elif #endif
在写跨平台程序的时候,经常见到这样的语句,易于程序的移植
#include<stdio.h> #define x86 100 #define MIPS 200 #define POWERPC 300 #define MACHINE POWERPC int main(void) { #if MACHINE == x86 printf("**********"); #elif MACHINE == MIPS printf("%%%%%%%"); #elif MACHINE == POWERPC printf("$$$$$$$$$$$"); #endif return 0; }
用条件编译来注释,解决了/**/不支持嵌套的特点
3.文件包含 #include 支持嵌套,递归进行的
(1) #include<stdio.h>
从系统指定路径中搜索包含头文件 Linux中的系统路径为/usr/include
(2)#include "myString.h"
从工程当前路径中搜索包含头文件,当前路径没有,就到系统路径下搜索包含
(3)c语言是以文件为单位进行编译的,编译期只需要函数声明即可
(4)定义头文件
自包含可以免去多余的前向声明,避免头文件重复包含采用如下格式:
#ifndef __XX_H__
#define __XX_H__
//数据类型声明
//函数声明
#endif
4.其他
(1)系统自定义宏
__DATE__ 进行预处理的日期 ("Mmm dd yyyy"形式的字符串文字)
__FILE__ 代表当前源代码文件名的字符串 文字
__LINE__ 代表当前源代码中的行号的整型常量
__TIME__ 源文件编译时间,格式“hh: mm: ss”
__func__ 胆怯所在函数名
__FILE__ __LINE__ 对开发者非常有用的提示,可以写在日志里
#include<stdio.h> #include<stdlib.h> void why_me() { printf("The file is %s\n",__FILE__); printf("The date is %s\n",__date__); printf("The time is %s\n",__TIME__); printf("This is line %d\n",__LINE__); printf("This function is %s\n",__func__); why_me(); return 0; } void why_me() { printf("This function is %s\n",__func__); printf("The file is %s\n",__FILE__); printf("This is line %d\n",__LINE__); }
(2)#line 和 #error
#line 用于重置由 __LINE__ 和 __FILE__宏定义的行号和文件名
#line 1000 //把当前行号重置为1000
#line 10 "cool.c" //把行号重置为10 ,文件名重置为 cool.c
#error 指令使预处理器发出一条错误信息,该信息包含指令中的文本,可能的话,编译过程应该中断
#if __STDC_VERSION__ != 199901L
#error Not c99
#endif
(3)pragma
在现在编译器中,可以用命令行参数或IDE菜单修改编译器的某些设置,也可用#pragma将编译器指令置于源代码中
#pragma message ("消息文本")
#ifdef _X86
#pragma message("_X86 macro ativated!")
#endif
#pragma code_seg(["section-name"[,"section-class"]])
能够设置程序中函数代码存放的代码段,驱动程序开发中用到
#pragma once 只要在头文件的最开始加入这条指令就能保证头文件被编译一次,这条指令
vs中就已经有了,但是考虑到兼容性并没有太多的使用它
#pragma pack(n) //按n字节对齐
#pragma apop() //取消n 字节对齐方式
#pragma warning(disable:450734) //不显示4507 和34号警告信息
#pragma warning(once:4385) //4385警告信息仅报告一次
#pragma warning(error:164) //把164号警告信息作为一个错误
#pragma disable
在函数前声明,只对一个函数有效。该函数调用过程中将不可被中断。一般在C51中使用
#pragma data_seg 建立一个新的 数据段 并定义共享数据
#pragma data_seg("shareddata")
HWND sharedwnd=NULL; //共享数据
#pragma data_seg()
一般用于DLL中。这个数据段中的全局变量可以被多个进程共享,否则多个进程之间无法共享DLL中的全局变量;共享数据必须出示化,否则微软编译会把没有初始化的数据放在.bss段中,导致共享失败
#pragma hdrstop
表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占用太多磁盘空间,所有使用这个选项可以排除一些头文件
#pragma resource "*.dfm" 把*.dfm 文件中的资源加入工程。 *.dfm中包括窗体外观的定义