预处理详解

发布于:2024-07-05 ⋅ 阅读:(23) ⋅ 点赞:(0)

1.宏替换的规则

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

2.宏函数的对比 

 宏通常被应用于执行简单的运算。

比如在两个数中找出较大的一个时,写成下面的宏,会更有优势一些。

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

 那为什么不用函数来完成这个任务?

这里有两个原因:

1.用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹
2.更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用于 >来比较的类型。宏的参数是类型无关的

但是和函数相比宏是有劣势的:

1.每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
2.宏是没法调试的。
3.宏由于类型无关,也就不够严谨。
4.宏可能会带来运算符优先级的问题,导致程容易出现错

 宏有时会做到函数做不到的事。比如:宏的参数可以出现类型,但是函数做不到。

下面给出个例子进行理解这句话:

函数的参数是不可能出现类型的。就像是我框出来的,如果没有前面的宏定义,仅仅是Malloc(10,int)这种写法是不可以的,因为函数的参数是不包括类型的,只有宏才可以在参数上存在类型。 

那么下面就是宏和函数的一个在某些方面的对比表格,我们可以看一下:

3.#和## 

3.1#运算符 

#运算符将宏的一个参数 转换 为字符串字面量。它仅允许出现在带参数的宏的替换列表中。

#运算符所执行的操作可以理解为”字符串化“。

我们怎么理解这些信息呢?下面我来讲解一下:

我们先来看一下这两个打印的结果,两个字符串即使中间空着打印出来的东西也是一样的。我们知道这个概念了之后就可以进行我们的讲解了。

我们如果想在屏幕上打印出这样的信息是不是要不断地使用函数,不断地重复the value of a is这串字符串呢,我们如果想写的简洁一点是不是要用到?下面我们就用宏来解决一下。 

这就是用#define定义宏得到的结果,但是看我框住的地方,为什么都是n? 这时候就要用到 # 这个运算符了,我们再把他的用法拿出来看一下:

#运算符将宏的一个参数 转换 为字符串字面量。它仅允许出现在带参数的宏的替换列表中。 

当我们按照上面的方式调用的时候:
print(a,"%d");//当我们把a替换到宏的体内时,就出现了#a,而#a就是转换为"a",时一个字符串 

#运算符所执行的操作可以理解为”字符串化“。

这样是不是就能理解了。

3.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 GENERIC_MAX(type) \
type type##_max(type x, type y)\//从这往下都是在模拟函数的实现,并不是真正的函数因为这是定义宏,所以用的是换行符,第一个type是返回类型,第二个type和max是要用##进行连接的。
{ \
 return (x>y?x:y); \
}
GENERIC_MAX(int) // 替换到宏体内后int##_max生成了新符号int_max做函数名

GENERIC_MAX(float) // 替换到宏体内后float##_max生成了新符号float_max做函数名
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;
}

这就是##的使用方法,用于连接。但是在实际开发过程中##使用很少,很难举出非常贴切的例子。

4.命名约定 

一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。那我们平时的一个习惯是
把 宏 名全部大写
函数名 不要全部大写 

5.#undef 

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

在C语言中,`#undef` 指令用于取消之前通过 `#define` 指令定义的宏。当你使用 `#undef` 后,指定的宏将不再被识别,直到它被重新定义。

`#undef` 的语法如下:
 

#undef 宏名

其中“宏名”是你想要取消定义的宏的名称。

使用 `#undef` 的一个常见场景是在条件编译中,你可能想要根据不同的条件定义或取消定义同一个宏。例如:

#define DEBUG

#ifdef DEBUG
    printf("Debug mode is on.\n");
#endif

#undef DEBUG

#ifdef DEBUG
    // 这部分代码不会被执行,因为DEBUG宏已经被取消定义了
    printf("Debug mode is on.\n");
#endif

在这个例子中,`DEBUG` 宏首先被定义,然后在条件编译块中使用。之后,通过 `#undef DEBUG` 取消了 `DEBUG` 宏的定义,因此在第二个条件编译块中,即使条件为真,相关的代码也不会被执行。

使用 `#undef` 可以帮助你控制宏的作用域,避免宏名冲突,并且在需要时可以重新定义宏

仅供了解:

在C语言中,`#if`、`#ifdef` 和 `#ifndef` 是预处理指令,用于条件编译。它们允许你根据特定条件来包含或排除代码块。`#if`、`#ifdef` 和 `#ifndef` 通常与 `#endif` 配对使用,以定义一个条件编译的代码块。

- `#if` 指令用于检查一个常量表达式是否为真(非零)。如果表达式为真,则包含 `#if` 和 `#endif` 之间的代码块。

- `#ifdef` 指令用于检查一个宏是否已经被定义。如果宏已定义,则包含 `#ifdef` 和 `#endif` 之间的代码块。

- `#ifndef` 指令用于检查一个宏是否未被定义。如果宏未定义,则包含 `#ifndef` 和 `#endif` 之间的代码块。

这些指令可以嵌套使用,以实现更复杂的条件编译逻辑。

下面是一个使用 `#if`、`#ifdef` 和 `#ifndef` 的例子:

#define DEBUG 1

int main() {
    #if DEBUG
    printf("Debug mode is on.\n");
    #endif

    #ifdef DEBUG
    printf("DEBUG is defined.\n");
    #endif

    #ifndef DEBUG
    printf("DEBUG is not defined.\n");
    #else
    printf("DEBUG is defined.\n");
    #endif

    return 0;
}


 

在这个例子中:

- `#if DEBUG` 会检查 `DEBUG` 是否被定义且其值为非零,由于 `DEBUG` 被定义为 `1`,所以 `printf("Debug mode is on.\n");` 会被执行。
- `#ifdef DEBUG` 会检查 `DEBUG` 是否被定义,由于 `DEBUG` 已经被定义,所以 `printf("DEBUG is defined.\n");` 会被执行。
- `#ifndef DEBUG` 会检查 `DEBUG` 是否未被定义,由于 `DEBUG` 已经被定义,所以 `#ifndef` 和 `#else` 之间的代码块不会被执行。

这些指令在编写可配置的代码时非常有用,例如,你可以使用它们来控制调试信息的输出、启用或禁用特定的功能等。

6.命令行定义 

 许多C的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。

例如:当我们根据同一个源文件要编译出一个程序的不同版本的时候,这个特性有点用处。(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一个机器内存大些,我们需要一个数组能够大些。)

#include <stdio.h>
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