C语言学习笔记(十五):预处理

1. 预处理概述

1.1 什么是预处理?

预处理是编译过程的第一个阶段,在正式编译之前对源代码进行处理。预处理器读取源代码,执行以 # 开头的指令,生成纯净的C代码供编译器处理。

源文件(.c) → 预处理器 → 预处理后文件(.i) → 编译器 → 目标文件(.o)

处理 #include

#define #if等

1.2 预处理器的主要功能

功能 指令 说明

文件包含 #include 插入头文件内容

宏定义 #define 定义符号常量或宏函数

条件编译 #if #ifdef #ifndef 根据条件编译代码

错误处理 #error 产生编译错误

行号控制 #line 修改行号和文件名

编译器指令 #pragma 编译器特定功能

2. 文件包含指令

2.1 #include 的两种形式

复制代码
// 形式1:尖括号 - 搜索系统头文件路径

#include <stdio.h>

#include <stdlib.h>



// 形式2:双引号 - 先搜索当前目录,再搜索系统路径

#include "myheader.h"

#include "utils/config.h"



// 搜索路径示例(Linux)

// 系统路径:/usr/include, /usr/local/include

// 可通过 -I 选项添加自定义路径:gcc -I./include program.c

2.2 防止头文件重复包含

复制代码
// 方法1:使用条件编译(推荐)

#ifndef MYHEADER_H

#define MYHEADER_H



// 头文件内容

void func(void);

#define VALUE 100



#endif



// 方法2:使用 #pragma once(非标准但广泛支持)

#pragma once



// 头文件内容

void func(void);

#define VALUE 100

2.3 头文件设计原则

复制代码
// 良好的头文件结构

#ifndef LIBRARY_H

#define LIBRARY_H



// 1. 包含必要的其他头文件

#include <stdio.h>

#include <stddef.h>



// 2. 宏定义

#define LIB_VERSION "1.0.0"

#define MAX_BUFFER 1024



// 3. 类型定义

typedef struct {

    int id;

    char name[50];

} Library_t;



// 4. 函数声明

void library_init(void);

void library_cleanup(void);



// 5. 内联函数(可选)

static inline int max(int a, int b) {

    return a > b ? a : b;

}



#endif

3. 宏定义

3.1 简单宏(对象宏)

// 定义常量

#define PI 3.14159

#define MAX_SIZE 100

#define FILE_PATH "/usr/local/config"

// 定义字符常量

#define NEWLINE '\n'

#define TAB '\t'

// 定义字符串

#define APP_NAME "MyApplication"

#define VERSION "1.0.0"

// 使用示例

double area = PI * radius * radius;

int arr[MAX_SIZE];

FILE *fp = fopen(FILE_PATH, "r");

3.2 带参数的宏(函数宏)

复制代码
// 基本用法

#define SQUARE(x) ((x) * (x))

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

#define MIN(a, b) ((a) < (b) ? (a) : (b))



// 使用示例

int result = SQUARE(5); // 展开为 ((5) * (5))

int bigger = MAX(10, 20); // 展开为 ((10) > (20) ? (10) : (20))



// 多语句宏

#define LOG_ERROR(msg) do { \

    fprintf(stderr, "Error: %s at %s:%d\n", msg, __FILE__, __LINE__); \

    exit(1); \

} while(0)



// 可变参数宏(C99)

#define DEBUG_PRINT(fmt, ...) \

    printf("[DEBUG] " fmt, __VA_ARGS__)



