您的位置:首页 > 房产 > 家装 > 中国电子加工网_能看任何网站的浏览器_国际新闻界_小说排行榜百度搜索风云榜

中国电子加工网_能看任何网站的浏览器_国际新闻界_小说排行榜百度搜索风云榜

2025/5/16 1:16:32 来源:https://blog.csdn.net/qq_67693066/article/details/147070172  浏览:    关键词:中国电子加工网_能看任何网站的浏览器_国际新闻界_小说排行榜百度搜索风云榜
中国电子加工网_能看任何网站的浏览器_国际新闻界_小说排行榜百度搜索风云榜

C —— 宏

  • 基本宏定义
    • 符号常量
    • 函数式宏
  • 宏的特殊用法
    • 多行宏
    • 字符串化操作符
    • 连接操作符
    • 可变参数宏
      • 基本语法
      • 省略可变参数(C99 和 GNU C 扩展)
      • 计算可变参数的个数(GNU C 扩展)**
      • `_Generic` + 可变参数宏(C11)
    • 可变参数宏的常见用途
    • 注意事项
    • 总结
  • 常用预定义宏
      • 1. `__LINE__` - 当前行号
      • 2. `__FILE__` - 当前文件名
      • 3. `__DATE__` - 编译日期(格式 "MMM DD YYYY")
      • 4. `__TIME__` - 编译时间(格式 "HH:MM:SS")
      • 5. `__STDC__` - 是否遵循 ANSI C 标准
      • 6. `__func__` - 当前函数名(C99)
      • 综合示例:调试日志宏
      • 关键点总结
  • 条件编译
  • C语言条件编译详解
    • 基本条件编译指令
      • 1. `#ifdef` / `#ifndef` / `#endif`
      • 2. `#if` / `#elif` / `#else` / `#endif`
    • 常见应用场景
      • 1. 跨平台开发
      • 2. 调试代码
      • 3. 功能开关
    • 预定义宏与条件编译
    • 实用技巧
      • 1. 宏定义检查
      • 2. 防止头文件重复包含
      • 3. 编译器特定指令
    • 注意事项
    • 高级用法
      • 1. 宏参数的条件编译
      • 2. 静态断言(C11之前)

我们在学习C语言时,很早就接触过了宏的概念,今天我们就对宏这一用法进行详细的梳理:

基本宏定义

符号常量

这种用法我们最熟悉,就是简单定义,然后放到代码段中去用:
在这里插入图片描述

#define PI 3.14159
#define MAX_SIZE 100
#define AUTHOR "John Doe"

这里我们就简单介绍一下,不做过多展开。

函数式宏

除了我们自己可以写函数之外,宏也可以定义函数:
在这里插入图片描述
在这里插入图片描述
这里给所有参数都带上括号,是为了保证运算顺序的正确性。如果不带括号,运算结果可能就会大相径庭:

#define FUCNTION(a,b)(a * b)int main()
{printf("%d\n", FUCNTION(10 + 10,20 + 20));
}

在这里插入图片描述
但是如果我加上括号:

#define FUCNTION(a,b)((a) * (b))int main()
{printf("%d\n", FUCNTION(10 + 10,20 + 20));
}

在这里插入图片描述
因为,宏不对本身做任何处理,它只会傻瓜式的展开,我们来看看两个函数展开之后长啥样子:
在这里插入图片描述
所以在编写宏时,如果运算顺序很重要,记得打上括号哦~。

宏的特殊用法

多行宏

使用反斜杠\可以定义多行宏:

# define SWAP(a,b,type){ \type temp = a; \a = b; \b = temp; \
}int main()
{int x = 10;int y = 20;printf("%d %d\n", x, y);SWAP(x, y, int)printf("%d %d\n", x, y);
}

在这里插入图片描述

字符串化操作符

将宏参数转换为字符串:

#define STR(x) #xint main()
{printf("%s\n", STR(123456));
}

在这里插入图片描述

连接操作符

将两个标记连接成一个:

#define CONCAT(a,b) a##bint main()
{int xy = 10;printf("%d\n", CONCAT(x, y));
}

在这里插入图片描述

可变参数宏

接下来就是一些比较高级的用法了,在 C 语言中,可变参数宏(Variadic Macros)允许宏接受 可变数量的参数,类似于 printf() 这样的可变参数函数。它使用 … 和 VA_ARGS 来实现,是 C99 标准引入的特性。

基本语法

#define MACRO_NAME(fixed_args, ...) replacement_text __VA_ARGS__
... 表示可变参数部分。__VA_ARGS__ 在宏展开时会被替换为传入的可变参数。
#define LOG(fmt,...) printf(fmt,__VA_ARGS__)int main()
{LOG("Hello, %s! Your score is %d.\n", "Alice", 95);
}

省略可变参数(C99 和 GNU C 扩展)

如果可变参数可能为空,C99 要求至少有一个参数,但 GNU C 允许完全省略:

