格式为#define
当出现该条语句的时候,他表示把name替换成stuff
预处理器查找一行中以#开始的预处理器指令。指令可以出现在源文件中的任何地方。他的定义从开始一直到源文件末尾都有效。所以不要在末尾加分号
#include
#define TWO 2 /* you can use comments if you like */
#define OW "Consistency is the last refuge of the unimagina\
tive. - Oscar Wilde" /* a backslash continues a definition */
/* to the next line */
#define FOUR TWO*TWO
#define PX printf("X is %d.\n", x)
#define FMT "X is %d.\n"
int main(void)
{
int x = TWO;
PX;
x = FOUR;
printf(FMT, x);
printf("%s\n", OW);
printf("TWO: OW\n");
return 0;
}
注意宏换行时要与上面一行对齐,不然空格也会算进去。
#define LIMIT 20
const int LIM = 50;
static int data1[LIMIT]; // 有效
static int data2[LIM]; // 无效
const int LIM2 = 2 * LIMIT; // 有效
const int LIM3 = 2 * LIM; // 无效
数组的声明可必须是
#define EIGHT 4 * 8
#define EIGHT2 4*8
这两个宏定义是不同的,一个是带空格的,而另一个不带空格。空格也是宏定义中的一部分。
#define LIM 20
#define LIM 20
可以重定义宏,但是重定义的值必须和原值相同。如果不同,则有些编译器会报错,有些会给出警告。
取消定义 #undef

移除LIMIT定义后,就可以给LIMIT重新定义成一个新值。如果没有定义LIMIT,取消定义一样有效。
看上去像函数调用,但是和函数调用完全不同。
看下面实例
#include
#define SQUARE(X) X*X
#define PR(X) printf("The result is %d.\n", X)
int main(void)
{
int x = 5;
int z;
z = SQUARE(x); //z = 5*5
PR(z); //The result is 25.
z = SQUARE(2); //z = 2*2
PR(z); //The result is 4.
PR(SQUARE(x+2)); //z = 5+2*5+2 The result is 17.
PR(100/SQUARE(2));//PR(100/2*2) The result is 100.
printf("x is %d.\n", x); // 5
PR(SQUARE(++x)); //++x*++x = 6*7 The result is 42.
printf("After incrementing, x is %x.\n", x); //7
return 0;
}
这个是使用在printf中
#define PSQR(x) printf("The square of "#x" is %d.\n",((x)*(x)))
int main(void)
{
int y = 5;
PSQR(y); //The square of y is 25.
PSQR(2 + 4); //The square of 2 + 4 is 36.
return 0;
}
这里的参数会替换字符串中的"#x"
#include
#define XNAME(n) x ## n
#define PRINT_XN(n) printf("x"#n" = %d\n", x ## n);
int main(void)
{
int XNAME(1) = 14; // int x1 = 14;
int XNAME(2) = 20; // int x2 = 20;
int x3 = 30;
PRINT_XN(1); // printf("x1 = %d\n", x1); x1 = 14
PRINT_XN(2); // printf("x2 = %d\n", x2); x2 = 20
PRINT_XN(3); // printf("x3 = %d\n", x3); x3 = 30
return 0;
}
... 和 _ _VA _ ARGS_ _ 打印可变参数宏是在宏中printf使用的
#include
#include
#define DEBUG(format, ...) printf(format, __VA_ARGS__)
#define PR(X, ...) printf("Message " #X " : " __VA_ARGS__)
int main(void)
{
DEBUG("%s: %d\r\n", "debug", 100); //printf("%s: %d\r\n", "debug", 100) debug: 100
double x = 48;
double y;
y = x*x;
PR(1, "x = %g\n", x); //Message 1: x = 48
PR(2, "x = %.2f, y = %.4f\n", x, y); // Message 2: x = 48.00, y = 2304.0000
return 0;
}
#include
#define JUST_CHECKING //空定义
#define LIMIT 4
int main(void)
{
int i;
int total = 0;
for (i = 1; i <= LIMIT; i++)
{
total += 2*i*i + 1;
#ifdef JUST_CHECKING
printf("i=%d, running total = %d\n", i, total);
#endif
}
printf("Grand total = %d\n", total);
return 0;
}
#ifndef SIZE
#define SIZE 100
#endif
// names.h --revised with include protection
#ifndef NAMES_H_
#define NAMES_H_
// constants
#define SLEN 32
// structure declarations
struct names_st
{
char first[SLEN];
char last[SLEN];
};
// typedefs
typedef struct names_st names;
// function prototypes
void get_names(names *);
void show_names(const names *);
char * s_gets(char * st, int n);
#endif
条件判断#ifdef NAMES我们可以在代码中使用#define NAMES来定义一个宏,还有另一种方式我们可以在编译的时候使用命令行就定义一个宏,而无需在代码中定义。
比如我设定一个数组大小
int array[ARRAY_SIZE];
我在编译的时候添加选项
gcc -DARRAY_SIZE=100 main.c
这时,就相当于我在代码最开始的位置定义了一个宏
#define ARRAY_SIZE 100
宏非常频繁的用于简单的计算比如
#define MAX(a,b) ( (a) > (b) ? (a) : (b) )
把函数变成内联,编译器可能会把函数代码直接替换掉函数调用。
目的:把函数变为内联,目的是尽可能快的调用该函数。
inline int fun(int x)
{
return x * x;
}
int main()
{
int b = fun(2 + 3);
return 0;
}
内联会把函数变为下面形式
inline int fun(int x)
{
return x * x;
}
int main()
{
int b = (2 + 3) * (2 + 3);
return 0;
}
未给内联函数预留代码块, 所以无法获得内联函数地址,并且内联函数无法在调试器中显示。
一般我们不在头文件中放可执行代码,但是内联函数是个特例。
内联函数一般是比较简短的函数,我们可以把它写成内联形式。
下面是一些预定义的宏,可以直接使用,在标准库中
// predef.c -- predefined identifiers
#include
void why_me();
int main()
{
printf("The file is %s.\n", __FILE__);
printf("The date is %s.\n", __DATE__);
printf("The time is %s.\n", __TIME__);
printf("The version is %ld.\n", __STDC_VERSION__);
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("This is line %d.\n", __LINE__);
}
// The file is predef.c.
// The date is Oct 29 2022.
// The time is 12:42:30.
// The version is 201710.
// This is line 11.
// This function is main
// This function is why_me
// This is line 21.
当预处理器发现#include指令,就会简单的删掉这条指令然后把该文件包含到当前文件中。