#define ERROR_PRINT(fmt, ...) \

    fprintf(stderr, "[ERROR] %s:%d: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__)



// 使用

DEBUG_PRINT("x = %d, y = %d\n", x, y);

ERROR_PRINT("Invalid value: %d", value);

3.3 宏定义的关键技巧

复制代码
// ⚠️ 重要:宏参数要加括号

#define BAD_SQUARE(x) x * x // 错误!

// SQUARE(1+2) 展开为 1+2*1+2 = 5,期望9



#define GOOD_SQUARE(x) ((x) * (x)) // 正确



// 宏的续行符 \

#define LONG_MACRO(x, y) \

    do { \

        int temp = (x) + (y); \

        printf("Result: %d\n", temp); \

    } while(0)



// 字符串化操作符 #

#define STR(x) #x

#define STRING(x) STR(x)



printf("%s\n", STR(Hello)); // 输出: Hello

printf("%s\n", STR(MAX_SIZE)); // 输出: MAX_SIZE

printf("%s\n", STRING(MAX_SIZE)); // 输出: 100(如果MAX_SIZE=100)



// 连接操作符 ##

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

#define VAR(name, num) name##num



int var1 = 10, var2 = 20;

printf("%d\n", CONCAT(var, 1)); // 输出: 10

printf("%d\n", VAR(var, 2)); // 输出: 20

3.4 预定义宏

复制代码
#include <stdio.h>



int main() {

    // 标准预定义宏

    printf("文件:%s\n", __FILE__);

    printf("行号:%d\n", __LINE__);

    printf("函数:%s\n", __func__); // C99

    printf("编译日期:%s\n", __DATE__);

    printf("编译时间:%s\n", __TIME__);

    

    // C99 新增

    printf("是否严格遵循标准:%d\n", __STDC__);

    printf("C版本:%ld\n", __STDC_VERSION__);

    

    // 编译器特定(GCC)

    printf("编译器版本:%s\n", __VERSION__);

    

    return 0;

}

4. 条件编译

4.1 #if、#elif、#else、#endif

复制代码
// 基本条件编译

#define DEBUG_LEVEL 2



#if DEBUG_LEVEL >= 3

    #define DEBUG_PRINT(fmt, ...) \

        printf("[DEBUG3] " fmt, __VA_ARGS__)

#elif DEBUG_LEVEL >= 2

    #define DEBUG_PRINT(fmt, ...) \

        printf("[DEBUG2] " fmt, __VA_ARGS__)

#elif DEBUG_LEVEL >= 1

    #define DEBUG_PRINT(fmt, ...) \

        printf("[DEBUG1] " fmt, __VA_ARGS__)

#else

    #define DEBUG_PRINT(fmt, ...)

#endif



// 平台适配

#if defined(_WIN32) || defined(_WIN64)

    #define PLATFORM "Windows"

    #define PATH_SEP '\\'

#elif defined(__linux__)

    #define PLATFORM "Linux"

    #define PATH_SEP '/'

#elif defined(__APPLE__)

    #define PLATFORM "macOS"

    #define PATH_SEP '/'

#else

    #define PLATFORM "Unknown"

    #define PATH_SEP '/'

#endif

4.2 #ifdef、#ifndef

复制代码
// 头文件保护

#ifndef MYHEADER_H

#define MYHEADER_H

// 头文件内容

#endif



// 调试开关

#define DEBUG



#ifdef DEBUG

    #define DEBUG_MSG(msg) printf("[DEBUG] %s\n", msg)

#else

    #define DEBUG_MSG(msg)

#endif



// 特性检测

#ifndef __cplusplus

    // C语言特定代码

    typedef struct {

        int x;

        int y;

    } Point;

#endif



// 防止重复定义

#ifndef MAX

    #define MAX 100

#endif

4.3 defined 操作符

复制代码
// 组合条件

#if defined(DEBUG) && defined(VERBOSE)

    #define LOG(msg) printf("[LOG] %s\n", msg)

#elif defined(DEBUG)

    #define LOG(msg) printf("[DBG] %s\n", msg)

#else

    #define LOG(msg)

#endif



// 检查多个宏

#if defined(USE_OPENGL) || defined(USE_VULKAN)

    #define USE_GRAPHICS

#endif



// 复杂条件

#if defined(__GNUC__) && (__GNUC__ >= 4)

    #define GCC_VERSION "4.0+"

#endif

5. 条件编译的应用场景

5.1 调试代码控制

复制代码
// debug.h

#ifndef DEBUG_H

#define DEBUG_H



#include <stdio.h>



// 调试级别

#define DEBUG_NONE 0

#define DEBUG_ERROR 1

#define DEBUG_WARN 2

#define DEBUG_INFO 3

#define DEBUG_TRACE 4



#ifndef DEBUG_LEVEL

    #define DEBUG_LEVEL DEBUG_NONE

#endif



// 调试宏

#if DEBUG_LEVEL >= DEBUG_ERROR

    #define DEBUG_ERROR_PRINT(fmt, ...) \

        fprintf(stderr, "[ERROR] %s:%d: " fmt "\n", \

                __FILE__, __LINE__, __VA_ARGS__)

#else

    #define DEBUG_ERROR_PRINT(fmt, ...)

#endif



#if DEBUG_LEVEL >= DEBUG_WARN

    #define DEBUG_WARN_PRINT(fmt, ...) \

        fprintf(stderr, "[WARN] %s:%d: " fmt "\n", \

                __FILE__, __LINE__, __VA_ARGS__)

#else

    #define DEBUG_WARN_PRINT(fmt, ...)

#endif



#if DEBUG_LEVEL >= DEBUG_INFO

    #define DEBUG_INFO_PRINT(fmt, ...) \

        printf("[INFO] " fmt "\n", __VA_ARGS__)

#else

    #define DEBUG_INFO_PRINT(fmt, ...)

#endif



// 断言宏

#ifdef DEBUG

    #define ASSERT(cond) \

        do { \

            if (!(cond)) { \

                fprintf(stderr, "Assertion failed: %s at %s:%d\n", \

                        #cond, __FILE__, __LINE__); \

                abort(); \

            } \

        } while(0)

#else

    #define ASSERT(cond)

#endif



#endif

5.2 跨平台开发

复制代码
// platform.h

#ifndef PLATFORM_H

#define PLATFORM_H



// 检测操作系统

#if defined(_WIN32) || defined(_WIN64)

    #define OS_WINDOWS 1

    #define OS_NAME "Windows"

    #include <windows.h>

#elif defined(__linux__)

    #define OS_LINUX 1

    #define OS_NAME "Linux"

    #include <unistd.h>

    #include <pthread.h>

#elif defined(__APPLE__)

    #define OS_MACOS 1

    #define OS_NAME "macOS"

    #include <unistd.h>

#else

    #define OS_UNKNOWN 1

    #define OS_NAME "Unknown"

#endif



// 编译器检测

#if defined(__GNUC__)

    #define COMPILER_GCC 1

    #define COMPILER_NAME "GCC"

    #define COMPILER_VERSION __VERSION__

#elif defined(_MSC_VER)

    #define COMPILER_MSVC 1

    #define COMPILER_NAME "MSVC"

    #define COMPILER_VERSION _MSC_VER

#endif



// 导出符号(动态库)

#if defined(_WIN32)

    #ifdef BUILDING_DLL

        #define DLL_EXPORT __declspec(dllexport)

    #else

        #define DLL_EXPORT __declspec(dllimport)

    #endif

#else

    #define DLL_EXPORT __attribute__((visibility("default")))

#endif



// 线程本地存储

#if defined(_WIN32)

    #define THREAD_LOCAL __declspec(thread)

#elif defined(__GNUC__)

    #define THREAD_LOCAL __thread

#endif



// 内联函数

#ifdef _MSC_VER

    #define INLINE __forceinline

#else

    #define INLINE inline __attribute__((always_inline))

#endif



#endif

5.3 功能特性开关

复制代码
// features.h

#ifndef FEATURES_H

#define FEATURES_H



// 功能开关

#define ENABLE_CACHE 1

#define ENABLE_LOGGING 1

#define ENABLE_SSL 0



// 根据开关编译不同代码

#if ENABLE_CACHE

    typedef struct {

        void *data;

        size_t size;

    } Cache_t;

    

    void cache_init(void);

    void cache_set(const char *key, void *data);

    void *cache_get(const char *key);

#else

    #define cache_init()

    #define cache_set(key, data)

    #define cache_get(key) NULL

#endif



// 版本控制

#define VERSION_MAJOR 2

#define VERSION_MINOR 1

#define VERSION_PATCH 0



#if VERSION_MAJOR >= 2

    #define USE_NEW_API 1

#endif



#endif

6. #error 和 #line

6.1 #error 产生编译错误

// 强制要求定义某个宏

#ifndef CONFIG_FILE

#error "CONFIG_FILE must be defined"

#endif

// 版本检查

#if STDC_VERSION < 199901L

#error "C99 or later is required"

#endif

// 平台检查

#if defined(_WIN32) && !defined(_WIN64)

#error "32-bit Windows is not supported"

#endif

// 配置检查

#if MAX_BUFFER_SIZE < 1024

#error "MAX_BUFFER_SIZE must be at least 1024"

#endif

6.2 #line 修改行号

// 重置行号和文件名

#line 100 "custom_file.c"

// 后续代码行号从100开始

int main() {

// 这里的行号是101

printf("Line: %d\n", LINE); // 输出 101

return 0;

}

7. #pragma 指令

7.1 常见 #pragma 用法

// 1. 防止头文件重复包含(MSVC)

#pragma once

// 2. 关闭特定警告(MSVC)

#pragma warning(disable: 4996) // 禁用不安全函数警告

// 3. 设置结构体对齐

#pragma pack(push, 1) // 1字节对齐

typedef struct {

char c;

int i;

} PackedStruct;

#pragma pack(pop)

// 4. 消息提示

#pragma message("Compiling main module...")

// 5. 链接库(MSVC)

#pragma comment(lib, "winmm.lib")

// 6. 优化设置(GCC)

#pragma GCC optimize("O3")

#pragma GCC optimize("unroll-loops")

7.2 编译器检测与 #pragma 兼容

// 跨平台的 #pragma 使用

#if defined(_MSC_VER)

#pragma warning(disable: 4996)

#define ALIGN(n) __declspec(align(n))

#elif defined(GNUC)

#define ALIGN(n) attribute((aligned(n)))

#else

#define ALIGN(n)

#endif

// 使用示例

ALIGN(16) int vector[4];

8. 宏的高级应用

8.1 日志系统

复制代码
// logger.h

#ifndef LOGGER_H

#define LOGGER_H



#include <stdio.h>

#include <time.h>



// 日志级别

#define LOG_LEVEL_DEBUG 0

#define LOG_LEVEL_INFO 1

#define LOG_LEVEL_WARN 2

#define LOG_LEVEL_ERROR 3



#ifndef LOG_LEVEL

    #define LOG_LEVEL LOG_LEVEL_INFO

#endif



// 获取时间戳

#define TIMESTAMP() \

    do { \

        time_t t = time(NULL); \

        struct tm *tm = localtime(&t); \

        printf("[%04d-%02d-%02d %02d:%02d:%02d] ", \

               tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, \

               tm->tm_hour, tm->tm_min, tm->tm_sec); \

    } while(0)



// 日志宏

#if LOG_LEVEL <= LOG_LEVEL_DEBUG

    #define LOG_DEBUG(fmt, ...) \

        do { \

            printf("[DEBUG] %s:%d: ", __FILE__, __LINE__); \

            printf(fmt, __VA_ARGS__); \

            printf("\n"); \

        } while(0)

#else

    #define LOG_DEBUG(fmt, ...)

#endif



#if LOG_LEVEL <= LOG_LEVEL_INFO

    #define LOG_INFO(fmt, ...) \

        do { \

            TIMESTAMP(); \

            printf("[INFO] " fmt "\n", __VA_ARGS__); \

        } while(0)

#else

    #define LOG_INFO(fmt, ...)

#endif



#if LOG_LEVEL <= LOG_LEVEL_WARN

    #define LOG_WARN(fmt, ...) \

        do { \

            fprintf(stderr, "[WARN] %s:%d: " fmt "\n", \

                    __FILE__, __LINE__, __VA_ARGS__); \

        } while(0)

#else

    #define LOG_WARN(fmt, ...)

#endif



#if LOG_LEVEL <= LOG_LEVEL_ERROR

    #define LOG_ERROR(fmt, ...) \

        do { \

            fprintf(stderr, "[ERROR] %s:%d: " fmt "\n", \

                    __FILE__, __LINE__, __VA_ARGS__); \

        } while(0)

#else

    #define LOG_ERROR(fmt, ...)

#endif



#endif

8.2 通用数据结构宏

复制代码
// vector.h - 动态数组模板

#define VECTOR_DECLARE(type, name) \

    typedef struct { \

        type *data; \

        size_t size; \

        size_t capacity; \

    } name##_t; \

    \

    name##_t* name##_create(void); \

    void name##_destroy(name##_t *vec); \

    int name##_push(name##_t *vec, type value); \

    type name##_get(name##_t *vec, size_t index);



#define VECTOR_IMPLEMENT(type, name) \

    name##_t* name##_create(void) { \

        name##_t *vec = malloc(sizeof(name##_t)); \

        if (!vec) return NULL; \

        vec->capacity = 4; \

        vec->size = 0; \

        vec->data = malloc(vec->capacity * sizeof(type)); \

        return vec; \

    } \

    \

    void name##_destroy(name##_t *vec) { \

        if (vec) { \

            free(vec->data); \

            free(vec); \

        } \

    } \

    \

    int name##_push(name##_t *vec, type value) { \

        if (vec->size >= vec->capacity) { \

            vec->capacity *= 2; \

            type *new_data = realloc(vec->data, vec->capacity * sizeof(type)); \

            if (!new_data) return 0; \

            vec->data = new_data; \

        } \

        vec->data[vec->size++] = value; \

        return 1; \

    } \

    \

    type name##_get(name##_t *vec, size_t index) { \

        return vec->data[index]; \

    }



