前言

在C语言中有着许许多多的符号,下面就一起来看看(#)这一符号吧,它有着许许多多的功能。


一、字符串化操作

#号作为字符串化操作符(Stringizing Operator)用于将宏参数转换为一个字符串常量。它可以用于在宏定义中将宏参数的值转换为一个以双引号括起来的字符串。
下面是使用 # 号进行字符串化操作的示例:

#include <stdio.h>

#define STR(x) #x

int main() {
    int num = 42;
    printf("Value of num: %s\n", STR(num));

    return 0;
}

输出结果:

Value of num: 42

在上面的示例中,我们定义了一个宏 STR,它接收一个参数 x。# 运算符将宏参数 x 转换为字符串常量。当我们在 printf 函数中使用 STR(num) 时,它会被展开为 “num” 字符串,并打印出 Value of num: 42。
需要注意的是# 号只能在宏定义中使用,而不能在普通的C代码中使用。它是在编译之前由预处理器进行处理的。此外,宏参数在字符串化操作中会被直接替换,不会进行任何类型转换。因此,在使用字符串化操作时,确保宏参数的类型和值都符合预期。

二、预处理标记连接

#号作为预处理标记连接操作符(Token Pasting Operator)用于将两个预处理标记连接为一个单独的标记。它可以用于在宏定义中将多个标记组合成一个新的标记。

下面是使用 # 号进行标记连接操作的示例:

#include <stdio.h>

#define CONCAT(a, b) a ## b

int main() {
    int num = 42;
    printf("Value of concatenated: %d\n", CONCAT(num, 0));

    return 0;
}

输出结果:

Value of concatenated: 420

在上面的示例中,我们定义了一个宏 CONCAT,它接收两个参数 a 和 b。## 运算符将这两个标记连接成一个新的标记。当我们使用 CONCAT(num, 0) 时,它会被展开为 num0,并打印出 Value of concatenated: 420。

需要注意的是## 运算符只能在宏定义中使用,而不能在普通的C代码中使用。它是在编译之前由预处理器进行处理的。另外,被连接的标记可以是任意类型的标记,包括关键字、运算符、常量等。##运算符不会对连接后的标记进行任何类型转换或字符串化。它只是简单地将两个标记连接在一起。因此,在使用标记连接操作时,确保连接的标记具有合法的语法和语义。

另外,如果其中一个参数是已经定义的宏,则在连接之前会先对宏进行展开。例如:

#define FOO 1

int main() {
    int FOOBAR = CONCAT(FOO, BAR);
    return 0;
}

在上面的示例中,CONCAT(FOO, BAR) 会被展开为 FOOBAR,因为宏 FOO 已经被定义为 1。所以最终的代码等价于 int FOOBAR = 1;。

在使用 # 进行预处理标记连接操作时,要遵循一些规则:
1、连接的标记不能包含空格或任何其他非标识符字符。
2、连接的标记不能构成无效的C语法。
3、如果其中一个参数是宏,则在连接之前会先展开宏。
正确使用预处理标记连接操作符可以增强宏的灵活性和通用性,提供更多的代码重用和简化的机会。

三、文件包含

#include 用于包含头文件的预处理指令。它允许将其他源代码文件的内容插入到当前文件中。

#include <stdio.h>

int main() {
    printf("Hello, World!\n");
    return 0;
}

在上面的示例中,#include <stdio.h> 将标准输入输出函数的声明和定义包含到当前文件中,以便我们可以使用 printf 函数打印输出。

需要注意的是,# 号只能在预处理指令中使用,而不能在普通的C代码中使用。它是在编译之前由预处理器进行处理的。

四、宏定义

#define 定义宏的预处理指令。它允许我们创建一些可以在代码中重复使用的片段。

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

int main() {
    int x = 10, y = 20;
    int max_value = MAX(x, y);
    printf("Maximum value is: %d\n", max_value);

    return 0;
}

在上面的示例中,我们定义了一个 MAX 宏,它接收两个参数 a 和 b,并返回其中较大的值。通过使用 #define 和 # 运算符,我们可以将宏定义为一个带有参数的表达式。

五、条件编译

#if, #ifdef, #ifndef 等条件编译指令也使用了 # 号。这些指令允许根据条件来选择性地包含或排除一些代码。

#include <stdio.h>

#define DEBUG

int main() {
    #ifdef DEBUG
        printf("Debug mode enabled\n");
    #else
        printf("Debug mode disabled\n");
    #endif

    return 0;
}

在上面的示例中,我们使用 #ifdef 指令检查是否定义了 DEBUG 宏。如果定义了该宏,就会打印出 “Debug mode enabled”;如果没有定义该宏,就会打印出 “Debug mode disabled”。

六、预定义宏

预定义宏是在编译器中提前定义好的一些宏,可以用于获取关于源代码文件、行号、日期、时间等信息。下面介绍一些常用的预定义宏及其示例运用:
1、__FILE__该宏会被替换为当前源代码文件的路径名字符串。

示例:

```c
#include <stdio.h>

int main() {
    printf("Current file: %s\n", __FILE__);
    return 0;
}

输出结果:

Current file: main.c

在上述示例中,使用__FILE__ 预定义宏获取当前源代码文件的路径名,并将其打印出来。

2、__LINE__该宏会被替换为当前代码行号的整数值。

示例:

#include <stdio.h>

int main() {
    printf("Current line number: %d\n", __LINE__);
    return 0;
}

输出结果:

Current line number: 6

在上述示例中,使用__LINE__ 预定义宏获取当前代码行号,并将其打印出来。

3、__DATE__该宏会被替换为一个字符串常量,表示当前编译日期。

示例:

#include <stdio.h>

int main() {
    printf("Compilation date: %s\n", __DATE__);
    return 0;
}

输出结果:

Compilation date: Aug 25 2022

在上述示例中,使用__DATE__ 预定义宏获取当前编译日期,并将其打印出来。

4、__TIME__该宏会被替换为一个字符串常量,表示当前编译时间。

示例:

#include <stdio.h>

int main() {
    printf("Compilation time: %s\n", __TIME__);
    return 0;
}

输出结果:

Compilation time: 13:45:29

在上述示例中,使用__TIME__ 预定义宏获取当前编译时间,并将其打印出来。

预定义宏可以在源代码中直接使用,无需进行额外的定义或声明。它们提供了方便的方法来访问与源代码文件、行号、日期和时间相关的信息,可以帮助我们进行调试、日志记录等操作。

文章结束,有误之处,望大佬指正

相关内容请关注:
1、结构体详解
2、所有C关键字简介
3、难点关键字详解加特殊运用

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