快速学C语言——第16章:预处理

第16章:预处理

​ 在C语言程序的编译过程中,有一个特殊的阶段++发生在实际编译之前++ ,这就是预处理阶段

​ 预处理器是一个独立的程序,它处理源代码中++以#开头++的特殊指令,为后续的编译工作做准备。

​ 理解预处理是掌握C语言编译流程的重要一环,它能帮助我们编写更灵活、更可移植的代码。


16.1 预处理概述

编译流程

​ ①预处理:处理所有预处理指令,生成纯粹的C代码。

​ ②编译:将C代码翻译成汇编代码。

​ ③汇编:将汇编代码转换成机器代码(目标文件)。

​ ④链接:将多个目标文件和库文件连接成可执行文件。

​ 预处理器不关心C语言的语法规则,它只是进行简单的文本替换和处理工作。


16.2 宏定义:#define

​ #define是最常用的预处理指令,用于创建宏。

16.2.1 对象式宏

​ ++对象式宏++ 用于定义常量简单的文本替换

c 复制代码
#include <stdio.h>

// 定义常量宏。
#define PI 3.14159
#define MAX_SIZE 100
#define PROGRAM_NAME "我的程序"

// 定义字符串宏。
#define WELCOME_MESSAGE "欢迎使用C语言"

int main() {
    double radius = 5.0;
    double area = PI * radius * radius;

    printf("%s\n", PROGRAM_NAME);
    printf("%s\n", WELCOME_MESSAGE);
    printf("半径为%.2f的圆面积:%.2f\n", radius, area);
    printf("最大尺寸:%d\n", MAX_SIZE);

    return 0;
}

运行结果:

bash 复制代码
我的程序
欢迎使用C语言
半径为5.00的圆面积:78.54
最大尺寸:100

--------------------------------
Process exited after 0.05483 seconds with return value 0
请按任意键继续. . .

#define 的格式是 #define A B 预处理器会把代码中出现的A全部替换成B。上述例子中printf("%s\n", PROGRAM_NAME); 直接输出的 "我的程序"PROGRAM_NAME"我的程序" 等价。

16.2.2 函数式宏

​ 函数式宏可以接受参数,看起来像函数调用,但实际上是文本替换。

c 复制代码
#include <stdio.h>

