C语言入门 – 宏定义

LongGuan_admin 发布于 2026-01-21 74 次阅读


1. 概念

C语言中的宏定义是一种预处理指令,它允许开发者为一段代码或值定义一个名称,称为宏。在编译程序时,预处理器会在实际编译之前对源代码进行预处理,将宏名称替换为其定义的内容。宏定义通常使用 #define 指令来实现。

宏定义的本质是在预处理时对文本进行替换,可以减少栈空间的使用 ,在编译期间计算(0运行开销 : 编译时即确定大小)

#define 名称/宏名/别名    代码块/数据/原来的数据
#define macro_name replacement_text 
//macro_name 是宏的名称,
//replacement_text 是宏替换时要插入的文本。

1.1 格式

  • 简单的宏定义:
    • #define <宏名> <字符串>
  • 带参数的宏定义(宏函数)
    • #define <宏名>(参数表) <宏体>
    • #define A(a,b,c) ({a=1;b+=1;c=3;a+b+c;})
  • #define xxx() ({})
#define MAX(a, b) ((a) > (b) ? (a) : (b))
//这是一个多个参数的宏
在上面的例子中,MAX 是一个带参数的宏,它接受两个参数 a 和 b,并返回两者中的较大值。在代码中使用 MAX(x, y) 时,预处理器会将其替换为 ((x) > (y) ? (x) : (y))。

1.2 注意

宏定义在C语言中非常有用,它们可以简化代码提高代码的可读性和可维护性,并允许开发者在编译时进行一些特定的代码替换或操作。然而,由于宏定义只是简单的文本替换没有类型检查或其他编译时的检查,因此在使用时需要特别小心,以避免一些常见的错误和陷阱。

  • 解决方案
    • 传参时检查类型是否符合要求
    • 宏代码块要加()保证其优先级不会因为跟其他表达式结合导致,优先级混乱

2. 复杂的宏定义

在C语言中,宏定义可以非常复杂,并且可以用于生成相当复杂的代码片段。通过结合参数、操作符、条件和嵌套宏,可以创建出功能强大的宏

复杂宏的每一行使用' \ '分隔换行,提高代码可读性

#define IS_EVEN(x) ((x) % 2 == 0)  
#define PRINT_NUMBERS(n) do { \  
    int i; \  
    for (i = 0; i < (n); ++i) { \  
        if (IS_EVEN(i)) { \  //这里使用了宏的嵌套
            printf("%d is even\n", i); \  
        } else { \  
            printf("%d is odd\n", i); \  
        } \  
    } \  
} while (0)

在这个例子中,IS_EVEN 是一个简单的宏,用于检查一个数是否是偶数。

PRINT_NUMBERS 是一个更复杂的宏,它使用 do { ... } while (0) 结构来模拟一个语句块,并在其中使用了一个 for 循环来打印从0到 n-1 的所有整数,同时标记每个数是偶数还是奇数。

3. 预定义宏

预定义宏是C语言中标准编译器预先定义的宏(官方定义了,不需要我们定义,我们需要时直接使用这些宏就行)

  • 预处理日期和时间
    • __DATE__ 、__TIME__
  • 函数名 和 当前行
    • __FUNCTION__、 ___LINE__
  • 文件名
    • __FILE__

例如以下应用

#include <stdio.h>

int test01()
{
    //............
    int a;
    printf("文件名:%s 当前函数:%s 当前行:%d a:%d\n",__FILE__,__FUNCTION__,__LINE__,a);

    //当前时间和日期
    printf("%s %s\n",__DATE__,__TIME__);
}

int main()
{
    test01();
    return 0;
}

4. #符号和##符号

使用 # 符号 ,把一个宏参数变成对应的字符串

如果对变量使用# ,会把变量名作为字符串打印出来

