预处理器
C标准:C语言+C预处理器+C标准库
在程序执行之前查看程序
根据预处理指令把符号缩写替换成表示的内容、转换文本
包含程序所需的其他文件、选择让编译器查看哪些代码
翻译
1、把源代码中出现的字符映射到源字符集、处理多字节字符和三字符序列
2、定位 \ +Enter换行、删除换行、把物理行转换为逻辑行
printf( "That's wond\
erful \n");// \ +Enter2个物理行
printf ( "That's wonderful\n ! ");//一个逻辑行 可以是多个物理行
3、文本划分为预处理记号序列、空白序列、注释序列
空格替换注释、除换行符外的空白字符序列
define
预处理器指令
#define 宏 替换体
宏:类对象宏(代表值)、类函数宏(可带参数)
宏名称中不能有空格、只能用字符数字和_、首字母不可以是数字、一般大写
替换字符串、参数列表可以有空格
圆括号括起宏的参数和替换体、可以大概率避免宏调用出错
宏展开:宏->替换文本、替换到没有宏结束
指令可以出现在源文件的任意位置、定义从指令出现到文件末尾有效
指令从#开始、到第一个换行符停止(逻辑行)
ANSI之后允许指令前中有空格、ANSI之前要求整条指令没有多余的空格
记号
记号:宏定义替换体中单独的词、空格分开
#define FOUR 2*2 //1个记号、记号序列:2*2
#define FOUR 2 * 2 //3个记号、记号序列:2 、*、2
记号型字符串:空格是替换体中各记号的分隔符
字符型字符串:空格是替换体的一部分
重定义常量
ANSI只有新旧定义完全相同才允许重定义
相同定义:替换体记号相同、顺序也相同
#define SIX 2 * 3
#define SIX 2 * 3//相同可重定义
#define SIX 2*3//不同、3个记号和1个记号、涉及到undef
define 带参数的类函数宏
类函数宏:生成内联代码、在程序中重复替换语句、不区分变量类型、嵌套循环中使用宏有助于提高效率
函数:只有一份函数副本、调用节省空间、函数跳转恢复浪费时间
内联函数
#运算符
在字符串中包含宏参数
字符串化:#x相当于"x"
##运算符
##:把2个记号组成一个记号
变参宏 …和__VA_ARGS__
#define LIMIT 20
const int LIM = 50;//C中不算常量
static int data1 [LIMIT];//有效
static int data2 [LIM];//无效、非自动数组大小:整型常量表达式、枚举常量、sizeof表达式
const int LIM2 = 2* LIMIT;//有效
const int LIM3 = 2 *LIM;//无效
/*preproc.c --简单的预处理示例*/
#include <stdio.h>
#define TWO 2//字符常量代替数字、清楚表达数字含义
/*可以使用注释*/
#define OW "Consistency is the last refuge of the unimagina\
tive. - Oscar wilde"
/*反斜杠把该定义延续到下一行、预处理开始前会把多行物理行处理为一行逻辑行、
第二行前不能有空格、要和第一行左对齐、从开始到"之间所有都算是字符串的一部分、包括空格
""定义字符串常量、存储在空字符'\0'结尾的数组中、预处理:字符串替换OW
''定义字符常量*/
#define FOUR TWO *TWO//宏包含宏、替换到没有宏结束、有编译器不支持嵌套
#define PX printf("X is %d. \n", x)
#define FMT "X is %d.\n"
//const char *fmt = "x is %d.\n";
#define SQUARE(X) X *X // SQUARE:宏标识符、X:宏参数 X*X:替换列表 X可由宏调用中的符号替代
#define PR(X) printf("The result is %d. \n", X)
#define PSQR(x) printf("The square of " #x " is %d.\n", ((x) * (x)))//#x->"x"
#define XNAME(n) x##n//##把两个记号组合成一个记号 x1
#define PRINT_XN(n) printf("x" #n " = %d\n", x##n);//#n相当于"n"、x##n相当于xn
#include <math.h>
#define PR(X, ...) printf("Message " #X ": " __VA_ARGS__)
/* ...只能代替最后的宏参数、可变参数
__VA_ARGS__在替换部分代替省略号、前必须有空格、否则报错*/
int main(void)
{
int x = TWO;//预处理器找到宏实例之后替换、int x=2;
PX;//宏可以表示任何字符串、C表达式
x = FOUR;//x=TWO*TWO;x=2*2;预处理器只做替换、不求值
printf(FMT, x); // printf("X is %d.\n", x) printf(fmt, x);
printf("%s\n", OW);
printf("TWO: OW\n"); // 双引号之间的宏不做替换 printf("%d:%s\n",TWO, OW);
x = 5;
int z;
printf("x = %d\n", x);
z = SQUARE(x);
printf("Evaluating SQUARE(x):"); // 5*5
PR(z);
z = SQUARE(2);
printf("Evaluating SQUARE(2): "); // 2*2
PR(z);
printf("Evaluating SQUARE(x+2): ");
PR(SQUARE(x + 2)); // 5+2*5+2 只做替换、不计算、可修改为#define SQUARE(x) (x)*(x)
printf("Evaluating 100/SQUARE(2): ");
PR(100 / SQUARE(2)); // 100/2*2 #define SQUARE(×)(x*×) 综合#define SQUARE(x) ((x)*(x))
printf("x is %d\n", x);
printf("Evaluating SQUARE(++x): ");
PR(SQUARE(++x)); //++x*++x 49\42 自增自减避免做宏参数
printf("After incrementing, x is %x.\n", x);
int y = 5;
PSQR(y);//"y"替换#x、"The square of y is %d.\n"
PSQR(2 + 4);//"2 + 4"替换#x
int XNAME(1) = 14; // 变成int xl = 14;
int XNAME(2) = 20; // 变成int x2 = 20;
int x3 = 30;
PRINT_XN(1); // 变成printf("x1 = %d\n", xl);
PRINT_XN(2); // 变成printf("x2 = %d\n", x2);
PRINT_XN(3); // 变成 printf("x3 = %d\n", x3);
double x = 48;
double y;
y = sqrt(x);
PR(1, "x = %g\n", x); // #X展开为"1"、__VA_ARGS__展开为 "x = %g\n", x print ( "Message 1: x= %g\n",×);
PR(2, "x= %.2f, y =%.4f\n", x, y); //__VA_ARGS__展开为 "x= %.2f, y =%.4f\n", x, y
getchar();
return 0;
}
include
.h:头文件、预处理指令、宏函数、结构声明、函数原型
把被包含文件的全部内容输入到本文件
.c:源代码文件、包含头文件以使用头文件中的信息
头文件中的函数实现在name.c、函数调用在main.c、两个源代码文件需要编译和连接
#include <stdio.h>//尖括号、查找标准系统目录、部分开发环境可以配置标准系统目录
#include "hot.h"//双引号、查找当前工作目录、具体查找取决于编译器、未找到再查找标准系统目录
#include "/usr/biff/p.h"//查找/usr/biff目录
P553 待完善