// 函数式宏定义。
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define PRINT_INT(n) printf(#n " = %d\n", n)

int main() {
    int x = 5, y = 10;

    printf("%d的平方是:%d\n", x, SQUARE(x));
    printf("%d和%d中较大的数是:%d\n", x, y, MAX(x, y));
    printf("%d和%d中较小的数是:%d\n", x, y, MIN(x, y));

    PRINT_INT(x);  // 输出:x = 5。
    PRINT_INT(y);  // 输出:y = 10。

    return 0;
}

运行结果:

bash 复制代码
5的平方是:25
5和10中较大的数是:10
5和10中较小的数是:5
x = 5
y = 10

--------------------------------
Process exited after 0.05414 seconds with return value 0
请按任意键继续. . .

​ 重要提示:在定义函数式宏时,参数和整个表达式都应该用括号包围,以避免运算符优先级问题。


16.3 文件包含:#include

​ #include用于将其他文件的内容插入到当前文件中

c 复制代码
// 系统头文件 - 使用尖括号。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 自定义头文件 - 使用双引号。
#include "my_functions.h"
#include "config.h"

创建和使用自定义头文件

my_functions.h

c 复制代码
#ifndef MY_FUNCTIONS_H  // 头文件保护,防止重复包含。
#define MY_FUNCTIONS_H

// 函数声明。
int add(int a, int b);
int multiply(int a, int b);
void print_message(const char* message);

// 常量定义。
#define MAX_VALUE 1000
#define MIN_VALUE 0

#endif

my_functions.c

c 复制代码
#include "my_functions.h"  //导入自定义头文件。
#include <stdio.h>

// 函数实现。
int add(int a, int b) {
    return a + b;
}

int multiply(int a, int b) {
    return a * b;
}

void print_message(const char* message) {
    printf("消息:%s\n", message);
}

main.c

c 复制代码
#include <stdio.h>
#include "my_functions.h"

int main() {
    int result1 = add(10, 20);
    int result2 = multiply(5, 6);

    printf("10 + 20 = %d\n", result1);
    printf("5 * 6 = %d\n", result2);
    print_message("Hello from header file!");

    printf("最大值:%d\n", MAX_VALUE);

    return 0;
}

16.4 条件编译

​ 条件编译允许我们根据不同的条件编译不同的代码段

16.4.1 #if, #elif, #else, #endif
c 复制代码
#include <stdio.h>

#define DEBUG_LEVEL 2
#define VERSION 3

int main() {
    // 根据DEBUG_LEVEL编译不同的调试代码。
#if DEBUG_LEVEL >= 2
    printf("[DEBUG] 程序开始执行\n");
#endif

#if VERSION == 1
    printf("版本1的功能\n");
#elif VERSION == 2
    printf("版本2的功能\n");
#elif VERSION == 3
    printf("版本3的功能\n");
#else
    printf("未知版本\n");
#endif

#if DEBUG_LEVEL >= 1
    printf("[DEBUG] 程序执行完成\n");
#endif

    return 0;
}

运行结果:

bash 复制代码
[DEBUG] 程序开始执行
版本3的功能
[DEBUG] 程序执行完成

--------------------------------
Process exited after 0.01537 seconds with return value 0
请按任意键继续. . .
16.4.2 #ifdef和#ifndef
c 复制代码
#include <stdio.h>

// 在编译时定义:gcc -DDEBUG program.c 
// #define DEBUG

int main() {
#ifdef DEBUG
    printf("[调试模式] 开始执行调试代码\n");
    // 调试专用的代码。
    printf("[调试模式] 变量检查完成\n");
#else
    printf("正常执行模式\n");
#endif

#ifndef RELEASE
    printf("这不是发布版本\n");
#endif

    return 0;
}

运行结果:

bash 复制代码
正常执行模式
这不是发布版本

--------------------------------
Process exited after 0.06809 seconds with return value 0
请按任意键继续. . .

​ 如果定义了DEBUG, 执行 调试专用的代码。

16.4.3 实际应用:跨平台代码
c 复制代码
#include <stdio.h>

// 根据平台定义不同的功能。
#ifdef _WIN32
    #define PLATFORM "Windows"
    #define CLEAR_SCREEN "cls"
#elif defined(__linux__)
    #define PLATFORM "Linux"
    #define CLEAR_SCREEN "clear"
#elif defined(__APPLE__)
    #define PLATFORM "macOS"
    #define CLEAR_SCREEN "clear"
#else
    #define PLATFORM "未知平台"
    #define CLEAR_SCREEN "echo '清屏命令未定义'"
#endif

int main() {
    printf("当前运行平台:%s\n", PLATFORM);

    // 在实际项目中,可以使用 system(CLEAR_SCREEN) 来清屏
    printf("清屏命令:%s\n", CLEAR_SCREEN);

    return 0;
}

16.5 其他预处理指令

16.5.1 #undef - 取消宏定义
c 复制代码
#include <stdio.h>

#define TEMP_VALUE 100

int main() {
    printf("TEMP_VALUE = %d\n", TEMP_VALUE);

#undef TEMP_VALUE  // 取消宏定义。

    // printf("TEMP_VALUE = %d\n", TEMP_VALUE);  // 错误!TEMP_VALUE未定义。

#define TEMP_VALUE 200  // 重新定义。
    printf("重新定义后 TEMP_VALUE = %d\n", TEMP_VALUE);

    return 0;
}
16.5.2 #error - 生成编译错误
c 复制代码
#include <stdio.h>

// 检查必要的定义。
#ifndef REQUIRED_CONFIG
    #error "REQUIRED_CONFIG 必须被定义!"
#endif

// 检查编译器版本。
#if __STDC_VERSION__ < 201112L
    #error "需要C11或更高版本的编译器"
#endif

int main() {
    printf("程序正常执行\n");
    return 0;
}
16.5.3 #pragma - 编译器特定指令
c 复制代码
#include <stdio.h>

// 禁止特定警告(编译器相关)。
#pragma GCC diagnostic ignored "-Wunused-variable"

// 打包结构体,节省内存。
#pragma pack(push, 1)  // 按1字节对齐
struct PackedData {
    char a;
    int b;
    char c;
};
#pragma pack(pop)      // 恢复默认对齐

// 默认对齐的结构体。
struct NormalData {
    char a;
    int b;
    char c;
};

int main() {
    printf("打包结构体大小:%zu字节\n", sizeof(struct PackedData));
    printf("普通结构体大小:%zu字节\n", sizeof(struct NormalData));

    return 0;
}

16.6 预定义宏

C语言预定义了一些有用的宏,它们提供关于编译环境的信息。

c 复制代码
#include <stdio.h>

int main() {
    printf("文件名:%s\n", __FILE__);      // 当前文件名
    printf("行号:%d\n", __LINE__);        // 当前行号
    printf("编译日期:%s\n", __DATE__);    // 编译日期
    printf("编译时间:%s\n", __TIME__);    // 编译时间

    // C标准版本检测
    #ifdef __STDC_VERSION__
        printf("C标准版本:%ld\n", __STDC_VERSION__);
    #endif

    #ifdef __cplusplus
        printf("这是C++代码\n");
    #else
        printf("这是C代码\n");
    #endif

    return 0;
}

16.7 预处理运算符

16.7.1 #运算符:字符串化

#运算符将宏参数转换为字符串常量。

c 复制代码
#include <stdio.h>

#define STRINGIFY(x) #x
#define PRINT_VAR(var) printf(#var " = %d\n", var)

int main() {
    int my_variable = 42;

    printf(STRINGIFY(Hello World!\n));  // 输出:Hello World!
    PRINT_VAR(my_variable);             // 输出:my_variable = 42

    // 多级字符串化
    #define LEVEL1(x) #x
    #define LEVEL2(x) LEVEL1(x)

    int value = 100;
    printf(LEVEL1(value));   // 输出:value
    printf(LEVEL2(value));   // 输出:100

    return 0;
}
16.7.2 ##运算符:标记连接

##运算符用于连接两个标记。

c 复制代码
#include <stdio.h>

#define CONCAT(a, b) a##b
#define MAKE_VARIABLE(name, number) name##number

int main() {
    int xy = 100;
    printf("CONCAT(x, y)的值:%d\n", CONCAT(x, y));  // 输出:100

    int var1 = 10, var2 = 20, var3 = 30;
    int MAKE_VARIABLE(var, 1) = 100;  // 创建变量var1
    int MAKE_VARIABLE(var, 2) = 200;  // 创建变量var2

    printf("var1 = %d\n", var1);  // 输出:100(不是10!)
    printf("var2 = %d\n", var2);  // 输出:200(不是20!)

    return 0;
}
相关推荐
matlabgoodboy1 小时前
软件开发定制小程序APP帮代做java代码代编写C语言设计python编程
java·c语言·小程序
yuan199971 小时前
基于 C# 实现的 Omron HostLink (FINS) 协议 PLC 通讯
开发语言·c#
qq_422828622 小时前
android图形学之SurfaceControl和Surface的关系 五
android·开发语言·python
handler013 小时前
UDP协议与网络通信知识点
c语言·网络·c++·笔记·网络协议·udp
如竟没有火炬3 小时前
用队列实现栈
开发语言·数据结构·python·算法·leetcode·深度优先
折哥的程序人生 · 物流技术专研3 小时前
《Java 100 天进阶之路》第17篇:Java常用包装类与自动装箱拆箱深入
java·开发语言·后端·面试
C+++Python3 小时前
C 语言 动态内存分配:malloc /calloc/realloc /free
c语言·开发语言
cen__y3 小时前
Linux11(网络编程)
linux·运维·服务器·c语言·网络·网络协议·tcp/ip
水木流年追梦4 小时前
大模型入门-应用篇3-Agent智能体
开发语言·python·算法·leetcode·正则表达式