# 宏-宏函数

## 怎么选择宏

在软件开发过程中，经常有一些常用或者通用的功能或者代码段，这些功能既可以写成函数，也可以封装成为宏定义。那么究竟是用函数好，还是宏定义好？这就要求我们对二者进行合理的取舍。我们来看一个例子，比较两个数或者表达式大小，首先我们把它写成宏定义：

```c
#define MAX( a, b) ( (a) > (b) (a) : (b) )
　　
int max( int a, int b){
　　return (a > b a : b)
}
```

很显然，我们不会选择用函数来完成这个任务，原因有两个：

* 函数调用会带来额外的开销，它需要开辟一片栈空间，记录返回地址，将形参压栈，从函数返回还要释放堆栈。这种开销不仅会降低代码效率，而且代码量也会大大增加，而使用宏定义则在代码规模和速度方面都比函数更胜一筹；其次，函数的参数必须被声明为一种特定的类型，所以它只能在类型合适的表达式上使用，我们如果要比较两个浮点型的大小，就不得不再写一个专门针对浮点型的比较函数。
* 反之，上面的那个宏定义可以用于整形、长整形、单浮点型、双浮点型以及其他任何可以用“>”操作符比较值大小的类型，也就是说，宏是与类型无关的。
* 和使用函数相比，使用宏的不利之处在于每次使用宏时，一份宏定义代码的拷贝都会插入到程序中。除非宏非常短，否则使用宏会大幅度增加程序的长度。
* 一些任务根本无法用函数实现，但是用宏定义却很好实现。比如参数类型没法作为参数传递给函数，但是可以把参数类型传递给带参的宏。

  利用这个宏，我们就可以为任何类型分配一段我们指定的空间大小，并返回指向这段空间的指针：

  ```c
  #define MALLOC（n, type） \
  　　（ (type *) malloc（（n）* sizeof（type）））
  ```

  我们可以观察一下这个宏确切的工作过程：

  ```c
  int *ptr;
  ptr = MALLOC ( 5, int );

  #将这宏展开以后的结果：
  ptr = (int *) malloc ( (5) * sizeof(int) );
  ```

## 多行宏

```c
//宏定义写出swap（x，y）交换函数
#define swap(x, y)\
x = x + y;\
y = x - y;\
x = x - y;
```

## 字符串常量化运算符（#）

在宏定义中，当需要把一个宏的参数转换为字符串常量时，则使用字符串常量化运算符（#）

```c
#include <stdio.h>

#define  message_for(a, b)  \
    printf(#a " and " #b ": We love you!\n")

int main(void)
{
   message_for(Carole, Debra);
   return 0;
}
```

## 标记粘贴运算符（##）

宏定义内的标记粘贴运算符（##）会合并两个参数。它允许在宏定义中两个独立的标记被合并为一个标记。例如：

```c
#include <stdio.h>

#define tokenpaster(n) printf ("token" #n " = %d", token##n)

int main(void)
{
   int token34 = 40;

   tokenpaster(34);
   return 0;
}
```

当上面的代码被编译和执行时，它会产生下列结果：

```c
token34 = 40
```

这是怎么发生的，因为这个实例会从编译器产生下列的实际输出：

```c
printf ("token34 = %d", token34);
```

这个实例演示了 token##n 会连接到 token34 中

## 宏语句

| 指令       | 描述                               |
| -------- | -------------------------------- |
| #define  | 定义宏                              |
| #include | 包含一个源代码文件                        |
| #undef   | 取消已定义的宏                          |
| #ifdef   | 如果宏已经定义，则返回真                     |
| #ifndef  | 如果宏没有定义，则返回真                     |
| #if      | 如果给定条件为真，则编译下面代码                 |
| #else    | #if 的替代方案                        |
| #elif    | 如果前面的 #if 给定条件不为真，当前条件为真，则编译下面代码 |
| #endif   | 结束一个 #if……#else 条件编译块            |
| #error   | 当遇到标准错误时，输出错误消息                  |
| #pragma  | 使用标准化方法，向编译器发布特殊的命令到编译器中         |

## 预定义宏

ANSI C 定义了许多宏，但是不能直接修改这些预定义的宏。

| 宏        | 描述                                |
| -------- | --------------------------------- |
| **DATE** | 当前日期，一个以 "MMM DD YYYY" 格式表示的字符常量。 |
| **TIME** | 当前时间，一个以 "HH:MM:SS" 格式表示的字符常量。    |
| **FILE** | 这会包含当前文件名，一个字符串常量。                |
| **LINE** | 这会包含当前行号，一个十进制常量。                 |
| **STDC** | 当编译器以 ANSI 标准编译时，则定义为 1。          |

让我们来尝试下面的实例：

```c
#include <stdio.h>

main()
{
   printf("File :%s\n", __FILE__ );
   printf("Date :%s\n", __DATE__ );
   printf("Time :%s\n", __TIME__ );
   printf("Line :%d\n", __LINE__ );
   printf("ANSI :%d\n", __STDC__ );

}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://im-qianuxn.gitbook.io/pytorch/ji-suan-ji/c/hong-hong-han-shu.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
