C语言预处理——

预定义符号

__FILE__

代表了当前代码所处的地方,即当前代码的绝对路径。

C语言预处理——_第1张图片

__LINE__

代表了当前行号

C语言预处理——_第2张图片

__TIME__

可以显示当前时间

__DATE__

显示今天日期

__FUNCTION__

显示代码所处函数名

C语言预处理——_第3张图片

利用这些预定义符号,我们可以写日志,如下

#define _CRT_SECURE_NO_WARNINGS 1
#include
int main()
{
	int arr[10] = { 0 };
	int i;
	FILE* pf = fopen("log.txt", "w");
	for (i = 0; i < 10; i++)
	{
		arr[i] = i;
		fprintf(pf, "%s\t%s\t%s\t%d\t%s\t%d\n", __DATE__, __TIME__, __FILE__, __LINE__, __FUNCTION__,arr[i]);
	}
	return 0;
}

C语言预处理——_第4张图片

#define

以#开头的都是预处理指令

#define定义标识符

C语言预处理——_第5张图片

可以是数,也可以是字符串等各种内容

#define reg register  //用更加简单的符号来代替

#define do_forever for(;;)  //用更形象的符号代替死循环

#define定义宏

#define  名称(参数列表)  表达式

其中名称和后面的括号要紧挨着,不可以有空格。例如:

C语言预处理——_第6张图片

但注意,下面代码有bug

C语言预处理——_第7张图片

为什么不是16和60呢?这是因为宏定义是文本替换

第一个被替换成3+1*3+1,即3+3+1=7,所以启示我们把每个变量单独加上括号

第二个被替换成10*3+3,即30+3=33,所以启示我们把整个表达式加上括号

如下:

C语言预处理——_第8张图片

注意

当预处理器搜索#define定义时,字符串中的符号不被替换

#define MAX 10;
printf("MAX=%d", MAX);

" "里面的内容时字符串常量,不被替换。

接下来就是#和##,它们在#define中有特殊用处。注意,是仅限于宏定义中

这里的#,##不是define前面的,而是一个单独的符号

#将符号变成字符串

对于下面的代码

C语言预处理——_第9张图片

我们是想打印出the value of a is 10  the value of b is 20,这就可以用到#。如果是单纯的宏定义#define PRINT(x) printf("the value of x is %d\n",x) 那结果和上面一样,那如何将“”中的x变成传过去的a或b呢?可以用#。

#a就是“a”

同时要明确printf("hello world")和printf("hello" " world")答应出来结果一样

所以宏定义可写成#define PRINT(x) printf("the value of #x is %d\n",x),对吗?不对,如果这样写,那就相当于 printf("the value of  “x” is %d\n",x),这样的话the value of 和  is %d\n 就不是完整的字符串了,所以应该#define PRINT(x) printf("the value of ”#x“ is %d\n",x)在#x前后都用引号引住。

注意of 后面和 is前面都要加空格。

C语言预处理——_第10张图片

##将分割的字符串连在一起

C语言预处理——_第11张图片

宏和函数的对比

宏通常用于一些简单的运算

C语言预处理——_第12张图片

这个比大小既可以用函数也可以用宏,但用宏更好

优缺点

1.如果a和b是float类型,直接调用Max函数就会出错,因为浮点数的存储和整型不同,这时候就得更改函数的定义了,很麻烦,而宏定义就不用更改

2.在Max函数调用时会有函数调用和返回的消耗(为了调用函数,需要做很多准备工作),需要很多工作才能执行到return (x > y ? x : y);而宏就不需要,它在预处理阶段就完成了替换,可以直接执行(x > y ? x : y)。

但宏也有劣势

1.宏没法进行调试

2.如果宏代表的代码特别长,而main函数中多次运用了该宏,那么预编译完后,代码会特别长

3.宏与类型无关,其实不够严谨

4.带有副作用的宏参数

int Max(int x, int y)
{
	return x > y ? x : y;
}
int main()
{

	int a = 10;
	int b = 20;
	int max1 = Max(a++, b++);
	printf("%d\n ", max1);
    printf("%d\n ", a);
    printf("%d\n ", b);
	return 0;
}

对于这个,用函数比大小,结果是20  11  21。也就是说,再将参数传给函数前,系统会把表达式算出来再使用,而对于后置++则是先使用再++,而宏就不一样了

C语言预处理——_第13张图片

