C语言
提供了多种预处理功能,如宏定义、文件包含、条件编译等。以"#
"号开头的预处理命令:包含命令#include
,宏定义命令#define
等。在源程序中这些命令都放在函数之外,而且一般都放在源文件的前面,它们称为预处理部分C语言
的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
举个栗子:
printf("file:%s line:%d\n", __FILE__, __LINE__);
#define name stuff
举个栗子:
#define MAX 1000
#define reg register //为 register这个关键字,创建一个简短的名字
#define do_forever for(;;) //用更形象的符号来替换一种实现
#define CASE break;case //在写case语句的时候自动把 break写上。
// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
date:%s\ttime:%s\n" ,\
__FILE__,__LINE__ , \
__DATE__,__TIME__ )
define
定义标识符的时候,要不要在最后加上 ;
?比如:
#define MAX 1000;
#define MAX 1000
;
,这样容易导致问题,这里会出现语法错误比如下面的场景:
if(condition)
max = MAX;
else
max = 0;
#define name( parament-list ) stuff
如:
#define SQUARE(x) x*x
int a = 5;
printf("%d\n" ,SQUARE( a + 1) );
乍一看,你可能觉得这段代码将打印36这个值。事实上,它将打印11,为什么?
因为替换文本时,参数x被替换成a + 1,所以这条语句实际上变成了:
printf (“%d\n”,a + 1 * a + 1 );
这样就比较清晰了,由替换产生的表达式并没有按照预想的次序进行求值。在宏定义上加上两个括号,这个问题便轻松的解决了:
#define SQUARE(x) (x)*(x)
printf ("%d\n",(a + 1) * (a + 1) );
#define DOUBLE(x) (x)+(x)
int a = 5;
printf("%d\n" ,10 * DOUBLE(a));
printf ("%d\n",10 * (5) + (5));
#define DOUBLE(x) ((x)+(x))
首先我们看看这样的代码:
char* p = "hello ""bit\n";
printf("hello"" bit\n");
printf("%s", p);
#define PRINT(FORMAT, VALUE) printf("the value is "FORMAT"\n", VALUE)
PRINT("%d", 10);
这里只有当字符串作为宏参数的时候才可以把字符串放在字符串中
比如:
int i = 10;
#define PRINT(FORMAT, VALUE) printf("the value of " #VALUE "is "FORMAT "\n", VALUE);
PRINT("%d", i + 3);//产生了什么效果?
#define ADD_TO_SUM(num, value) sum##num += value;
ADD_TO_SUM(5, 10);//作用是:给sum5增加10
x+1;//不带副作用
x++;//带有副作用
#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
...
x = 5; y = 8; z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);//输出的结果是什么?
这里我们得知道预处理器处理之后的结果是什么:
z = ( (x++) > (y++) ? (x++) : (y++));
所以输出的结果是:
x=6 y=10 z=9
#define MAX(a, b) ((a)>(b)?(a):(b))
那为什么不用函数来完成这个任务?
原因有二:
宏的缺点:
当然和函数相比宏也有劣势的地方:
宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到
#define MALLOC(num, type) (type*)malloc(num* sizeof(type))
...
//使用
MALLOC(10, int);//类型作为参数
//预处理器替换之后:
(int *)malloc(10 * sizeof(int));
#undef NAME
//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。
#include
int main()
{
int array [ARRAY_SIZE];
int i = 0;
for(i = 0; i< ARRAY_SIZE; i ++)
{
array[i] = i;
}
for(i = 0; i< ARRAY_SIZE; i ++)
{
printf("%d " ,array[i]);
}
printf("\n" );
return 0; }
编译指令:
//linux 环境演示
gcc -D ARRAY_SIZE=10 programe.c
#include
#define __DEBUG__
int main()
{
int i = 0;
int arr[10] = { 0 };
for (i = 0; i < 10; i++)
{
arr[i] = i;
#ifdef __DEBUG__
printf("%d\n", arr[i]);//为了观察数组是否赋值成功。
#endif //__DEBUG__
}
return 0;
}
#include "filename"
查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样
在标准位置查找头文件。如果找不到就提示编译错误
#include
查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。这样是不是可以说,
对于库文件也可以使用 “ ” 的形式包含?答案是肯定的。但是这样做查找的效率就低些,
当然这样也不容易区分是库文件还是本地文件了
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif
或者:
#pragma once
就可以避免头文件的重复引入