// C99 标准写法(必须至少有一个可变参数)
#define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__)// GNU C 扩展(允许完全省略可变参数)
#define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__)int main() {LOG("Hello, world!\n");  // ✅ GNU C 允许,C99 会报错return 0;
}

在这里插入图片描述
说明:

## 是 标记粘贴运算符,如果 __VA_ARGS__ 为空,它会删除前面的逗号,避免语法错误。

计算可变参数的个数(GNU C 扩展)**

#define COUNT_ARGS(...) sizeof((int[]){__VA_ARGS__}) / sizeof(int)int main() {printf("Number of args: %zu\n", COUNT_ARGS(1, 2, 3, 4));  // 输出 4return 0;
}

注意:

  • 这种方法仅适用于 相同类型的参数(如 int)。
  • 不是标准 C,仅适用于 GCC/Clang。

_Generic + 可变参数宏(C11)

结合 _Generic 实现类型安全的可变参数宏:

#include <stdio.h>#define PRINT_TYPE(x) _Generic((x), \int: printf("%d\n", x), \float: printf("%f\n", x), \char*: printf("%s\n", x) \
)#define PRINT_ALL(...) do { \int arr[] = {__VA_ARGS__}; \for (size_t i = 0; i < sizeof(arr)/sizeof(arr[0]); i++) { \PRINT_TYPE(arr[i]); \} \
} while(0)int main() {PRINT_ALL(42, 3.14, "Hello");  // 需要更复杂的实现return 0;
}

说明:

  • 这个例子只是示意,实际实现需要更复杂的宏技巧。

可变参数宏的常见用途

  1. 日志系统
    #define LOG_INFO(fmt, ...) fprintf(stderr, "[INFO] " fmt, ##__VA_ARGS__)
    
  2. 调试输出
    #define DBG_PRINT(...) printf("[DEBUG] %s:%d: ", __FILE__, __LINE__); \printf(__VA_ARGS__)
    
  3. 泛型容器操作
    #define FOREACH(item, ...) \for (int keep = 1, count = 0; keep && count != sizeof((int[]){__VA_ARGS__})/sizeof(int); \keep = !keep, count++) \for (item = ((int[]){__VA_ARGS__})[count]; keep; keep = !keep)
    

注意事项

  1. 标准 C (C99) 要求至少一个可变参数,但 GNU C 允许零个。
  2. 避免参数副作用(如 LOG("%d", i++) 可能导致多次求值)。
  3. 调试困难,因为宏在预处理阶段展开,错误信息可能难以理解。
  4. 可读性差,复杂的可变参数宏可能难以维护。

总结

特性说明
...定义可变参数
__VA_ARGS__引用可变参数
##__VA_ARGS__允许省略可变参数(GNU C)
_Generic + 可变宏类型安全可变参数(C11)
do-while(0)包裹多语句宏

最佳实践:

  • 优先使用 函数 替代复杂宏。
  • 如果必须用宏,确保 可读性安全性
  • 在跨平台代码中,避免依赖 GNU C 扩展(如 ##__VA_ARGS__)。

可变参数宏非常强大,但需要谨慎使用!

常用预定义宏

__LINE__      // 当前行号
__FILE__      // 当前文件名
__DATE__      // 编译日期
__TIME__      // 编译时间
__STDC__      // 如果编译器符合ANSI C标准则为1
__func__      // 当前函数名 (C99)

下面是对这些预定义宏的具体用法示例,每个宏都附带一个完整的代码示例和输出说明:


1. __LINE__ - 当前行号

#include <stdio.h>int main() {printf("This line is at: %d\n", __LINE__);  // 输出当前行号printf("Now we're at line: %d\n", __LINE__); // 行号+1return 0;
}

输出示例:

This line is at: 4
Now we're at line: 5

2. __FILE__ - 当前文件名

#include <stdio.h>int main() {printf("This code is in file: %s\n", __FILE__);return 0;
}

输出示例:

This code is in file: example.c

3. __DATE__ - 编译日期(格式 “MMM DD YYYY”)

#include <stdio.h>int main() {printf("This program was compiled on: %s\n", __DATE__);return 0;
}

输出示例:

This program was compiled on: Jun 15 2023

4. __TIME__ - 编译时间(格式 “HH:MM:SS”)

#include <stdio.h>int main() {printf("Compilation time: %s\n", __TIME__);return 0;
}

输出示例:

Compilation time: 14:30:45

5. __STDC__ - 是否遵循 ANSI C 标准

#include <stdio.h>int main() {#if __STDC__printf("This compiler is ANSI C compliant\n");#elseprintf("This compiler is NOT ANSI C compliant\n");#endifreturn 0;
}

典型输出(现代编译器):

This compiler is ANSI C compliant

6. __func__ - 当前函数名(C99)

#include <stdio.h>void my_function() {printf("Current function: %s\n", __func__);
}int main() {printf("Entering %s\n", __func__);my_function();return 0;
}

输出:

Entering main
Current function: my_function

综合示例:调试日志宏