这是因为宏是把整个表达式看作一个整体进行替换,变成了((a++)>(b++)?(a++):(b++)),所以当真正执行到?或:前,++操作已经进行了一次,所以导致结果出乎意料,这种参数就是带有副作用了。

函数无法实现而宏可以实现的

宏的参数可以是类型而函数不可以,例如:

C语言预处理——_第14张图片

再比如malloc函数,int* p = malloc(10 * sizeof(int));这样开辟空间就看着十分别扭,此时就可以用到宏,如下

#define MALLOC(num,type) (type*)malloc(num * sizeof(type))
int* p = MALLOC(10, int);

这样会使代码更好看好理解。

#undef

用于移除一个宏定义

C语言预处理——_第15张图片

C语言预处理——_第16张图片

报错说第8行的max未定义。

命令行定义

在Linux环;境下,代码中可以有未定义的符号,之后在预编译阶段去指定其大小,如下

int main()
{
	int arr[SZ] = { 0 };
	int i;
	for (i = 0; i < SZ; i++)
	{
		arr[i] = i;
	}
	return 0;
}

编译指令:gcc test.c -D SZ=10

gcc test.c表示编译.c文件,-D是说要给一个参数

条件编译

对于某些调试性代码,删掉可惜,保留又碍事,我们就可以用条件编译语句来进行选择性编译,让其还存在于源代码中,但在预编译阶段被删去

#define DEBUG
int main()
{
	int arr[10] = { 0 };
	int i;
	for (i = 0; i < 10; i++)
	{
		arr[i] = i;
#ifdef DEBUG
		printf("%d ", arr[i]);
#endif
	}
	return 0;
}

#ifdef DEBUG 表示如果定义了DEBUG,那么printf语句就执行,#endif(结束条件编译)是和#ifdef配套使用的。

而在上面的代码中,有#define DEBUG(后面可以不写它的值),所以会打印

C语言预处理——_第17张图片

如果没有#define,就不打印

C语言预处理——_第18张图片

其实这段代码在预编译阶段,#ifdef printf("%d ", arr[i]) #endif这三个语句就被删除了

常见的条件编译指令

单分支

#if 常量表达式   

//……

#enif

if后面的常量表达式为真,则代码执行,如下,1为真,所以打印

C语言预处理——_第19张图片

#define MAX 值

#if MAX

//……

#endif

C语言预处理——_第20张图片

因为MAX的值为0,表示假,所以不打印

多分支

#if 常量表达式

//……

#elif 常量表达式

//……

#else

//……

#endif

C语言预处理——_第21张图片

判断是否被定义

#if defined(DEBUG)
    printf("hehe");
#endif

表示如果定义过,就打印,等价于下面的代码

#ifdef DEBUG
    printf("hehe");
#endif

#if !defined(DEBUG)
    printf("hehe");
#endif

表示如果没有定义过,就打印,等价于下面的代码

#ifndef DEBUG
    printf("hehe");
#endif

头文件的包含

例如

#include

#include"test.h"

一般来说,尖括号包含的是库文件,双引号包含的是自己写的头文件。原因如下:

若用了双引号则会先在源文件所在的目录下查找,若未找到,则编译器像查找库函数头文件一样到标准路径下查找,若未找到则报错;若用了尖括号,则直接到标准路径下查找。

既然知道自己写的头文件一定在源文件所在目录下,那就直接用双引号。

如何防止多次包含

如果多个文件嵌套包含,很可能出现一个头文件被包含多次,导致预编译之后代码的冗余,同时降低代码运行效率。我们可以通过Linux环境看到这个效果

指令:gcc test.c -E > test.i  表示先进性预编译,编译后放到test.i文件中

接着用 vim test.i  可以查看test.i文件。如果stdio.h文件被包含了3次,我们就可以看到三段重复的代码。

那如何防止多次包含?

方法一:预编译指令

#ifndef __TEST__H__
#define __TEST__H__

int ADD(int x, int y);

#endif

在头文件test.h里面如上这样写,如果没定义过DEBUG,下面的代码就执行,函数ADD就可以被声明;当再次遇到这个头文件时,DEBUG已经被定义过了,所以#ifndef DEBUG为假,后面的语句就不再执行,ADD函数就不会被重复声明。

注意,一般ifndef后面的符号是把头文件大写并加上下划线

方法二:#pragma once

#pragma once
int ADD(int x, int y);

在test.h中这样写,则只包含一次

你可能感兴趣的:(c语言,算法,开发语言)