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

news2024/10/5 20:14:55

目录

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 来结束条件编译块

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2190458.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于懒惰学习与渴求学习的一份介绍

在这篇文章中&#xff0c;我将介绍些懒惰学习与渴求学习的算法例子&#xff0c;会介绍其概念、优缺点以及其python的运用。 一、渴求学习 1.1概念 渴求学习&#xff08;Eager Learning&#xff09;是指在训练阶段构建出复杂的模型&#xff0c;然后在预测阶段运用这个构建出的…

分布式锁--redission 最佳实践!

我们知道如果我们的项目服务不只是一个实例的时候&#xff0c;单体锁就不再适用&#xff0c;而我们自己去用redis实现分布式锁的话&#xff0c;会有比如锁误删、超时释放、锁的重入、失败重试、Redis主从一致性等等一系列的问题需要自己解决。 当然&#xff0c;上述问题并非无…

3dsMax合并FBX的时候相同的节点会被合并(重命名解决),3Ds MAX创建空物体(虚拟对象或者点)

3dsMax合并FBX的时候相同的节点会被合并 3dsamax的文档&#xff0c;但是并没有说FBX的合并如何处理 https://help.autodesk.com/view/3DSMAX/2024/CHS/?guidGUID-98146EB8-436F-4954-8682-C57D4E53262A模型节点信息&#xff0c;yase&#xff0c;Points&#xff0c;Mesh 都是点…

【优选算法】(第二十一篇)

目录 外观数列(medium) 题目解析 讲解算法原理 编写代码 数⻘蛙&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 外观数列(medium) 题目解析 1.题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 2.题目描述 给定⼀个正整数n&#xff0…

openpnp - 坐标文件中的元件0角度如果和编带规定的角度不一样,需要调整贴片任务中的元件旋转角度

文章目录 openpnp - 坐标文件中的元件0角度如果和编带规定的角度不一样&#xff0c;需要调整贴片任务中的元件旋转角度笔记查看自己图纸中的封装的0角度方法贴片任务的角度值范围编带规定的0角度根据编带规定的元件0角度来调整贴片的元件旋转角度如果是托盘飞达备注备注END ope…

电脑失声,一招搞定

早已习惯了Edge浏览器的“大声朗读”功能&#xff0c;今天值班&#xff0c;值班室用的两台电脑只配有耳机&#xff0c;没有音箱&#xff0c;顿时感觉不适。 先找了一个带功放的老音箱&#xff0c;发现少了电箱到功放的音频线。 一顿搜索&#xff0c;在找到音频线的同时&#…

2024年计算机视觉与艺术研讨会(CVA 2024)

目录 基本信息 大会简介 征稿主题 会议议程 参会方式 基本信息 大会官网&#xff1a;www.icadi.net&#xff08;点击了解参会投稿等信息&#xff09; 大会时间&#xff1a;2024年11月29-12月1日 大会地点&#xff1a;中国-天津 大会简介 2024年计算机视觉与艺术国际学术…

基于SpringBoot+Vue+MySQL的装修公司管理系统

系统展示 管理员后台界面 员工后台界面 系统背景 随着信息技术的快速发展&#xff0c;装修行业正面临数字化转型的关键时刻。传统的装修管理方式存在信息管理混乱、出错率高、信息安全性差等问题&#xff0c;已无法满足现代市场的需求。因此&#xff0c;开发一套高效、便捷的装…

仿《11773手游》源码/手机游戏软件下载门户网站模板/帝国CMS 7.5

帝国CMS 7.5仿《11773手游》源码&#xff0c;手机游戏软件下载门户网站模板。简洁漂亮的手游下载网站模板&#xff0c;采用帝国CMS7.5核心&#xff0c;同步刷新M端。 该模板带同步生成插件&#xff0c;整站干净大气界面漂亮&#xff0c;简单不失简约&#xff0c;模板中的典范&…

css 简单网页布局(一)

