27. 【C语言】编写可移植的头文件与模块

7.4、模块化编程中的头文件、标准库 & 标准头文件 前面演示多文件编程时创建 main.c 和 module.c 两个源文件,并在 module.c 中定义一个函数和全局变量,然后在 main.c 中进行了声明。 阅读详情

上一篇文章我们系统学习了预处理器——宏、条件编译、预定义宏。这些知识不只是“语法点”,它们组合起来能解决一个实际工程中的大问题:让同一套代码在不同平台、不同编译器下都能顺利编译运行。

C 语言常被用于嵌入式、操作系统、跨平台库等领域,硬件和操作系统的差异是家常便饭。如果不加设计,代码很快就会变成到处是 #ifdef 的“意大利面条”。今天我们就来学习如何用预处理器,配合良好的模块化设计,写出干净、可移植的 C 代码。


一、为什么可移植性是设计出来的?

先看两个场景:

  • 你在 Linux 下写了个获取文件大小的函数,用了 stat。同事在 Windows 上编译,发现找不到 <sys/stat.h>。
  • 你写了个库,用户同时包含了你的 a.h 和 b.h,结果因为 a.h 里也包含了 b.h,报了一堆“重复定义”的错误。

这些问题都不是“代码逻辑错了”,而是代码在不同环境、不同包含路径下无法正确展开。解决它们,需要我们在写头文件时就遵循一定的“防御性编程”规范。


二、处理平台差异:条件编译实战

不同操作系统提供了不同的 API,不同编译器有不同的特性。条件编译就是用来隔离这些差异的“隔板”。

1. 利用预定义宏检测操作系统

主流编译器在编译时会自动定义一些平台宏,我们可以利用它们:

宏含义
_WIN32Windows(32/64位都会定义)
_WIN64仅 64 位 Windows 环境
__linux__Linux
__APPLE__macOS / iOS
__unix__Unix 类系统(包括 Linux、macOS)
__STDC__符合 ANSI C 标准

示例:实现一个跨平台的清屏函数:

// 头文件 console_utils.h
#ifndef CONSOLE_UTILS_H
#define CONSOLE_UTILS_H

void clear_screen(void);

#endif
// 源文件 console_utils.c
#include "console_utils.h"

#ifdef _WIN32
    #include <windows.h>
#else
    #include <stdio.h>
#endif

void clear_screen(void) {
    #ifdef _WIN32
        system("cls");
    #else
        printf("\033[2J\033[H");  // ANSI 转义序列清屏
    #endif
}

注意,这里我们把系统相关的 #include 和实现代码都放在 .c 文件 里,头文件只暴露一个干净的接口。调用者完全不需要关心底层是 Windows 还是 Unix。

2. 抽象平台差异,而非到处 #ifdef

如果平台差异非常大(比如文件操作、线程),更推荐的做法是为每个平台写一个实现文件,然后通过构建系统选择编译哪一个,而不是把所有平台的代码塞进一个 .c 里。

src/
  main.c
  platform/
    linux/
      fs.c
      thread.c
    windows/
      fs.c
      thread.c

然后用构建工具(如 CMake)根据平台选择源文件。这样代码更干净,条件编译只出现在构建脚本中。但当差异较小、仅几处时,条件编译是最轻量的方式。


三、防止头文件重复包含的多种方式

我们第十四篇学过用 #ifndef / #define / #endif 做“头文件防护”。这里再系统回顾并对比另一种方案 #pragma once。

1. 经典 Include Guard

#ifndef MY_MODULE_H
#define MY_MODULE_H

// 头文件内容...

#endif

原理:第一次包含时,MY_MODULE_H 未定义,所以进入 #ifndef 块,定义该宏,然后展开内容。第二次包含时,宏已定义,#ifndef 条件为假,整个文件被跳过。

优点:

  • 是 C 标准的一部分,所有编译器都支持。
  • 能用于保护一段代码的任何位置(不仅限于整个文件)。

缺点:

  • 需要手动起名,可能出现命名冲突(虽然通常用 文件名_H 约定)。
  • 每次包含仍需要打开文件、扫描到 #endif,在极端情况下影响编译速度(现代编译器对此已有优化)。

2. #pragma once

#pragma once

// 头文件内容...

