38 文件包含(标准库头文件、自定义头文件)、相对路径与绝对路径、条件编译(#if、#ifdef、#if define、#ifndef)

目录

1 文件包含

1.1 #include 指令

1.2 包含标准库头文件

1.3 包含自定义头文件

1.3.1 使用相对路径

1.3.2 使用绝对路径

2 条件编译

2.1 #if … #endif

2.1.1 语法格式

2.1.2 功能说明

2.1.3 流程分析

2.1.4 案例演示:#if 0 ... #endif

2.1.5 案例演示:临时启用或禁用某些调试代码

2.2 #if ... #else ... #endif

2.2.1 格式语法

2.2.2 功能说明

2.2.3 流程分析

2.2.4 案例演示:基本用法

2.3 #if … #elif … #else … #endif

2.3.1 格式语法

2.3.2 功能说明

2.3.3 流程分析

2.3.4 案例演示:基本用法

2.4 #ifdef ... #else … #endif

2.4.1 格式语法

2.4.2 功能说明

2.4.3 流程分析

2.4.4 案例演示:基本用法

2.4.5 案例演示:控制头文件的加载

2.5 #if defined … #elif … #else … #endif

2.5.1 格式语法

2.5.2 功能说明

2.5.3 流程分析

2.5.4 案例演示:基本用法

2.6 #ifndef ... #endif

2.6.1 格式语法

2.6.2 功能说明

2.6.3 流程分析

2.6.4 案例演示:基本用法

2.6.5 案例演示:防止头文件重复加载

2.7 可选的使用小括号包裹宏名

3 多平台编译

3.1 常见的平台宏

3.2 简单的多平台编译

3.3 包含平台特定的头文件

3.4 使用条件编译实现平台特定的代码路径

3.5 综合案例

3.5.1 具体要求

3.5.2 暂停函数

3.5.3 案例实现

4 预处理命令总结表


1 文件包含

1.1 #include 指令

        #include 指令用于在程序中引入其他文件的内容,这些文件通常包含程序运行所需的函数声明、变量定义、宏定义等。通过使用 #include,能够将标准库头文件、自定义头文件或其它外部源代码文件中的内容整合到当前的源文件中,从而实现代码的复用和模块化管理。

  • 标准库头文件和自定义头文件通常具有 .h 扩展名
  • 一个源文件可以通过多次使用 #include 指令来引入多个不同的头文件
  • 同一头文件可以被多个不同的源文件所引用

1.2 包含标准库头文件

        标准库头文件是由系统提供的预定义文件,它们包含了各种常用功能的声明,如输入输出(stdio.h)、布尔值支持(stdbool.h)、字符串操作(string.h)以及时间处理(time.h)等。要使用这些标准库中的功能,需要在源文件中通过 #include 指令引入相应的头文件。引入标准库头文件的语法格式为:

#include <头文件名.h>   // 尖括号

        例如, 若要使用标准输入输出函数,可以在程序中加入以下代码行:

#include <stdio.h>

        这种格式告诉编译器从系统的标准库路径中查找并包含指定的头文件。使用尖括号 < > 是引入标准库头文件的标准做法,它确保了编译器能够正确地定位到正确的文件位置。

1.3 包含自定义头文件

        自定义头文件是用户根据项目需求创建的头文件,用于存放特定的常量、宏定义、全局变量声明以及函数原型。为了在不同的源文件中重用这些定义,需要使用 #include 指令来引入自定义头文件。自定义头文件的引入格式如下:

#include "文件名.h"  // 双引号

1.3.1 使用相对路径

        当自定义头文件位于源文件的同一目录或其子目录中时,可以使用相对路径来指定头文件的位置。相对路径以当前源文件所在的目录为起点。例如,基于以下目录结构:

├─ myheader05.h
└─ project
   ├─ inc
   │  └─ myheader04.h
   ├─ myheader03.h
   └─ src
      ├─ includes
      │  └─ myheader02.h
      ├─ main.c
      └─ myheader01.h

        若要在 main.c 文件中引入上述自定义头文件,可以采用如下写法:

        引入同一目录下的头文件(./ 表示当前目录,可以省略,当在路径前使用 ./,意味着路径是从当前目录开始的,敲了 ./ 之后编译器会有智能路径提示):

#include "./myheader01.h"  // 使用 ./ 表示当前目录下

#include "myheader01.h" // 直接使用文件名,省略 ./,因为文件在同一目录下

        引入子目录中的头文件:

#include "includes/myheader02.h"  // 子目录相对路径

        引入父目录中的头文件(../ 表示上一级目录,每增加一个 ../,就表示向上一级目录移动一层):

#include "../myheader03.h"  // 上一级目录相对路径

        引入更高层级目录中的头文件:

#include "../inc/myheader04.h" // 上一级目录下的子目录相对路径
#include "../../myheader05.h"  // 移动两层到项目根目录

提示:

        建议将头文件放置在源文件所在目录或子目录中,使用相对路径来引用头文件,这有助于保持项目的结构清晰,并提高代码的可移植性。 

         Windows 文件系统内部可以识别和处理正斜杠 / 和反斜杠 \。无论使用哪种分隔符,Windows 都能正确解析路径。如下所示:

#include "./myHeaders/headers1.h"   //正斜杠,建议,更直观
#include ".\myHeaders\headers1.h"   // 反斜杠
#include ".\\myHeaders\\headers1.h" // 转义字符的使用

1.3.2 使用绝对路径

        绝对路径指明了从文件系统的根目录到目标文件的确切路径。虽然可以使用绝对路径来引入自定义头文件,但这通常不推荐,因为它会降低代码的可移植性和灵活性。不过,如果确实需要使用绝对路径,不同操作系统下的写法如下:

Windows 系统:

        在 C 语言和其他编程语言中,反斜杠 \ 通常用作转义字符: \\ 表示反斜杠本身

#include "C:\\Preparation\\Embedded\\01CLang\\code\\project\\foo.h"

Linux 系统或 MacOS 系统:

#include "/usr/local/lib/foo.h"

提示:

        为了项目的维护性和跨平台兼容性,推荐优先考虑使用相对路径来包含自定义头文件


2 条件编译

2.1 #if … #endif

2.1.1 语法格式

        #if ... #endif 指令用于预处理器的条件编译。其基本语法格式如下:

#if 条件表达式
    // 条件成立时执行的代码
#endif
  • #if:开始一个条件判断块,后面跟一个条件表达式。
  • #endif:结束条件判断块。

2.1.2 功能说明

        #if ... #endif 指令允许在编译时根据条件表达式的值来决定是否编译某段代码。如果条件表达式的值不等于 0,条件为真,#if 和 #endif 之间的代码会被编译;如果条件表达式的值等于 0,条件为假,这部分代码会被编译器忽略。

2.1.3 流程分析

1. 预处理器解析

        编译器的预处理器首先读取源代码文件,并解析 #if ... #endif 指令。

2. 条件评估

        预处理器评估 #if 后面的条件表达式。

3. 代码选择

  • 如果条件表达式的值不等于 0,条件为真,#if 和 #endif 之间的代码会被保留并编译
  • 如果条件表达式的值等于 0,条件为假,#if 和 #endif 之间的代码会被忽略,不会被编译

4. 继续编译

        预处理器处理完 #if ... #endif 指令后,编译器继续编译剩余的代码。

2.1.4 案例演示:#if 0 ... #endif

        #if 0 ... #endif 指令在预处理器中用于条件编译。当条件表达式的值为 0 时,#if 和 #endif 之间的代码会被编译器忽略。这种写法常用于临时注释掉一段代码,而不需要使用传统的注释符号(如 /* ... */)。

#include <stdio.h>

#if 0
#define P 66.66           // 这行代码不会被编译
const double PI = 3.1415; // 这行代码不会被编译
#endif

#if 1
const double radius = 5.6789; // 这行代码会被编译
#endif

int main()
{

    // double d1 = PI; // 错误:未定义标识符 "PI"
    // double d2 = P; // 错误:未定义标识符 "P"

    double d3 = radius;
    printf("radis=%.2lf", d3); // 5.68

    return 0;
}

2.1.5 案例演示:临时启用或禁用某些调试代码

        在调试过程中,#if ... #endif 指令可以用来临时启用或禁用某些调试代码。通过定义一个调试宏(如 DEBUG),可以在编译时控制是否包含调试代码。这样可以在不影响代码可读性的情况下,快速启用或禁用调试信息。

        假设我们有一个简单的程序,需要在调试模式下输出额外的信息。

#include <stdio.h>

// 定义 DEBUG 宏来控制调试模式
#define DEBUG 1 // 取消注释或注释以切换调试模式

int main()
{
    int x = 10;
    int y = 20;
    int sum = x + y;

#if DEBUG
    printf("Debug mode is on.\n");
    printf("x = %d, y = %d\n", x, y);
#endif

    printf("Sum: %d\n", sum);

    return 0;
}

        发布模式:注释掉 config.h 中的 #define DEBUG 1,然后编译和运行程序。

        调试模式:取消注释 config.h 中的 #define DEBUG 1,然后编译和运行程序。

2.2 #if ... #else ... #endif

2.2.1 格式语法

        #if ... #else ... #endif 指令用于预处理器的条件编译。其基本语法格式如下:

#if 条件表达式
    // 条件成立时执行的代码
#else
    // 条件不成立时执行的代码
#endif
  • #if:开始一个条件判断块,后面跟一个条件表达式。
  • #else:指定条件不成立时的代码块。#else 是可选的
  • #endif:结束条件判断块。

2.2.2 功能说明

        #if ... #else ... #endif 指令允许在编译时根据条件表达式的值来决定是否编译某段代码。如果条件表达式的值不等于 0,条件为真,#if 和 #else 之间的代码会被编译;如果条件表达式的值等于 0,条件为假,#else 和 #endif 之间的代码会被编译。

2.2.3 流程分析

1. 预处理器解析

        编译器的预处理器读取源代码文件,并解析 #if ... #else ... #endif 指令。

2. 条件评估

        预处理器评估 #if 后面的条件表达式。

3. 代码选择

  • 如果条件表达式的值不等于 0,条件为真,#if 和 #else 之间的代码会被保留并编译
  • 如果条件表达式的值等于 0,条件为假,#else 和 #endif 之间的代码会被保留并编译

4. 继续编译

        预处理器处理完 #if ... #else ... #endif 指令后,编译器继续编译剩余的代码。

2.2.4 案例演示:基本用法

#include <stdio.h>

#define FOO 1

int main()
{
#if FOO
    printf("defined\n"); // 会编译且执行
#else
    printf("not defined\n"); // 不会编译也不会执行
#endif

    if (1)
    {
        printf("defined\n"); // 会编译且执行
    }
    else
    {
        printf("not defined\n"); // 会编译但不会执行
    }

    return 0;
}

2.3 #if … #elif … #else … #endif

2.3.1 格式语法

        #if ... #elif ... #else ... #endif 指令用于预处理器的多条件编译。其基本语法格式如下:

#if 条件表达式1
    // 条件 1 成立时执行的代码
#elif 条件表达式2
    // 条件 2 成立时执行的代码
#elif 条件表达式3
    // 条件 3 成立时执行的代码
    // 可以有多个 #elif 分支
#else
    // 所有条件都不满足时执行的代码
#endif
  • #if:开始一个条件判断块,后面跟一个条件表达式。
  • #elif:指定额外的条件分支。可以有多个 #elif 分支
  • #else:指定所有条件都不满足时的代码块。#else 是可选的
  • #endif:结束条件判断块。

2.3.2 功能说明

        #if ... #elif ... #else ... #endif 指令允许在编译时根据多个条件表达式的值来决定是否编译某段代码预处理器会依次评估每个条件表达式,直到找到一个条件为真的表达式,然后编译对应的代码块。如果所有条件都不满足,则编译 #else 分支的代码

2.3.3 流程分析

1. 预处理器解析

        编译器的预处理器读取源代码文件,并解析 #if ... #elif ... #else ... #endif 指令。

2. 条件评估

        预处理器依次评估 #if 和每个 #elif 后面的条件表达式

3. 代码选择

  • 如果 #if 后面的条件表达式为真,编译 #if 和第一个 #elif 之间的代码。
  • 如果 #if 后面的条件表达式为假,继续评估每个 #elif 后面的条件表达式,直到找到一个条件为真的表达式,编译对应的代码块。
  • 如果所有 #if 和 #elif 后面的条件表达式都为假,编译 #else 分支的代码。

4. 继续编译

        预处理器处理完 #if ... #elif ... #else ... #endif 指令后,编译器继续编译剩余的代码。

2.3.4 案例演示:基本用法

#include <stdio.h>

// 定义 HAPPY_FACTOR 宏来控制条件编译
#define HAPPY_FACTOR 2 // 可以设置为 0, 1, 或其他值

int main()
{
#if HAPPY_FACTOR == 0
    printf("I'm not happy!\n");
#elif HAPPY_FACTOR == 1
    printf("I'm just regular\n");
#elif HAPPY_FACTOR == 2
    printf("I'm extra happy!\n");   // 这行代码会编译
#else
    printf("Unknown happiness level\n");
#endif

    return 0;
}

2.4 #ifdef ... #else … #endif

2.4.1 格式语法

        #ifdef ... #endif 指令用于预处理器判断某个宏是否定义过。并根据宏的状态选择不同的代码路径。虽然它可以提供两个分支(宏已定义和宏未定义),但它并不支持 #elif 指令,因为 #ifdef 是专门用于检查宏是否定义的,而不是用于多条件判断。其基本语法格式如下:

#ifdef 宏名
    // 宏已定义时执行的代码
#else
    // 宏未定义时执行的代码
#endif
  • #ifdef:开始一个条件判断块,后面跟一个宏名。
  • #else:指定宏未定义时的代码块。#else 是可选的
  • #endif:结束条件判断块。

2.4.2 功能说明

        #ifdef ... #endif 指令允许在编译时根据某个宏是否定义来决定是否编译某段代码。如果宏已被定义,#ifdef 和 #endif 之间的代码会被编译;如果宏未定义,这部分代码会被编译器忽略。

2.4.3 流程分析

1. 预处理器解析

        编译器的预处理器读取源代码文件,并解析 #ifdef ... #endif 指令。

2. 宏检查

        预处理器检查 #ifdef 后面的宏名是否已定义。

3. 代码选择

  • 如果宏已定义,#ifdef 和 #else 之间的代码会被保留并编译
  • 如果宏未定义,#else 和 #endif 之间的代码会被保留并编译

4. 继续编译

        预处理器处理完 #ifdef ... #endif 指令后,编译器继续编译剩余的代码。

2.4.4 案例演示:基本用法

#include <stdio.h>

// 定义 FEATURE_X 宏来控制是否编译特定功能模块
#define FEATURE_X 1 // 取消注释或注释以切换功能模块

int main()
{
#ifdef FEATURE_X  // 判断有无定义这个宏,不看宏的值为多少
    printf("Feature X is enabled.\n"); // 会编译执行
#else
    printf("Feature X is disabled.\n");
#endif

    return 0;
}

2.4.5 案例演示:控制头文件的加载

        #ifdef ... #else ... #endif 指令可以用于根据宏的状态选择不同的头文件和代码路径。

#include <stdio.h>

// 定义 MAVIS 宏来控制加载哪个头文件
#define MAVIS 1 // 取消注释或注释以切换头文件

// 条件加载头文件和定义常量
#ifdef MAVIS // 判断有无定义过宏 MAVIS
#include "foo.h"
#define STABLES 1
#else
#include "bar.h"
#define STABLES 2
#endif

int main()
{
    printf("STABLES: %d\n", STABLES); // STABLES: 1

    return 0;
}

2.5 #if defined … #elif … #else … #endif

2.5.1 格式语法

        #if defined 是 #ifdef 的另一种形式,用于判断某个宏是否定义过。它提供了更多的灵活性,可以进行多重判断。#if defined 的基本语法格式如下:

#if defined 宏名
    // 宏已定义时执行的代码
#elif defined 宏名2
    // 宏 2 已定义时执行的代码
#else
    // 所有宏都未定义时执行的代码
#endif
  • #if defined 宏名:检查宏名是否已定义。如果已定义,条件为真。
  • #elif defined 宏名2:指定另一个宏名进行检查。可以有多个 #elif 分支
  • #else:指定所有宏都未定义时的代码块。#else 是可选的
  • #endif:结束条件判断块。

2.5.2 功能说明

        #if defined 指令允许在编译时根据某个宏是否定义来决定是否编译某段代码。与 #ifdef 不同的是,#if defined 可以嵌入到更复杂的条件表达式中,提供更大的灵活性。

2.5.3 流程分析

1. 预处理器解析

        编译器的预处理器读取源代码文件,并解析 #if defined 指令。

2. 宏检查

        预处理器检查 #if defined 后面的宏名是否已定义。

3. 代码选择

  • 如果宏已定义,#if defined 和 #elif 之间的代码会被保留并编译。
  • 如果宏未定义,继续检查 #elif 后面的宏名。
  • 如果所有宏都未定义,#else 和 #endif 之间的代码会被保留并编译。

4. 继续编译

        预处理器处理完 #if defined ... #endif 指令后,编译器继续编译剩余的代码。

2.5.4 案例演示:基本用法

        假设我们有一个程序,需要根据多个宏的状态选择不同的代码路径。

#include <stdio.h>

// 定义一些宏来控制条件编译
#define FOO 1 // 取消注释或注释以切换条件
// #define BAR 1 // 取消注释或注释以切换条件

int main()
{
    int x;

#if defined FOO
    x = 2;
#elif defined BAR
    x = 3;
#else
    x = 4;
#endif

    printf("x = %d\n", x); // x = 2

    return 0;
}

2.6 #ifndef ... #endif

2.6.1 格式语法

        #ifndef ... #endif 指令用于判断某个宏是否没有被定义过。它与 #ifdef ... #endif 正好相反,如果宏没有被定义,则执行指定的操作。#ifndef ... #endif 常用于防止头文件的重复加载,确保每个头文件只被包含一次。#ifndef ... #endif 的基本语法格式如下:

#ifndef 宏名
    // 宏未定义时执行的代码
#endif
  • #ifndef 宏名:检查宏名是否未定义。如果未定义,条件为真。
  • #endif:结束条件判断块。

2.6.2 功能说明

        #ifndef ... #endif 指令允许在编译时根据某个宏是否未定义来决定是否编译某段代码。它特别适用于防止头文件的重复包含,确保每个头文件只被包含一次,避免重复定义导致的编译错误。

2.6.3 流程分析

1. 预处理器解析

        编译器的预处理器读取源代码文件,并解析 #ifndef ... #endif 指令。

2. 宏检查

        预处理器检查 #ifndef 后面的宏名是否未定义。

3. 代码选择:

  • 如果宏未定义,#ifndef 和 #endif 之间的代码会被保留并编译
  • 如果宏已定义,#ifndef 和 #endif 之间的代码会被编译器忽略

4. 继续编译

        预处理器处理完 #ifndef ... #endif 指令后,编译器继续编译剩余的代码。

2.6.4 案例演示:基本用法

        假设我们有一个程序,需要根据宏 EXTRA_HAPPY 是否定义来输出不同的信息。

#include <stdio.h>

// 定义 EXTRA_HAPPY 宏来控制条件编译
#define EXTRA_HAPPY 1 // 取消注释或注释以切换条件

int main()
{
#ifdef EXTRA_HAPPY
    printf("I'm extra happy!\n"); // 会编译执行
#endif

#ifndef EXTRA_HAPPY
    printf("I'm just regular\n");
#endif

    return 0;
}

2.6.5 案例演示:防止头文件重复加载

        假设我们有一个头文件 myheader.h,需要防止它被重复加载。

// myheader.h

#ifndef MYHEADER_H
#define MYHEADER_H

// 头文件的内容
#define PI 3.14159

#endif // MYHEADER_H

        主程序文件:

// main.c

#include <stdio.h>
#include "myheader.h"
#include "myheader.h"  // 重复包含头文件,也没有关系

int main() {
    printf("Value of PI: %f\n", PI);

    return 0;
}

        上面示例中,宏 MYHEADER_H 对应文件名 myheader.h 的大写。只要 #ifndef 发现这个宏没有被定义过,就说明该头文件没有加载过,从而加载内部的代码,并会定义宏 MYHEADER_H,防止被再次加载

2.7 可选的使用小括号包裹宏名

        对于 #ifdef#ifndef 这两个指令用来检查某个宏是否已经被定义。它们后面跟的宏名可以不用小括号包裹,因为这里只关心宏是否存在,而不关心它的值。

#ifdef DEBUG
// 如果 DEBUG 宏被定义,则编译这里的代码
#endif

#ifndef RELEASE
// 如果 RELEASE 宏未被定义,则编译这里的代码
#endif

        对于 #if #elif 这些指令允许更复杂的条件判断,包括对宏值的算术运算和逻辑运算。在这种情况下,如果宏代表一个数值或表达式,使用小括号可以帮助确保正确的运算顺序

#define VERSION 2

#if (VERSION > 1)
// 如果 VERSION 大于1,则编译这里的代码
#endif

#if (DEBUG == 1) && (VERSION >= 2)
// 如果 DEBUG 等于 1 且 VERSION 大于等于 2,则编译这里的代码
#endif

        对于 #if defined,这是一种替代 #ifdef 的方式,语法上更灵活一些,因为它可以嵌入到更复杂的条件表达式中。

#if defined(DEBUG) && defined(RELEASE)
// 如果 DEBUG 和 RELEASE 都被定义,则编译这里的代码
#endif

        总结来说,在 #ifdef 和 #ifndef 中,宏名通常不加小括号;而在 #if 和 #elif 中,为了提高代码的可读性和确保运算的准确性,建议对宏名或表达式使用小括号。不过,这并不影响宏名本身的识别,而是为了确保整个条件表达式的正确性。


3 多平台编译

        条件编译是 C 语言中非常强大的工具,特别是在开发跨平台应用程序时。通过使用 #ifdef、#ifndef、#if defined 等预处理指令,可以根据不同的编译平台选择不同的代码路径,从而实现多平台编译。

3.1 常见的平台宏

        不同的编译器和操作系统通常会定义一些特定的宏,这些宏可以帮助我们识别当前的编译平台。以下是一些常见的平台宏:

  • Windows:_WIN32 或 _WIN64
  • Linux:__linux__
  • macOS:__APPLE__
  • Unix:__unix__

3.2 简单的多平台编译

        假设我们有一个程序,需要根据不同的平台输出不同的信息。

// main.c

#include <stdio.h>

int main() {
    #ifdef _WIN32
        printf("Running on Windows\n");
    #elif defined(__linux__)           // 可以使用小括号包裹宏名
        printf("Running on Linux\n");
    #elif defined(__APPLE__)
        printf("Running on macOS\n");
    #elif defined(__unix__)
        printf("Running on Unix\n");
    #else
        printf("Running on an unknown platform\n");
    #endif

    return 0;
}

3.3 包含平台特定的头文件

        假设我们需要在不同的平台上包含不同的头文件。

// main.c

#include <stdio.h>

// 条件加载头文件
#ifdef _WIN32
    #include "win_header.h"
#elif defined(__linux__)        // 可以使用小括号包裹宏名
    #include "linux_header.h"
#elif defined(__APPLE__)
    #include "macos_header.h"
#else
    #include "generic_header.h"
#endif

int main() {
    // 调用平台特定的函数
    #ifdef _WIN32
        win_function();
    #elif defined(__linux__)
        linux_function();
    #elif defined(__APPLE__)
        macos_function();
    #else
        generic_function();
    #endif

    return 0;
}

3.4 使用条件编译实现平台特定的代码路径

        假设我们需要在不同的平台上实现不同的功能。

// main.c

#include <stdio.h>

int main() {
    #ifdef _WIN32
        printf("Running on Windows\n");
        // Windows-specific code
    #elif defined(__linux__)
        printf("Running on Linux\n");
        // Linux-specific code
    #elif defined(__APPLE__)
        printf("Running on macOS\n");
        // macOS-specific code
    #else
        printf("Running on an unknown platform\n");
        // Generic code
    #endif

    return 0;
}

3.5 综合案例

3.5.1 具体要求

        开发一个 C 语言程序,让它暂停 5 秒以后再输出内容 "helllo, world~",并且要求跨平台,在 Windows 和 Linux 下都能运行。

3.5.2 暂停函数

        Windows 平台下的暂停函数的原型是 void Sleep(DWORD dwMilliseconds),参数的单位是“毫秒”,位于 <windows.h> 头文件。

#include <stdio.h>  
#include <windows.h> // 包含 Windows 头文件  
  
int main() {  
    printf("程序将暂停5秒钟...\n");  
    // Sleep 函数接受毫秒作为参数,所以 5 秒需要 5000 毫秒  
    Sleep(5000);   
    printf("程序继续执行。\n");  
    return 0;  
}

        Linux 平台下暂停函数的原型是 unsigned int sleep (unsigned int seconds),参数的单位是“秒”,位于 <unistd.h> 头文件。

3.5.3 案例实现

#include <stdio.h>

// 使用条件编译来区分不同的操作系统平台
#if _WIN32 // 如果是在 Windows 平台上编译
// 包含 Windows 平台特有的头文件
#include <windows.h>
// 定义一个带参宏 SLEEP,它调用 Windows API 中的 Sleep 函数,注意 Sleep 函数的参数是毫秒
#define SLEEP(t) Sleep(t * 1000) // 将秒转换为毫秒,因为 Sleep 函数接受的是毫秒作为参数
#elif __linux__                  // 如果是在 Linux 平台上编译
// 包含 Linux 平台特有的头文件
#include <unistd.h>
// 定义一个宏 SLEEP,它直接映射到 unistd.h中 的 sleep 函数,注意 sleep 函数的参数是秒
#define SLEEP sleep // 在 Linux上,sleep 函数接受的是秒作为参数
#endif              // 结束条件编译

int main()
{
    // 调用 SLEEP 宏,传入 5 作为参数,根据平台不同,这将调用 Windows 的 Sleep 函数或 Linux 的 sleep 函数
    // 在 Windows 上,这将导致程序暂停 5000 毫秒(5 秒);在 Linux上,这将导致程序暂停 5 秒
    SLEEP(5);

    printf("hello, world~");

    return 0;
}

4 预处理命令总结表

指令说明
#include包含一个源代码文件或头文件,可以是标准库头文件或用户自定义的头文件。
#define定义一个宏,宏可以是常量、函数样式的宏(宏函数)或代码块。宏定义后,在编译时会进行文本替换。
#undef取消之前定义的宏,使该宏在后续代码中不再有效。
#if如果给定的条件表达式为真(非零),则编译随后的代码块。条件表达式可以是宏定义、常量表达式或运算符组合。
#ifdef如果指定的宏已经被定义,则编译随后的代码块。这通常用于检查某个功能是否启用。
#ifndef如果指定的宏没有被定义,则编译随后的代码块。这通常用于检查某个功能是否未启用或进行平台特定的代码编译。
#elif如果前面的 #if 或 #elif 条件为假,并且当前 #elif 的条件为真,则编译随后的代码块。它允许在单个 #if...#endif 块中测试多个条件。
#else如果前面的所有 #if、#elif 条件都为假,则编译随后的代码块。它提供了在条件不满足时的备用代码路径。
#endif结束一个 #if...#else 条件编译块。每个 #if 指令都必须有一个对应的 #endif 来结束条件编译块
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Thanks_ks

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值