双引号,会查找当前工作目录中如果没有再去系统目录中查找。但是查找还是要看编译器,可能会查找当前源文件所在目录,也可能从项目文件所在目录查找。
所以<>用于函数库文件的包含,而“”用于本地文件的包含。
当出现了多重文件包含的情况,我们就是用上面条件宏的处理方式来防止多重头文件包含发生。

全局变量

#error 指令让预处理器发出一条错误消息,该消息包含指令中的文本。 如果可能的话,编译过程应该中断。
#if _ _STDC_VERSION_ _ != 201112L
#error Not C11
#endif
如果判断有问题,则编译会直接报错中断
$ gcc newish.c
newish.c:14:2: error: #error Not C11
一种用途较小的指令
指令重置#line
#line 1000 // 把当前行号重置为1000
#line 10 "cool.c" // 把行号重置为10,把文件名重置为cool.c
#include
int main(void)
{
printf("-- %s: %d -- \n", __FILE__, __LINE__);
#line 201 "foo.c"
printf("-- %s: %d -- \n", __FILE__, __LINE__);
#line 101 "foo.c"
printf("-- %s: %d -- \n", __FILE__, __LINE__);
return 0;
}
-- ./main.c: 5 --
-- foo.c: 201 --
-- foo.c: 101 --
#pragma指令的作用是:用于指定计算机或操作系统特定的编译器功能。
他的作用因编译器而异。所以通常他是不可移植的。
#pragma once
大家应该都知道:指定该文件在编译源代码文件时仅由编译器包含(打开)一次。
使用 #pragma once 可减少生成次数,和使用预处理宏定义来避免多次包含文件的内容的效果是一样的,但是需要键入的代码少,可减少错误率,例如:
//使用#progma once
#pragma once
// Code placed here is included only once per translation unit
//使用宏定义方式
#ifndef HEADER_H_
#define HEADER_H_
// Code placed here is included only once per translation unit
#endif // HEADER_H_