原理:告诉编译器“这个文件在整个编译过程中只被包含一次”。这是一个非标准的、但几乎所有现代编译器(GCC、Clang、MSVC)都支持的指令。

优点:

  • 简洁,不需要起宏名,不会冲突。
  • 编译器可能进一步优化,完全记住此文件已处理,连扫描都跳过。

缺点:

  • 不是 C 标准的一部分(虽然广泛支持)。
  • 依赖文件系统路径的标识,某些复杂情况(如符号链接、不同路径指向同一文件)可能导致失效。

3. 如何选择?

对可移植性要求最高的项目(如开源库),建议同时使用两者:

#pragma once
#ifndef MY_MODULE_H
#define MY_MODULE_H

// 头文件内容...

#endif

这样,支持 #pragma once 的编译器可以受益于其优化,不支持时退回到经典的 Include Guard。我们后续的例子都会采用这种双保险写法。


四、模块化设计原则:写出“好”的头文件

好的头文件不只是“编译不出错”,还要让使用者一眼看懂怎么用,且不容易误用。

原则 1:接口最小化

头文件只暴露外部需要的函数原型、类型定义、宏、常量。绝不在头文件里暴露内部实现细节。

// bad: 暴露了内部使用的全局变量和辅助函数
extern int internal_counter;
void helper_sort(int *data, int n);
void sort(int *data, int n);

// good: 只暴露公共接口
void sort(int *data, int n);

内部辅助函数和全局变量用 static 限制在 .c 文件内,外部完全不可见。

原则 2:声明与定义分离

  • 变量:头文件用 extern 声明,只在一个 .c 文件定义。
  • 函数:头文件写原型(默认 extern),.c 实现。
  • 结构体:如果需要外部使用结构体成员,完整定义放头文件;如果只是不透明指针,可以只声明类型(前向声明)。

不透明指针 是模块化的利器:

// engine.h
typedef struct Engine Engine;   // 前向声明,不暴露成员

Engine* engine_create(void);
void    engine_destroy(Engine *e);
void    engine_run(Engine *e);
// engine.c
struct Engine {
    int speed;
    double fuel;
    // ... 大量内部成员
};
// 函数的实现...

使用者只能看到 Engine* 指针,无法直接访问成员,必须通过我们提供的函数操作。这就是 C 语言的“封装”。

原则 3:避免头文件包含其他头文件

如果头文件 A 的类型只用到指针或引用,尽量前向声明而不是 #include 另一个头文件。这可以减少编译依赖,加快编译速度。

// a.h
struct B;  // 前向声明,不用 #include "b.h"

void foo(struct B *b);

只有在需要知道结构体成员或调用其函数时,才在 .c 里包含 b.h。

原则 4:使用一致的命名约定

为自己的模块起一个前缀,避免全局名字冲突。比如一个叫 Vec 的向量库,所有函数都叫 vec_create、vec_dot、vec_free,所有宏叫 VEC_MAX_SIZE 等。


五、实战:构建一个跨平台的“文件工具”模块

我们把前面的技巧整合起来,写一个简单的跨平台模块:获取文件大小。

文件结构

file_utils.h
file_utils.c
main.c

file_utils.h

#pragma once
#ifndef FILE_UTILS_H
#define FILE_UTILS_H

#include <stddef.h>   // for size_t

// 返回文件大小(字节),失败返回 -1
long long file_utils_size(const char *filename);

#endif

接口极其简洁:一个函数,一个类型。

file_utils.c

#include "file_utils.h"

#ifdef _WIN32
    #include <windows.h>
#else
    #include <sys/stat.h>
#endif

long long file_utils_size(const char *filename) {
    #ifdef _WIN32
        WIN32_FILE_ATTRIBUTE_DATA attr;
        if (GetFileAttributesExA(filename, GetFileExInfoStandard, &attr)) {
            LARGE_INTEGER size;
            size.HighPart = attr.nFileSizeHigh;
            size.LowPart  = attr.nFileSizeLow;
            return (long long)size.QuadPart;
        }
        return -1;
    #else
        struct stat st;
        if (stat(filename, &st) == 0) {
            return (long long)st.st_size;
        }
        return -1;
    #endif
}

所有平台差异都被封在 .c 内部,对外是统一的接口。

main.c