1. 三种布局方式 1.1 标准流 1.2 浮动的使用 1.3 简述浮动 1.3.1 浮动三大特性 <style>.out {border: 1px red solid;width: 1000px;height: 500px;}.one {background-color: aquamarine;width: 200px;height: 100px;}.two {background-color: blueviolet;width: 200px;h…

Chromium 中JavaScript Fetch API接口c++代码实现(二)

Chromium 中JavaScript Fetch API接口c代码实现&#xff08;一&#xff09;-CSDN博客 接着上一篇继续介绍调用&#xff0c;上函数堆栈。 1、打开http://192.168.8.1/chfs/shared/test/test02.html 此标签进程ID12484&#xff0c; 2、打开vs附加上此进程ID12484 3、点击页面测…

华为 HCIP-Datacom H12-821 题库 (31)

&#x1f423;博客最下方微信公众号回复题库,领取题库和教学资源 &#x1f424;诚挚欢迎IT交流有兴趣的公众号回复交流群 &#x1f998;公众号会持续更新网络小知识&#x1f63c; 1. 默认情况下&#xff0c;IS-IS Level-1-2 路由器会将 Level-2 区域的明细路由信息发布到Lev…

YOLOv8 基于NCNN的安卓部署

YOLOv8 NCNN安卓部署 前两节我们依次介绍了基于YOLOv8的剪枝和蒸馏 本节将上一节得到的蒸馏模型导出NCNN&#xff0c;并部署到安卓。 NCNN 导出 YOLOv8项目中提供了NCNN导出的接口&#xff0c;但是这个模型放到ncnn-android-yolov8项目中你会发现更换模型后app会闪退。原因…

【STM32 Blue Pill编程实例】-OLED显示HC-SR04超声波测距结果

OLED显示HC-SR04超声波测距结果 文章目录 OLED显示HC-SR04超声波测距结果1、HC-SR04超声波传感器介绍2、硬件准备及接线模块配置3.1 定时器配置3.2 OLED I2C接口配置3.3 HC-SR04引脚配置4、代码实现在本文中,我们将 HC-SR04 超声波传感器与 STM32 Blue Pill 开发板结合使用,并…

Python-函数与数据容器超详解

1.函数的定义 函数是&#xff1a;组织好的、可重复使用的、用来实现特定功能的代码段。它的优点&#xff1a;将功能封装在函数内&#xff0c;可供随时随地重复利用&#xff0c;提高代码的复用性&#xff0c;减少重复代码&#xff0c;提高开发效率 Python函数的定义方式&#…

Perforce演讲回顾(上):从UE项目Project Titan,看Helix Core在大型游戏开发中的版本控制与集成使用策略

日前&#xff0c;Perforce携手合作伙伴龙智一同亮相Unreal Fest 2024上海站&#xff0c;分享Helix Core版本控制系统及其协作套件的强大功能与最新动态&#xff0c;助力游戏创意产业加速前行。 Perforce解决方案工程师Kory Luo在活动主会场&#xff0c;带来《Perforce Helix C…

论文理解【LLM-CV】—— 【MAE】Masked Autoencoders Are Scalable Vision Learners

文章链接&#xff1a;Masked Autoencoders Are Scalable Vision Learners代码&#xff1a;GitHub - facebookresearch/mae发表&#xff1a;CVPR 2022领域&#xff1a;LLM CV一句话总结&#xff1a;本文提出的 MAE 是一种将 Transformer 模型用作 CV backbone 的方法&#xff0c…

新闻推荐系统:Spring Boot的可扩展性

6系统测试 6.1概念和意义 测试的定义&#xff1a;程序测试是为了发现错误而执行程序的过程。测试(Testing)的任务与目的可以描述为&#xff1a; 目的&#xff1a;发现程序的错误&#xff1b; 任务&#xff1a;通过在计算机上执行程序&#xff0c;暴露程序中潜在的错误。 另一个…

csp-j模拟五补题报告

前言 今天第二题文件名把 r 写成 t 了 又跌出前10名了 白丢了好多分 &#xff08;“关于二进制中一的个数的研究与规律”这篇文章正在写&#xff09; 第一题 牛奶(milk) 我的代码&#xff08;AC&#xff09; #include<bits/stdc.h> #define ll long long #define fi …

Acwing 线性DP

状态转移方程呈现出一种线性的递推形式的DP&#xff0c;我们将其称为线性DP。 Acwing 898.数字三角形 实现思路&#xff1a; 对这个三角形的数字进行编号&#xff0c;状态表示依然可以用二维表示&#xff0c;即f(i,j),i表示横坐标&#xff08;横线&#xff09;&#xff0c;j表…