- C语言编程教程
- C语言 - 首页
- C语言基础
- C语言 - 概述
- C语言 - 特性
- C语言 - 历史
- C语言 - 环境搭建
- C语言 - 程序结构
- C语言 - Hello World
- C语言 - 编译过程
- C语言 - 注释
- C语言 - 词法单元
- C语言 - 关键字
- C语言 - 标识符
- C语言 - 用户输入
- C语言 - 基本语法
- C语言 - 数据类型
- C语言 - 变量
- C语言 - 整数提升
- C语言 - 类型转换
- C语言 - 类型强制转换
- C语言 - 布尔值
- C语言中的常量和字面量
- C语言 - 常量
- C语言 - 字面量
- C语言 - 转义序列
- C语言 - 格式说明符
- C语言中的运算符
- C语言 - 运算符
- C语言 - 算术运算符
- C语言 - 关系运算符
- C语言 - 逻辑运算符
- C语言 - 位运算符
- C语言 - 赋值运算符
- C语言 - 一元运算符
- C语言 - 自增和自减运算符
- C语言 - 三元运算符
- C语言 - sizeof运算符
- C语言 - 运算符优先级
- C语言 - 其他运算符
- C语言中的决策机制
- C语言 - 决策机制
- C语言 - if语句
- C语言 - if...else语句
- C语言 - 嵌套if语句
- C语言 - switch语句
- C语言 - 嵌套switch语句
- C语言中的循环
- C语言 - 循环
- C语言 - while循环
- C语言 - for循环
- C语言 - do...while循环
- C语言 - 嵌套循环
- C语言 - 死循环
- C语言 - break语句
- C语言 - continue语句
- C语言 - goto语句
- C语言中的函数
- C语言 - 函数
- C语言 - 主函数
- C语言 - 按值调用函数
- C语言 - 按引用调用函数
- C语言 - 嵌套函数
- C语言 - 可变参数函数
- C语言 - 用户自定义函数
- C语言 - 回调函数
- C语言 - 返回语句
- C语言 - 递归
- C语言中的作用域规则
- C语言 - 作用域规则
- C语言 - 静态变量
- C语言 - 全局变量
- C语言中的数组
- C语言 - 数组
- C语言 - 数组的特性
- C语言 - 多维数组
- C语言 - 将数组传递给函数
- C语言 - 从函数返回数组
- C语言 - 变长数组
- C语言中的指针
- C语言 - 指针
- C语言 - 指针和数组
- C语言 - 指针的应用
- C语言 - 指针运算
- C语言 - 指针数组
- C语言 - 指向指针的指针
- C语言 - 将指针传递给函数
- C语言 - 从函数返回指针
- C语言 - 函数指针
- C语言 - 指向数组的指针
- C语言 - 指向结构体的指针
- C语言 - 指针链
- C语言 - 指针与数组的区别
- C语言 - 字符指针和函数
- C语言 - 空指针
- C语言 - void指针
- C语言 - 悬空指针
- C语言 - 解引用指针
- C语言 - 近、远和巨型指针
- C语言 - 指针数组的初始化
- C语言 - 指针与多维数组的区别
- C语言中的字符串
- C语言 - 字符串
- C语言 - 字符串数组
- C语言 - 特殊字符
- C语言中的结构体和联合体
- C语言 - 结构体
- C语言 - 结构体和函数
- C语言 - 结构体数组
- C语言 - 自引用结构体
- C语言 - 查找表
- C语言 - 点(.)运算符
- C语言 - 枚举(enum)
- C语言 - 结构体填充和打包
- C语言 - 嵌套结构体
- C语言 - 匿名结构体和联合体
- C语言 - 联合体
- C语言 - 位域
- C语言 - typedef
- C语言中的文件处理
- C语言 - 输入与输出
- C语言 - 文件I/O(文件处理)
- C语言预处理器
- C语言 - 预处理器
- C语言 - 预处理指令
- C语言 - 预处理器运算符
- C语言 - 宏
- C语言 - 头文件
- C语言中的内存管理
- C语言 - 内存管理
- C语言 - 内存地址
- C语言 - 存储类别
- 其他主题
- C语言 - 错误处理
- C语言 - 可变参数
- C语言 - 命令执行
- C语言 - 数学函数
- C语言 - static关键字
- C语言 - 随机数生成
- C语言 - 命令行参数
- C语言编程资源
- C语言 - 问答
- C语言 - 快速指南
- C语言 - 速查表
- C语言 - 有用资源
- C语言 - 讨论
C语言预处理器
C预处理器不是编译器的一部分,而是编译过程中的一个单独步骤。简单来说,C预处理器只是一个文本替换工具,它指示编译器在实际编译之前进行必要的预处理。我们将C预处理器称为CPP。
在C编程中,预处理是C代码编译的第一步。它发生在词法分析步骤之前。预处理器的重要功能之一是包含包含程序中使用的库函数的头文件。C语言中的预处理器还定义常量并展开宏。
C语言中的预处理器语句称为指令。程序的预处理器部分始终出现在C代码的顶部。每个预处理器语句都以井号(#)符号开头。
C语言中的预处理器指令
下表列出了所有重要的预处理器指令:
指令 | 描述 |
---|---|
# define | 替换预处理器宏。 |
#include | 插入来自另一个文件的特定头文件。 |
#undef | 取消定义预处理器宏。 |
#ifdef | 如果此宏已定义,则返回true。 |
#ifndef | 如果此宏未定义,则返回true。 |
#if | 测试编译时条件是否为真。 |
#else | #if 的替代方案。 |
#elif | #else 和 #if 合并成一个语句。 |
#endif | 结束预处理器条件。 |
#error | 在stderr上打印错误消息。 |
#pragma | 使用标准化的方法向编译器发出特殊命令。 |
预处理器示例
分析以下示例以了解各种指令。
此#define指令告诉CPP将MAX_ARRAY_LENGTH的所有实例替换为20。使用#define定义常量以提高可读性。
#define MAX_ARRAY_LENGTH 20
以下指令告诉CPP从系统库中获取“stdio.h”并将文本添加到当前源文件中。下一行告诉CPP从本地目录获取“myheader.h”并将内容添加到当前源文件中。
#include <stdio.h> #include "myheader.h"
现在,让我们看一下以下#define和#undef指令。它们告诉CPP取消定义现有的FILE_SIZE并将其定义为42。
#undef FILE_SIZE #define FILE_SIZE 42
以下指令告诉CPP仅在MESSAGE未定义时定义MESSAGE。
#ifndef MESSAGE #define MESSAGE "You wish!" #endif
以下指令告诉CPP如果定义了DEBUG,则处理包含的语句。
#ifdef DEBUG /* Your debugging statements here */ #endif
如果您在编译时向gcc编译器传递-DDEBUG标志,这将很有用。这将定义DEBUG,因此您可以在编译过程中动态地打开和关闭调试。
C语言中的预定义宏
ANSI C定义了许多宏。尽管每个宏都可用于编程,但不应直接修改预定义宏。
宏 | 描述 |
---|---|
__DATE__ | 当前日期,以“MMM DD YYYY”格式的字符字面量表示。 |
__TIME__ | 当前时间,以“HH:MM:SS”格式的字符字面量表示。 |
__FILE__ | 包含当前文件名,作为字符串字面量。 |
__LINE__ | 包含当前行号,作为十进制常量。 |
__STDC__ | 当编译器符合ANSI标准时定义为1。 |
示例
让我们尝试以下示例:
#include <stdio.h> int main(){ printf("File: %s\n", __FILE__ ); printf("Date: %s\n", __DATE__ ); printf("Time: %s\n", __TIME__ ); printf("Line: %d\n", __LINE__ ); printf("ANSI: %d\n", __STDC__ ); }
输出
运行此代码时,将产生以下输出:
File: main.c Date: Mar 6 2024 Time: 13:32:30 Line: 8 ANSI: 1
预处理器运算符
C预处理器提供以下运算符来帮助创建宏:
示例:C语言中的宏续接符(\)运算符
宏通常限制在一行内。宏续接符(\)用于继续过长而无法在一行中容纳的宏。请看下面的例子:
#include <stdio.h> #define message_for(a, b) \ printf(#a " and " #b ": We love you!\n") int main() { message_for(Ram, Raju); }
输出
运行此代码时,将产生以下输出:
Ram and Raju: We love you!
示例:C语言中的字符串化(#)运算符
字符串化运算符(#),也称为井号运算符,在宏定义中使用时,会将宏参数转换为字符串常量。
字符串化运算符只能用于具有指定参数列表的宏。例如:
#include <stdio.h> #define message_for(a, b) \ printf(#a " and " #b ": We love you!\n") int main() { message_for(Carole, Debra); return 0; }
输出
运行代码并检查其输出:
Carole and Debra: We love you!
示例:C语言中的标记粘贴(##)运算符
宏定义中的标记粘贴运算符(##)组合两个参数。它允许宏定义中的两个单独标记连接成一个标记。例如:
#include <stdio.h> #define tokenpaster(n) printf ("token" #n " = %d", token##n) int main() { int token34 = 40; tokenpaster(34); return 0; }
输出
运行此代码时,将产生以下输出:
token34 = 40
示例:C语言中的defined()运算符
预处理器defined运算符用于常量表达式中,以确定是否使用#define定义了标识符。如果指定了标识符,则值为true(非零)。如果符号未定义,则值为false(零)。
以下示例演示如何在C程序中使用defined运算符:
#include <stdio.h> #if !defined (MESSAGE) #define MESSAGE "You wish!" #endif int main() { printf("Here is the message: %s\n", MESSAGE); return 0; }
输出
运行代码并检查其输出:
Here is the message: You wish!
C语言中的参数化宏
CPP的强大功能之一是能够使用参数化宏来模拟函数。例如,我们可能有如下计算平方数的代码:
int square(int x) { return x * x; }
我们可以使用宏重写上述代码,如下所示:
#define square(x) ((x) * (x))
带参数的宏必须在使用之前使用#define指令定义。参数列表括在括号中,并且必须紧跟在宏名之后。宏名和左括号之间不允许有空格。
示例
以下示例演示如何在C语言中使用参数化宏:
#include <stdio.h> #define MAX(x,y) ((x) > (y) ? (x) : (y)) int main() { printf("Max between 20 and 10 is %d\n", MAX(10, 20)); return 0; }
输出
运行此代码时,将产生以下输出:
Max between 20 and 10 is 20