#include <stdio.h>
#include "file_utils.h"

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("用法: %s <文件名>\n", argv[0]);
        return 1;
    }
    long long size = file_utils_size(argv[1]);
    if (size >= 0) {
        printf("%s 的大小: %lld 字节\n", argv[1], size);
    } else {
        printf("无法获取文件大小。\n");
    }
    return 0;
}

编译运行:

# Linux / macOS
gcc main.c file_utils.c -o filesize

# Windows (MinGW)
gcc main.c file_utils.c -o filesize.exe

一套代码,跨平台编译,使用者看到的只是 file_utils.h 里的一个函数。


六、常见错误与陷阱

1. 头文件防护宏命名冲突

两个不同模块都起名 UTILS_H,就会有一个被跳过。

解决:使用更独特的名字,推荐 项目名_文件名_H,比如 MYLIB_FILE_UTILS_H。

2. 在头文件里定义变量

// bad_module.h
int global_counter;   // 这是定义!每个包含此头文件的 .c 都会生成一个

应用 extern 声明,在 .c 里定义。

3. 条件编译遗漏导致平台专用代码暴露

void clear_screen(void) {
    #ifdef _WIN32
        system("cls");
    #endif
    printf("\033[2J");  // 在 Windows 上也会执行,导致乱码
}

使用 #else 或 #elif 明确平台界限,避免代码“穿透”。

4. 滥用条件编译把整个文件变成“大乱炖”

如果一个 .c 文件里 #ifdef 嵌套超过三层,而且散布在各处,应该考虑拆分成不同平台的实现文件。


七、小结

可移植性不是等到代码写完再“打补丁”,而是从头设计时就融入的纪律:

  • 条件编译隔离平台差异,但尽量把差异封装在 .c 内部,头文件保持干净。
  • 头文件防护使用 #pragma once + #ifndef 双保险。
  • 模块化原则:接口最小化、不透明指针、前向声明、统一命名。
  • 实战:一个跨平台的文件大小获取模块,演示了从设计到实现的完整流程。

现在,你已经不仅会写 C 代码,还知道如何让代码在不同环境下优雅地存活。这标志着我们从“写给自己看的小程序”迈向了“写给大家用的软件模块”。

预处理器部分到此完结。从下一篇开始,我们将进入第七阶段——高级主题:void * 泛型编程、链表、树、调试与性能优化。第一站就是通用数据操作:用 void * 实现类型无关的算法,再亲手实现一个泛型排序库。准备好了吗?


课后小练习

  1. 修改上面的 file_utils 模块,增加一个 file_utils_exists 函数,判断文件是否存在。在 Windows 和 Linux 下分别实现,并在 main 中测试。
  2. 设计一个跨平台的 sleep_ms(int milliseconds) 函数,头文件暴露接口,源文件分别用 Windows 的 Sleep 和 POSIX 的 nanosleep 实现。(注意包含对应头文件)
  3. 给定两个结构体 struct A 和 struct B,A 里有一个 B 指针成员。在头文件中如何避免包含 B 的头文件?写出前向声明示例。
  4. (小挑战)尝试拆分一个已有的小程序(比如之前的简易计算器)为多个模块:input(处理输入)、calc(计算)、output(格式化输出),每个模块都有自己的头文件和源文件,并保证主程序只包含必要的接口。

我们下期见!

💡获取本系列示例代码请访问 GitCode 仓库。

C语言五子棋 目前为止跟着学校进度学习C语言大概半年左右,基础学习只学到了指针,学得非常浅。说实话,起初对C语言的印象———只是一个学习计算机语言的敲门砖,对具体C语言如何应用等,非常迷茫。直到大一下半学期的高级语言设计课程之后,试过dos运行的图形化界面的完整小程序,才发现C语言的魅力。 ok,废话不多说了,下面是我第一节课研究的程序————C语言的五子棋。一个简单的小程序。并非纯原创,是基于网络搜索到的“... 阅读详情

相关推荐

keil 程序之分模块编写

keil与visual studio一样可以分文件编写,不同的是vs可以很方便的添加类,头文件和源程序,今天在小破站发现一个机构讲的挺好的,思路值得借鉴! 传送门在此,海创电子! https://www.bilibili.com/video/BV1vJ411X7iZ?p=27&spm_id_from=pageDriver 先简单讲下思路,keil需要手动分别的添加头文件和源程序,然后再手动写定义 如图所示: 新建个头文件 pbdata.h 然后需要手动写定义 #ifndef __PBDAT

