1.预定义符号

2.#define定义常量

3.#define定义宏

4.带有副作用的宏参数

5.宏替换的规则

6.宏函数的对比

7.#和##

8.命名约定

9.#undef

10.命令行定义

11.条件编译

12.头文件的包含


1.预定义符号

C语言设置了一些预定义符号,可以直接使用,预定义符号也是在预处理阶段处理的。

__FILE__ //进行编译的文件
__LINE__ //文件当前的行号
__TIME__ //文件被编译的时间
__DATA__ //文件被编译的日期
__STDC__ //如果编辑器遵循ANSI C,其值为1,否则未定义
//例子:
printf("file:%s line:%d\n",__FILE__,__LINE__);

(续行符 \ ,后面不能有空白)

2.define定义常量

基本语法:

#define name stuff

例子:

#define MAX 100
#define reg register
#define do_forever for(;;)
//对于for(),初始值、判断条件、更新都可省略,但判断条件要省略的话,就进入了死循环
#define DEBUG_PRINT printf(" ")
//若define最后加上; , 会把;也替换进去:
#define MAX 100;
//MAX   ==    100;   而不是100

3.#define定义宏

#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常叫做,或定义宏。
下面是宏的声明方式:

#define name( parament -list) stuff

其中parament-list是一个由逗号隔开的符号表,他们可能出现在stuff中。
注意:

  • 参数列表的左括号必须与name紧邻,如果二者之间有任何空白的存在,在参数列表就会解释为stuff的一部分。
  • 使用宏传递参数时,他不会计算传递的表达式的值,而是将表达式直接替换。
  • 要考虑计算顺序问题,多加括号。
    代码示例:
#define SQUARE(x) x * x
int main()
{
	int a = 5;
	printf("%d\n", SQUARE(a + 1));//11  -- 将5+1替换到x * x为a + 1 * a + 1
	return 0;
}
#define SQUARE(x) ((x) * (x))
int main()
{
	int a = 5;
	printf("%d\n", SQUARE(a + 1));//36
	return 0;
}

4.带有副作用的宏参数

副作用就是表达式求值的时候出现的永久性后果。
例如:x++
对于MAX宏更要注意:

#define MAX(a,b) ((a) > (b) ? (a) : (b))
...
x=5;
y=8;
z=MAX(x++,y++);
//z=((x++) > (y++) ? (x++) : (y++))

5.宏替换的规则

在程序中扩展#define定义符号和宏时,需要涉及几个步骤:

  1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,他们首先被替换。
  2. 替换文本后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
  3. 最后,再次对结果文件进行扫描,看看他是否包含任何由#define定义的符号。如果有,重复上述步骤。
    注意:
    1.宏参数和#define定义中可以出现其他#define定义的符号。但对于宏,不能出现递归。
    2.当预处理器搜索#define定义的符号的时候,字符串常量的内容不被搜索。

6.宏函数的对比

对于简单的运算,如:

#define MAX(a,b) ((a) > (b) ? (a) : (b))

为什么不用函数来完成?
原因有二:
1.宏比函数在程序规模和速度方面更胜一筹。
2.宏是直接替换的,参数是类型无关的。
和函数相比宏的劣势:

  1. 每次使用宏时,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
  2. 宏没法调试。
  3. 宏由于类型无关,不严谨。
  4. 宏可能带来运算优先级问题,要多加括号。

宏可以做函数做不到的事情。比如:宏的参数可以是类型,但函数做不到。
代码示例:

#define MALLOC(num,type)\
(type*)malloc(num * sizeof(type))
int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	int* ptr = MALLOC(10, int);
	//int* ptr = (int*)malloc(10*sizeof(int));
	return 0;
}

在这里插入图片描述

7.#和##

7.1#运算符

#运算符将宏的一个参数转换为字符串字面量。它仅允许出现在带参数的宏的替换列表中,它所执行的操作可以理解为“字符串化”。
代码示例:

int a = 10;
#define PRINT(n) printf("the value of " #n " is %d",n)
int main()
{
	PRINT(a);
	return 0;
}

7.2##运算符

##把位于两边的符号合成一个符号,被称为记号粘合
当我们写一个函数求2两个数的较大值的时候,不同的数据类型就得写不同的函数。
代码示例:

int int_max(int x, int y)
{
	return x > y ? x : y;
}
float float_max(float x, float y)
{
	return x > y ? x : y;
}

使用宏后:

#define GEBERTIC_MAX(type)\
type type##_max(type x,type y)\
{\
	return x>y?x:y;\
}

GEBERTIC_MAX(int);
GEBERTIC_MAX(float);

int main()
{
	int m = int_max(2, 3);
	printf("%d\n", m);
	float fm = float_max(3.5f, 4.5f);
	printf("%f\n", fm);
	
	return 0;
}

8.命名约定

  • 宏名全大写
  • 函数名不全大写

9.#undef

这条指令用来移除一个宏定义。

10.命令行定义

C编辑器提供一种能力,允许在命令行中定义符号,用于启动编译过程。
如下面代码在命令行中定义ARRAT_SIZE以满足内存要求:

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;
}
//编译指令 gcc -D ARRAY_SIZE=10 programe.c

11.条件编译

在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为有条件编译指令。
常见条件编译指令,代码示例:

1.
#if 常量表达式
// ...
#endif
2.多个分支的条件编译
#if 常量表达式
//...
#elif
//...
#else
//...
#endif
3.判断是否被定义
#if defined(X)/#ifdef X
#if !defined(X)/#ifndef X
//...
#elif defined(Y)...
//...
#endif

代码应用示例:

#define M 3
int main()
{
#if M==0
	printf("ghehe\n");
#elif M==1
	printf("haha\n");
#elif M==2
	printf("OK\n");
#else
	printf("bit\n");
#endif
	return 0;
}
#define MAX 1
int main()
{
# ifdef MAX
	printf("dede\n");
#endif
#if defined(MAX)
	printf("dede\n");
#endif
#ifndef MAX
	printf("hehe\n");
#endif
#if !defined(MAX)
	printf("hehe\n");
#endif
	return 0;
}

12.头文件的包含

12.1头文件被包含的方式

1,本地文件包含:#include “filename”
查找策略:现在源文件目录下查找,如果没找到,就和库函数一样去标准位置(安装路径)去找。
2.库文件包含: #include
查找策略:直接去标准路径下去查找。

对于库文件也可以用“”形式包含,但是这样做效率降低,并且不容易区分是库文件还是本地文件。

12.2嵌套文件包含

#include指令可以使另外一个文件被编译,所以如果重复包含,对编译的压力就比较大。
方法:条件编译
每一个头文件开头写:

#ifdef __TEST_H__
#define __TEST_H__
//文件内容
#endif

或者

#pragma once
Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