#include <stdio.h>#define LOG(msg) printf("[%s] %s (Line %d in %s): %s\n", \__TIME__, __func__, __LINE__, __FILE__, msg)int main() {LOG("Program started");if (1) {LOG("Inside conditional block");}return 0;
}

输出示例:

[14:30:45] main (Line 7 in example.c): Program started
[14:30:45] main (Line 9 in example.c): Inside conditional block

关键点总结

作用输出示例标准支持
__LINE__当前行号42C89
__FILE__源文件名"example.c"C89
__DATE__编译日期"Jun 15 2023"C89
__TIME__编译时间"14:30:45"C89
__STDC__ANSI C 合规1C89
__func__函数名"main"C99

注意事项:

  1. __func__ 是 C99 引入的,不是字符串字面量(不能直接拼接)
  2. 所有带下划线的宏由编译器预定义,不可修改
  3. 在调试和日志系统中特别有用

条件编译

C语言条件编译详解

条件编译是C语言预处理器提供的一项重要功能,它允许程序员在编译时根据不同的条件选择性地包含或排除代码段。条件编译在跨平台开发、调试代码、功能开关等场景中非常有用。

基本条件编译指令

1. #ifdef / #ifndef / #endif

#ifdef MACRO_NAME// 如果定义了MACRO_NAME,则编译这部分代码
#endif#ifndef MACRO_NAME// 如果没有定义MACRO_NAME,则编译这部分代码
#endif

示例:

#define DEBUG  // 定义DEBUG宏#ifdef DEBUGprintf("Debug information\n");
#endif#ifndef RELEASEprintf("Not in release mode\n");
#endif

2. #if / #elif / #else / #endif

#if 表达式// 如果表达式为真,编译这部分代码
#elif 其他表达式// 如果前面的表达式为假且这个表达式为真,编译这部分代码
#else// 如果所有表达式都为假,编译这部分代码
#endif

示例:

#define VERSION 3#if VERSION == 1printf("Version 1\n");
#elif VERSION == 2printf("Version 2\n");
#elif VERSION == 3printf("Version 3\n");
#elseprintf("Unknown version\n");
#endif

常见应用场景

1. 跨平台开发

#ifdef _WIN32// Windows平台专用代码#include <windows.h>
#elif __linux__// Linux平台专用代码#include <unistd.h>
#elif __APPLE__// macOS平台专用代码#include <TargetConditionals.h>
#endif

2. 调试代码

#define DEBUG_MODE 1#if DEBUG_MODE#define DEBUG_PRINT(fmt, ...) printf("[DEBUG] " fmt, ##__VA_ARGS__)
#else#define DEBUG_PRINT(fmt, ...) // 定义为空,不产生任何代码
#endifint main() {DEBUG_PRINT("This is debug info: %d\n", 42);return 0;
}

3. 功能开关

#define FEATURE_A_ENABLED 1
#define FEATURE_B_ENABLED 0#if FEATURE_A_ENABLEDvoid feature_a() {printf("Feature A is enabled\n");}
#endif#if FEATURE_B_ENABLEDvoid feature_b() {printf("Feature B is enabled\n");}
#endifint main() {#if FEATURE_A_ENABLEDfeature_a();#endif#if FEATURE_B_ENABLEDfeature_b();#endifreturn 0;
}

预定义宏与条件编译

C语言提供了一些预定义宏,常用于条件编译:

#if __STDC_VERSION__ >= 201112L// C11或更高版本代码
#elif __STDC_VERSION__ >= 199901L// C99代码
#else// C89代码
#endif

实用技巧

1. 宏定义检查

#if defined(MACRO1) && !defined(MACRO2)// 如果定义了MACRO1且没有定义MACRO2
#endif

2. 防止头文件重复包含

// myheader.h
#ifndef MYHEADER_H
#define MYHEADER_H// 头文件内容#endif // MYHEADER_H

3. 编译器特定指令

#ifdef __GNUC__// GCC编译器特有代码#define likely(x)       __builtin_expect(!!(x), 1)#define unlikely(x)     __builtin_expect(!!(x), 0)
#else// 其他编译器#define likely(x)       (x)#define unlikely(x)     (x)
#endif

注意事项

  1. 条件编译指令必须在函数体外使用
  2. 表达式必须是常量表达式
  3. 过度使用条件编译会使代码难以维护
  4. 确保所有条件分支都有正确的#endif匹配

高级用法

1. 宏参数的条件编译

#define CONFIG(opt) (defined(CONFIG_##opt) && CONFIG_##opt)#if CONFIG(DEBUG)printf("Debug mode enabled\n");
#endif

2. 静态断言(C11之前)

#define STATIC_ASSERT(expr, msg) \typedef char static_assertion_##msg[(expr) ? 1 : -1]STATIC_ASSERT(sizeof(int) == 4, int_size_must_be_4_bytes);

条件编译是C语言中非常强大的功能,合理使用可以大大提高代码的灵活性和可移植性。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com