ZHLCHLC的博客 5404

C语言模块化

上面的例子是比较简单的应用,在应用函数时先声明(例如8.1节的实例,main函数中声明了三个函数),然后再在编程应用函数。然而,在实际项目中,有成百上千的函数要编写,且函数在不同开发人员之中应用,一旦利用别人的函数多了,自己的代码因为声明函数的代码显示非常臃肿,而且一个实际工程项目,一般需要给每个函数做注释,解释函数具有的功能、参数的意义和函数的用法等,这些工作在调用前用声明来做,不利于阅读程序代码,也不易于维护,为解决这样的问题,就产生了一种称为头文件的文本文件,用于声明和描述函数。

zzqingyun的博客 1704

【C语言实战(27)】解锁C语言高阶技能:头文件与多文件编程实战

本文聚焦 C 语言头文件与多文件编程实战,先阐述头文件作用与规范,包括函数声明、宏定义等内容,两种头文件保护方式原理,以及命名和存放规则;再讲解多文件编程结构,涉及划分原则、编译链接流程及 Dev - C++、VS Code 工具配置;还通过学生成绩管理、简易计算器案例展示实战应用,分析常见问题;助力开发者理解该编程方式,提升 C 语言编程能力与项目开发效率。

邓邓子的博客 1064

初识C语言--各模块知识点简单整理

C语言是计算机编程语言,简而言之,就是人与计算机沟通的语言,同时,它是面向过程的语言,广泛应用于底层软件开发。这里提到的底层软件开发,是指在电脑应用软件之下的功能开发,包括操作系统、驱动层、硬件等。C语言的发展历史可以追溯到二进制指令,而后为方便记忆,出现了汇编语言,也就是我们常说的助记符,后来又发展为B语言,最后有了现在的C语言。起初C语言的标准无法统一,各公司的数据无法转化,后来有了大家目前公认的ANSI C标准,C语言标准得以确立。return 0。

Annabelle123_的博客 503

27、常见C语言头文件解析

本文详细解析了常见的C语言头文件,包括<arpa/inet.h>用于网络编程、<assert.h>用于程序断言验证、<complex.h>用于复数运算、<cpio.h>用于cpio归档格式定义、<ctype.h>用于字符类型判断、<devctl.h>用于设备控制、<dirent.h>用于目录操作等。每个头文件的功能、定义的类型和宏、声明的函数以及使用注意事项都有详细说明,并提供了代码示例,帮助开发者更好地理解和应用。

wasm7browser的博客 68

27、C++ 模块与传统头文件编程详解

本文详细解析了C++模块编程与传统头文件编程的区别与应用。从模块基础、类定义、导出与编译等方面深入讲解模块编程的使用方法,并结合rational类示例说明模块接口与实现的分离技巧。同时对比传统头文件编程的结构、内联函数处理和头文件包含方式,分析了两种编程方式的优缺点及适用场景。通过表格对比和流程图展示,帮助开发者根据项目需求选择合适的编程方式,提高代码的可维护性和开发效率。

rock5的博客 86

【C语言进阶】函数与模块

C语言中,函数用于封装代码块以实现特定功能,模块通过头文件与源文件组织代码,提升代码复用性与可维护性。函数需明确声明与定义,模块则注重接口设计与封装。

byte轻骑兵的技术小窝 1万+

C语言模块化编程(我见过最好的)

C语言模块化编程(我见过最好的),我目前就使用的,觉得不错

1.c语言的基本模块是什么,c语言的基本模块

c语言的模块编程是什么意思难道每个函数不就是一个模块吗???函数就是一个模块。 模块编程指的就是用函数实现各个功能,然后在主函数中调用各个功能模块(函数),这样在维护或升级程序时就可以只关注某个模块。 回答完毕,希望喜欢。标准 C 库: Standard C I/O Standard C String & Character Standard C Math Standard C Time ...

weixin_42361622的博客 6364

Echarts之风力风速玫瑰图

