预处理
预处理指令
宏定义
#define
无参数
#define PI 3.14
有参数
可以在一对括号"()"内指定参数。如果有多个参数的话,用逗号","分隔。
#define ADD1(a) a
#define ADD2(a, b) (a + b)
ADD1(1)
ADD2(1, 2)
调用时参数个数要对应,有几个参数就必须传几个参数。否则将报错:
error: macro "ADD" requires 2 arguments, but only 1 given
error: ‘ADD’ undeclared (first use in this function)
括号"()"内也可以没有参数,表示无参数
#define ADD0() 0
但在调用时也要有括号:
ADD0()
如果没有括号:
ADD0
将会报错:
error: ‘ADD0’ undeclared (first use in this function)
这跟上面的无参数的宏不一样,其实是两个不一样的宏。
和函数一样,宏定义也不允许重载
#define ADD(a, b) (a + b)
#define ADD(a, b, c) (a + b + c)
这样会提示警告:
warning: "ADD" redefined
ADD(1, 2)
ADD(1, 2, 3)
如果使用的话会报错:
error: macro "ADD" requires 3 arguments, but only 2 given
error: ‘ADD’ undeclared (first use in this function)
上面其实后面的重复宏定义已经将前面的宏定义覆盖了。使用的时候按照最后的宏定义使用就不会报错了:
//ADD(1, 2);
ADD(1, 2, 3);
#undef
和#define对应,#define用于定义宏,#undef用于取消宏定义。
__FILE__
__LINE__
__FUNCTION__
__func__
__DATE__
__TIME__
__VA_ARGS
__VA_ARGS__
操作符
宏定义中有两个比较特殊的操作符。
#和##用于对宏参数字符串化和连接操作。
#
#define __LOG0__(F, L, FORMAT) \ do { \ printf((F ":" "(" #L ")" " " FORMAT "\n")); \ } while (0)
__LOG0__(__FILE__, 50, "hello, c"); string_test_3.c:(50) hello, c
__LOG0__(__FILE__, __LINE__, "hello, c"); string_test_3.c:(__LINE__) hello, c
#define __FORMAT__(F, L, FORMAT) (F ":" "(" #L ")" " " FORMAT "\n") #define LOG0(F, L, FORMAT) \ do { \ printf(__FORMAT__(F, L, FORMAT)); \ } while (0)
LOG0(__FILE__, __LINE__, "hello, c"); string_test_3.c:(53) hello, c
##
#define ____R____0_1(CB, LEAD, ARGS...) #define ____R____1_1(CB, LEAD, ARGS...) CB(LEAD) ____R____0_1(CB, ARGS) #define ____R____2_1(CB, LEAD, ARGS...) CB(LEAD) ____R____1_1(CB, ARGS) #define ____R____3_1(CB, LEAD, ARGS...) CB(LEAD) ____R____2_1(CB, ARGS) #define ____R____4_1(CB, LEAD, ARGS...) CB(LEAD) ____R____3_1(CB, ARGS) #define ____R____5_1(CB, LEAD, ARGS...) CB(LEAD) ____R____4_1(CB, ARGS) #define ____R____(CB, N, LEAD, ARGS...) ____R____##N##_1(CB, LEAD, ARGS) #define ____R_WITH_CB____(CB, N, ARGS...) ____R____(CB, N, ARGS)
#define FN(ARG) ARG int main(int argc, char **argv) { printf(____R_WITH_CB____(FN, 0) "\n"); printf(____R_WITH_CB____(FN, 1, "A") "\n"); printf(____R_WITH_CB____(FN, 2, "A", "B") "\n"); printf(____R_WITH_CB____(FN, 3, "A", "B", "C") "\n"); printf(____R_WITH_CB____(FN, 4, "A", "B", "C", "D") "\n"); printf(____R_WITH_CB____(FN, 5, "A", "B", "C", "D", "E") "\n"); return 0; }
#define FN2(FMT) printf(FMT "\n"); ____R_WITH_CB____(FN2, 1, "A"); ____R_WITH_CB____(FN2, 2, "A", "B"); ____R_WITH_CB____(FN2, 3, "A", "B", "C"); ____R_WITH_CB____(FN2, 4, "A", "B", "C", "D"); ____R_WITH_CB____(FN2, 5, "A", "B", "C", "D", "E");
#line
#line预处理指令将影响__FILE__, __LINE__的值。
#define __FORMAT__(F, L, FORMAT) (F ":" "(" #L ")" " " FORMAT "\n") #define LOG0(F, L, FORMAT) \ do { \ printf(__FORMAT__(F, L, FORMAT)); \ } while (0)
LOG0(__FILE__, __LINE__, "hello, c"); #line 99 __FILE__ LOG0(__FILE__, __LINE__, "hello, c"); LOG0(__FILE__, __LINE__, "hello, c"); LOG0(__FILE__, __LINE__, "hello, c"); LOG0(__FILE__, __LINE__, "hello, c"); #line 67 __FILE__ LOG0(__FILE__, __LINE__, "hello, c"); string_test_3.c:(58) hello, c string_test_3.c:(99) hello, c string_test_3.c:(100) hello, c string_test_3.c:(101) hello, c string_test_3.c:(102) hello, c string_test_3.c:(67) hello, c
实现函数回调的宏定义
#define FX(CB, FMT) CB(FMT) #define FN(ARG) printf(ARG)
FX(FN, "222\n"); 222
也可以去回调函数
#define FX(CB, FMT) CB(FMT) #define FN(ARG) printf(ARG) void fn2(char* arg) { printf(arg); }
FX(fn2, "333\n"); 333
头文件包含
#include
头文件包含有两种形式
#include<stdio.h>
#include"stdio.h"
这两种形式在搜索头文件路径顺序是不同的。尖括号不是操作符,仅仅只是一个分界符。双引号也是一样。尖括号和双引号包含的文件名不是字符串,就是一个字符串字面量(string literal)。
搜索路径
编译器在实现的时候,对于include的头文件,会按照规定的搜索路径查找头文件。
可以参考gcc的搜索路径
可以通过以下命令查看头文件搜索路径
$ cpp -v /dev/null -o /dev/null
#include "..." search starts here:
#include <...> search starts here:
/usr/local/include
/Users/admin/Downloads/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.0.0/include
/Users/admin/Downloads/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
/usr/include
/System/Library/Frameworks (framework directory)
/Library/Frameworks (framework directory)
End of search list.
也可以通过以下方式查看头文件搜索路径
$ `gcc -print-prog-name=cc1` -v
忽略不存在的目录“/usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../../i686-pc-cygwin/include”
#include "..." 搜索从这里开始:
#include <...> 搜索从这里开始:
/usr/local/include
/usr/lib/gcc/i686-pc-cygwin/4.5.3/include
/usr/lib/gcc/i686-pc-cygwin/4.5.3/include-fixed
/usr/include
搜索列表结束。
如果是C++的话
$ `gcc -print-prog-name=cc1plus` -v
忽略不存在的目录“/usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../../i686-pc-cygwin/include”
#include "..." 搜索从这里开始:
#include <...> 搜索从这里开始:
/usr/lib/gcc/i686-pc-cygwin/4.5.3/include/c++
/usr/lib/gcc/i686-pc-cygwin/4.5.3/include/c++/i686-pc-cygwin
/usr/lib/gcc/i686-pc-cygwin/4.5.3/include/c++/backward
/usr/local/include
/usr/lib/gcc/i686-pc-cygwin/4.5.3/include
/usr/lib/gcc/i686-pc-cygwin/4.5.3/include-fixed
/usr/include
搜索列表结束。
预处理条件判断
这也是一种包含,表示条件包含。通过此指令可以将符合条件的内容包含到源程序中。也通常通过此指令将符合条件的代码直接inline到函数中。
#if
#ifdef
#elif
#else
#endif
defined
在#if,#elif中可以通过defined判断指定的宏是否定义。等同于#ifdef。
错误告警
#warning
#error
#pragma
#pragma message string
#pragma GCC warning message
#pragma GCC error message
相关推荐
在C语言程序设计中,编译预处理是一个重要的步骤,它在正式编译源代码之前进行,主要包括宏定义、文件包含和条件编译等操作。本章主要关注的是宏定义,分为无参宏定义和带参宏定义。 无参宏定义是通过`#define`...
第一章:C语言程序设计概述 2课时 第二章:基本数据类型与表达式 4课时 ...第八章:指针 8课时 第九章:结构体数据类型与链表 6课时 第十章:共用体与枚举类型 4课时 第十一章:文件
C语言第8章编译预处理与位运算习题集答案解析 本文档提供了C语言第8章编译预处理与位运算习题集的答案解析,涵盖了宏定义、文件包含、条件编译、位运算等知识点。 一、选择题 1. 在宏定义#define A 3.897678中,...
在C语言中,编译预处理是一个重要的环节,它在正式编译程序之前对源代码进行处理,主要包括宏定义、文件包含和条件编译等操作。编译预处理指令以"#"号开头,用于扩展C语言的功能,增强程序的可读性、可维护性和可...
在C语言中,编译预处理是程序编译过程中的第一步,它主要负责处理源代码中的特定指令,这些指令在实际编译之前被执行。本章主要涵盖了三个关键知识点:宏定义及其调用、文件包含处理以及条件编译指令。 1. **宏定义...
《C语言程序设计教程第8章预处理》深入解析 C语言编程中,预处理是程序编译过程的重要环节,它在正式的编译和链接之前先对源代码进行处理,主要包括宏定义、文件包含和条件编译等功能,极大地提高了程序的可读性...
8. 语法检查:尽管C语言的语法检查较为宽松,如不检查数组下标越界,这可能导致一些潜在的错误,但也给予了程序员更高的灵活性。 C语言程序通常由预处理、编译、链接三个阶段组成。例如: ```c #include "stdio.h...
第 8 章 预处理命令是 C 语言编程中的一个重要概念,它涉及到程序在实际编译之前的一些预备处理工作。预处理命令主要由宏定义、文件包含和条件编译三部分组成,这些功能使得程序员能够更灵活地编写和管理代码。 1. ...
C语言全套资料 C语言程序设计 C语言算法 C语言课件 C语言顺序程序设计 C语言数组 C语言循环控制 C语言预处理命令 C语言文件...第八章 函数 第九章编译预处理命令 第十章 指针 第十一章 结构体与共用体 第十三章 文件
### 数据库系统概论:第八章SQL数据库编程 #### 8.1 嵌入式SQL 嵌入式SQL是一种将SQL语句嵌入到高级程序设计语言中的技术,允许开发人员结合SQL的强大数据处理能力与宿主语言的流程控制功能。这种方式克服了SQL...
C语言程序设计(第三版)高禹 电子课件
C程序的格式和结构通常包括预处理指令、声明、函数定义和调用等部分。一个典型的C程序通常包含头文件、变量声明、函数定义和主函数。程序的运行过程一般分为编辑、编译、链接和执行四个步骤。 学习C语言,需要注重...
第8章 编译预处理主要探讨的是在C语言编程中,如何在编译之前对源代码进行处理,以优化和准备源程序的编译过程。这个过程涉及到宏定义、文件包含和条件编译三个主要方面。 8.1 宏定义与符号常量 宏定义是C语言中一...
《数据库系统概论:第8章 数据库编程》这一章主要介绍了在数据库系统中如何进行编程,特别是关于SQL的使用方式。SQL(Structured Query Language)是用于管理关系数据库的标准语言,它分为交互式和嵌入式两种使用...
第1章 C语言概述 ...第8章 函数 第9章 预处理命令 第10章 指针 第11章 结构体与共用体 第12章 位运算 第13章 文件 第14章 C++对C的扩充 第15章 C++的面向对象基础 第16章 常见错误和程序调试
第一章: C语言概论 第二章: 数据类型、运算符、表达式 第三章: C语言程序设计初步 第四章: 数组 第五章:函数 第六章:指针 ...第八章:枚举,位运算 第九章: 预处理 第十章: 文件 +100例经典c程序
数据预处理包括去除噪声、异常值检测和基线解算,最后进行质量检核和技术总结,以确保测量结果的准确性和有效性。 总结来说,GPS测量的设计与实施是一个系统的过程,需要综合考虑技术规范、精度要求、基准转换和...
内容: 第一章:c语言程序设计预备知识 第二章:c语言程序设计基础 第三章:基本数据类型、运算符与表达式 ...第八章:函数 第九章:指针 第十章:预处理命令 第十一章:复杂数据类型 第十二章:文件 实验
在C语言程序设计的学习过程中,第二章通常会涵盖基础语法和基本编程概念。这份"第二章习题参考答案"提供了对这些概念的实践应用解析,旨在帮助学习者巩固理论知识,提升编程技能。以下是根据标题、描述和标签提取的...
本文是txt文本里面有网盘永久链接视频,C语言基础,包括:002第二章 数据类型,运算符和表达式01,007第三章 顺序...函数01,042第八章 指针02(新版),052第九章 预处理03(新版),065第十一章 位运算02(新版),一共11章