// 使用示例

VECTOR_DECLARE(int, IntVector)

VECTOR_IMPLEMENT(int, IntVector)



int main() {

    IntVector_t *vec = IntVector_create();

    IntVector_push(vec, 10);

    IntVector_push(vec, 20);

    printf("%d\n", IntVector_get(vec, 0));

    IntVector_destroy(vec);

    return 0;

}

8.3 错误处理宏

复制代码
// error.h

#ifndef ERROR_H

#define ERROR_H



#include <stdio.h>

#include <stdlib.h>



// 错误码

typedef enum {

    ERROR_SUCCESS = 0,

    ERROR_NULL_PTR,

    ERROR_INVALID_PARAM,

    ERROR_MEMORY,

    ERROR_FILE_IO,

    ERROR_UNKNOWN

} ErrorCode;



// 错误处理宏

#define CHECK_NULL(ptr) \

    do { \

        if ((ptr) == NULL) { \

            fprintf(stderr, "NULL pointer at %s:%d\n", __FILE__, __LINE__); \

            return ERROR_NULL_PTR; \

        } \

    } while(0)



#define CHECK_COND(cond, error_code) \

    do { \

        if (!(cond)) { \

            fprintf(stderr, "Condition failed: %s at %s:%d\n", \

                    #cond, __FILE__, __LINE__); \

            return (error_code); \

        } \

    } while(0)



#define TRY(expr) \

    do { \

        int _err = (expr); \

        if (_err != ERROR_SUCCESS) { \

            fprintf(stderr, "Error %d at %s:%d\n", _err, __FILE__, __LINE__); \

            return _err; \

        } \

    } while(0)



#define RETURN_IF_ERROR(expr) \

    do { \

        int _err = (expr); \

        if (_err != ERROR_SUCCESS) return _err; \

    } while(0)



#define ASSERT(cond) \

    do { \

        if (!(cond)) { \

            fprintf(stderr, "Assertion failed: %s at %s:%d\n", \

                    #cond, __FILE__, __LINE__); \

            abort(); \

        } \

    } while(0)



#endif

9. 常见陷阱与最佳实践

9.1 宏的常见陷阱

// 陷阱1:缺少括号

#define BAD_SQUARE(x) x * x

int result = BAD_SQUARE(1+2); // 1+2*1+2 = 5,期望9

// 陷阱2:多次求值

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

int x = 1, y = 2;

int max = MAX(x++, y++); // x++ 被求值两次!

// 实际:((x++) > (y++) ? (x++) : (y++)) → x=2, y=4

// 陷阱3:分号问题

#define LOG(msg) printf("%s\n", msg)

if (condition)

LOG("message"); // 正常

else

LOG("error"); // 如果宏定义末尾有分号,这里会出错

// 陷阱4:宏名与函数名冲突

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

// 可能覆盖标准库的 max 函数

// 正确做法:宏名使用大写

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

9.2 最佳实践总结

// ✅ 1. 宏名使用大写字母

#define BUFFER_SIZE 1024

// ✅ 2. 宏参数和整个表达式加括号

#define SQUARE(x) ((x) * (x))

// ✅ 3. 多语句宏使用 do-while(0) 包装

#define SWAP(a, b) \

do { \

int temp = (a); \

(a) = (b); \

(b) = temp; \

} while(0)

// ✅ 4. 使用 #undef 清理不再使用的宏

#define TEMP_VALUE 100

// ... 使用后

#undef TEMP_VALUE

// ✅ 5. 调试宏只在开发时定义

#ifdef DEBUG

#define ASSERT(cond) /* 断言实现 */

#else

#define ASSERT(cond)

#endif

// ✅ 6. 避免重复定义,使用 #ifndef 保护

#ifndef MAX_BUFFER

#define MAX_BUFFER 4096

#endif

10. 总结

10.1 预处理指令速查表

指令 功能 示例

#include 包含文件 #include <stdio.h>

#define 定义宏 #define PI 3.14

#undef 取消定义 #undef PI

#if 条件判断 #if DEBUG

#ifdef 如果定义 #ifdef _WIN32

#ifndef 如果未定义 #ifndef HEADER_H

#else 否则 #else

#elif 否则如果 #elif defined(linux)

#endif 结束条件 #endif

#error 产生错误 #error "Not supported"

#line 修改行号 #line 100 "file.c"

#pragma 编译器指令 #pragma once

10.2 核心要点

  1. 预处理在编译之前执行,不进行语法检查

  2. 宏是文本替换,理解替换规则避免陷阱

  3. 条件编译用于调试、跨平台、功能开关

  4. 头文件保护防止重复包含

  5. 合理使用宏可以提高代码可维护性,但过度使用会降低可读性

记住:预处理是C语言的强大特性,但要谨慎使用。清晰的代码结构往往比精巧的宏更重要!

相关推荐
不会聊天真君6471 小时前
基础语法·中(golang笔记第二期)
开发语言·笔记·golang
m0_569881472 小时前
C++中的适配器模式变体
开发语言·c++·算法
态态态2 小时前
平板PDF充足笔记空间的最优解
笔记·pdf
NAGNIP2 小时前
面试官:正则化都有哪些经典的方法?
算法·面试
Theodore_10222 小时前
深度学习(12)正则化线性回归中的偏差与方差调试
人工智能·深度学习·算法·机器学习·线性回归
louiseailife2 小时前
企业自动化升级:RPA与AI智能体融合趋势
经验分享
是娇娇公主~2 小时前
C++ 多态机制与虚函数实现原理
c语言·c++
m0_569881472 小时前
跨语言调用C++接口
开发语言·c++·算法
2501_918126913 小时前
学习python所有用来写ai的语句
人工智能·python·学习