风力风速玫瑰图前言改进之路代码如下总结导出与导入 前言 今天上班收到任务,我们老大让我研究一下这个“风速风向玫瑰图”,还说这个是百度的雷达图的变异体,于是乎我直接找的雷达图,但是雷达图没有这种柱形的,都是点线,然后我又找了好久,最后突然在echarts官网上面看到了图1-2,才终于有了思路,原来这个图不是雷达图的变异体,而是极坐标系下的堆叠柱状图,于是就开始我的“改进”之路了。 图1-1 改进之路...

qq_38776605的博客 1万+

深度学习batch、batch_size、epoch、iteration以及小样本中episode、support set、query set关系

训练整个数据集是epoch、把数据集分为多少小块就是batch,每个小块(batch)多少个数据就是batch_size,整个数据集需要训练多少轮是iteration

qq_42740834的博客 1610

C语言基础 — ( 函数——模块化设计)

函数”是从英文 function 翻译过来的,其实,function在英文中的意思是“函数”,也是“功能”。从本质意义上来说,函数就是用来完成一定的功能的。这样,对函数的概念就很好理解了,所谓函数名就是给该功能起一个名字,如果该功能是用来实现求正弦运算的,就称为正弦函数。函数就是功能。每一个函数用来实现一个特定的功能。函数的名字应反映其代表的功能。在设计一个较大的程序时,往往把它分为若干个程序模块,每一个模块包括一个或多个函数,每个函数实现一个特定的功能。一个C程序可由一个主函数和若干个其他函数构成。

weixin_44759598的博客 2079

C 语言 头文件的作用,C 语言的头文件是必须的吗?

原标题:C 语言的头文件是必须的吗? 来源:Bean_lee链接:http://blog.chinaunix.net/uid-24774106-id-3291005.htmlN年前学习C语言开始,就被老师教导,要记得包含头文件。自己也养成了二话不说就#include的习惯。从来没有静下信来想想,C语言必需要有头文件吗?头文件到底起到什么的作用。最近一段时间做了一些静态库和动态库相关的东西,我的静态...

weixin_32503491的博客 1276

闲人闲谈PS之一项目库存跨公司业务STO解决方案--SAP闲人的开篇

写这篇文章之前,简单说一下发这篇文章的动机。也算是在SAP这一行干了快10多年,在对一个模块了解越来越深入,越发觉自己的SAP知识浅薄。总的来说,和多数顾问一样,对于**标准功能**的应用业务场景理解还是存在或多或少的知识盲区。 因为一个偶然,看到SAP团子君的文章,分享了很多干货文章。这也是自己一直想做的时候,不管水平高低与否,分享本身就意味着提升。好了废话不多说,先在优快云上发一篇吧,困惑自己将近10年的问题的解决方案。 业务场景:在项目型企业中,通常会有这样的业务,母公司和客...

SAP闲人的博客 9266

从内参矩阵到归一化平面:揭秘相机成像的标准化核心

本文深入解析了相机成像中的核心概念——归一化平面。通过拆解内参矩阵(包含焦距、主点等参数)的作用,详细阐述了如何将像素坐标转换为与具体相机硬件无关的归一化坐标。这一标准化过程是计算机视觉算法(如多相机系统对齐、视觉SLAM)实现跨设备通用性的关键,能有效提升算法的鲁棒性和泛化能力。

opencv7vision的博客 449

c语言中的stdbool.h头文件,【C语言】中的stdbool.h头文件

C语言中的stdbool.h头文件一、相关基础知识二、具体内容Win7下安装的VS2015中的stdbool.h的位置为:F:\ProgramFiles(x86)\MicrosoftVisualStudio14.0\VC\include头文件内容:////stdbool.h////Copyright(c)MicrosoftCorporation.Allrights...

weixin_34188576的博客 4890

AgentVerse实战:构建多智能体协作系统的关键技术与应用场景

本文深入探讨了AgentVerse框架在构建多智能体协作系统中的关键技术及应用场景。通过分析智能体通信协议、环境交互机制和任务分解算法,展示了如何利用大型语言模型(LLM)实现智能体间的专业化分工与动态协作。文章还提供了智能软件开发团队和智能客服系统等典型应用案例,以及部署优化实践,为开发者构建高效的多智能体系统提供实用指导。

weixin_29254029的博客 361

C语言引用不同路径下的头文件的方法