#include <stdio.h>
#define CHANGE(STR)  #STR  // #将文本STR转换成字符串
int main()
{
    char *str = "hello" "world" "ikun";
    printf("str = %s\n", str);
    printf("CHANGE(STR)= %s\n", CHANGE(ikun i love you));    // #ikun i love you 转成"ikun i love you"
    return 0;
}

##可以把位于它两边的符号合成一个符号。它允许宏定义从分离的文本片段创建标识符

链接产生在预处理时,必须链接产生一个合法的标识符

#include <stdio.h>
#define STRCAT(str1, str2)   str1##str2  // 将str1和str2标识连在一起    
int main()
{
    int sum5 = 100;
    printf("sum5 =  %d\n", STRCAT(sum, 5));
    return 0;
}

5.条件编译

5.1 概念

能够根据不同情况编译不同代码,产生不同目标文件的机制,称之为条件编译。说白了,条件编译的意思就是有条件地编译某些我们指定的代码,而不一定编译文件中所有的代码。

5.2 #if指令

  • 检查内容为表达式的值是否为真 非0则编译
  • 关心宏的具体值
  • 必须跟常量表达式
#if 整型常量表达式1   //可以使用宏的形式作为常量表达式
    程序段1   // 如果常量表达式为真(非0),则编译此代码
#elif 整型常量表达式2
    程序段2
#elif 整型常量表达式3
    程序段3
#else          // 如果常量表达式为假(0),则编译此代码
    程序段4
#endif

5.3 #ifdef指令

  • 检查内容时宏是否被定义(与值无关
  • 定义则编译
  • 预定义宏  ifdef 一般根据系统提供的宏判断出当前程序运行的平台, 已经开发模式 DEBUG(调试),产品模式
#ifdef  宏名  //如果程序中存在这个宏,执行下面的程序段1
	程序段1
#else         //如果程序中不存在这个宏,执行程序段2
	程序段2
#endif
#ifdef _WIN32
    // Windows 特定代码
    system("cls");
#elif defined(__linux__)
    // Linux 特定代码
    system("clear");
#elif defined(__APPLE__)
    // macOS 特定代码
    system("clear");
#endif

#ifdef MACRO ≡ #if defined(MACRO),但后者可以在 #if 表达式中组合使用,更灵活。

5.5  C/C++ 中的头文件保护

  • 主要作用:
    • 防止重复包含
      • 当同一个头文件被多个源文件包含,或者被嵌套包含时,这个保护机制可以防止:
    • 重复定义(编译错误)
    • 重复声明
    • 宏重复定义
#ifndef _TOOL_H_     // 第一次:如果 _TOOL_H_ 未定义
#define _TOOL_H_     // 第一次:定义 _TOOL_H_
    // 头文件内容(只会在第一次包含时生效)
#endif               // 结束条件编译

5.6 #if与#ifdef间的区别

特性#if#ifdef#ifndef
检查内容表达式值是否为真宏是否已定义宏是否已定义
语法#if 表达式#ifdef 宏名#ifndef 宏名
等价形式-#if defined(宏名)-
未定义宏当作 0 处理条件为假条件为真
空值宏当作 0 处理条件为真条件为假
灵活性高(可组合、运算)低(只能检查存在)低(只能检查存在)
常见用途版本控制、级别判断头文件保护、平台检测头文件保护、平台检测

编译过程

编译过程分为以下四个阶段

  • 预处理
    • 处理预处理语句(#开头的语句),删除注释头文件展开宏替换...
    • gcc hello.c -o hello.i -E
  • 编译:
    • C语言程序转化为汇编语言
    • gcc hello.c -o hello.s -S
  • 汇编
    • 程序代码转化为二进制代码//大型项目中可以节省编译时间
    • gcc hello.c -o hello.o -c
  • 链接
    • 将所有二进制代码合并起来,根据应用规则生成一个专门针对某个平台执行的应用程序镜像
    • gcc hello.c -o hello

此作者没有提供个人介绍。
最后更新于 2026-01-21