C语言学习笔记,备查: 希望在一个A.c文件中引用另一个B.c文件的方法,可以将B.c文件中的方法提取至B.h头文件,然后在B.c和A.c中分别用#include "XXX/B.h"引用。其中XXX/B.h可以是绝对路径也可以是相对路径。 举例: FindPrime.c文件中,有个isPrime方法用于判断是否为素数,希望在Factorization.c文件中引用: FindPrim...

weixin_34032792的博客 5642

LeRobot 入门教程(三)在仿真环境和自定义机器人上部署 LeRobot

本教程详细介绍了基于真实世界机器人和仿真环境的模仿学习实现方法,涵盖数据采集、训练策略和评估全流程。在真实机器人部分,重点讲解了远程操作设置、摄像头配置、数据集记录与管理技巧、策略训练(支持GPU/MPS设备)以及模型评估。仿真环境部分则演示了gym-hil的安装使用、模拟数据采集和策略训练。此外,教程还提供了自定义机器人集成指南,包括电机选择、接口继承、传感器/动作特征定义、连接管理和校准配置等核心步骤。所有方法均支持HuggingFace数据集中心和Weights & Biases工具链,为机器

2506_90492529的博客 2399

华为OD机试 - 幸运数字(Java 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中,刷题。

学Java,找哪吒 3774

c语言在查找编译头使用跳过,《C语言27—头文件》

2019年3月29日星期五 阴(声明:理论知识部分来自菜鸟教程网站!)今日学习内容:31、C 头文件头文件是扩展名为 .h 的文件,包含了 C 函数声明和宏定义,被多个源文件中引用共享。有两种类型的头文件:程序员编写的头文件和编译器自带的头文件。在程序中要使用头文件,需要使用 C 预处理指令 #include 来引用它。前面我们已经看过 stdio.h 头文件,它是编译器自带的头文件。引用头文件相...

weixin_39929465的博客 229

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

c语言编译成python库_使用C/C++代码编写Python模块

假如我们要用C语言实现下面的python脚本bird.pyimport osdef fly(name):print(name + " is flying.\n")调用脚本main.pyimport birdbird.fly("fwd")执行效果如下> python main.pyfwd is flying.实现相同逻辑的原始C代码bird.c#include void fly(const char...

weixin_39945915的博客 793

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

C/C++头文件与变量的声明和定义

C/C++头文件与变量的声明和定义最近遇到了变量重复包含的问题,才发现自己有好多知识已经模糊了,真惭愧。首先说下头文件,其实头文件对计算机而言没什么作用,她只是在预编译时在#include的地方展开一下,没别的意义了,头文件主要作用有两个:一是把很多其他文件需要重复使用的函数变量等在此声明,在需要的地方include;二是给使用你函数文件的人看的。那既然是说明,那么头文件里面放的自然就是关于函数,变

祝 枫 2万+

编写python扩展模块_《深度剖析CPython解释器》27. 使用Python/C API编写扩展模块:编写扩展模块的整体流程...

楔子到目前为止,我们已经介绍了很多关于解释器方面的内容,本来接下来应该要说内存管理的,但是个人觉得应该对前面的系列做一个总结。而最好的方式,就是使用Python/C API编写扩展模块,个人是这么认为的。我们编写过不少Python的模块,但显然用的都是Python语言,那么问题来了,我们如何使用C语言来编写呢?使用C为Python编写扩展的话,是需要遵循一定套路的,而这个套路很固定。那么下面我们就...

weixin_40001967的博客 289

如何找linux宏的头文件,如何在Linux中找到C编程语言的头文件?

如何在Linux中找到C编程语言的头文件?当我用Linux编写C程序,然后使用gcc编译它们时,我总是对那些头文件在哪里感到好奇。 例如,其中stdio.h是。 更笼统地说,stdbool.h在哪里?我不仅想知道它在哪里,而且还想知道如何获得这些地方,例如,使用shell命令或使用C编程语言。10个解决方案62 votes#include将打印每个包含文件的完整路径,作为常规编译的副作用。 除了使...

weixin_29802189的博客 722
上一篇: 26. 【C语言】编译前的“文本大师”:预处理器指令
下一篇: 28. 【C语言】通用数据操作:`void *` 与类型无关编程
gugucoding
博客等级 码龄140天 7粉丝 102原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值