C Primer Plus--C预处理器和C库(2)
#include指令
- #include <头文件.h>//在标准系统目录中寻找头文件
- #include "头文件.h"//先在当前目录下面寻找头文件,然后在标准系统目录下寻找头文件
预处理器在碰到#inclide
指令时,就会将后面附加的头文件的内容包含到当前文件中。
头文件
后缀为.h
的文件是头文件。这类文件经常包含预处理器所需要的语句。
头文件内容最常见形式包括:
- 明显常量
- 宏函数
- 函数声明
- 结构模板定义
- 类型定义
其他指令
#undef
#undef
指令取消一个给定的已定义#define
.
#define FF 10
#undef FF //FF定义被取消
条件编译
#ifdef
#else
#endif
#ifdef
后面的标识符如果已经定义了,那么执行它的分支控制的指令,知道下一个#else
或#endif
为止。如果有#else
存在,那么在未定义时会执行#else
知道#endif
之间的所有指令。
#define FF 10
#ifdef FF
#define AA 100
#else
#define AA 1000
#endif
int main() {
printf("FF :%d\n",AA);
}
#ifndef
#ifndef
是#ifdef
的反义词,它是用来确定后面的表示符未被定义。通常用来定义此前未被定义的常量。
#ifndef FF
#define FF 199
#endif
int main() {
printf("FF :%d\n",FF);
}
#ifndef
也可以用于防止多次包含同一文件。
/*
*假设这个头文件是test.h
*/
//原本头文件的内容直接写,现在我们把头文件的内容放到下面这样的结构里:
#ifndef _TEST_H
#define _TEST_H
/*
*#define和#endif之间存放头文件的所有内容
*/
#endif
来看看上面这段代码什么意思:
首先,明确我们的目的:防止一个文件被多次的include。为什么会存在多次include呢?因为可能一个文件会include多个文件,而不同文件有可能include同一个文件,而在一个文件中只能对一个文件中include一次。举个栗子:存在a.h
,b.h
,test.h
,有可能a.h
中有一个#include "test.h"
,同时b.c
中有两行
#include "a.h"
#include "test.h"
这样就出现了b.h
被包含了两次,而C是不允许这样的。上面用到#ifndef
就解决了这个问题。
当test.h
未被定义,也就是说这个头文件被第一次包含了,会定义一个_TEST_H
的常量,但没有body,因为我们不需要这个常量具有body。这时候头文件里的所有内容被预处理器执行。当test.h
被第二次包含时,预处理器检测到已经存在_TEST_H
这个量,就会跳过,也就不会重复执行头文件里的内容了。这里的关键是保持_TEST_H
的唯一。我们这里用头文件的文件名做标识符,将文件名前面加下划线,并将点换成下划线,能够保证这个标识符不重复。但这种做法是编译器提供商为了标准头文件采用的方法,我们平时自己写的时候要想防止与标准头文件冲突,就不要按这种方法来取标识符。例如可以把下划线加到文件名后面等等。
#if
和#elif
#if
、#elif
后面跟常量整数表达式,当这个表达式值为非0值时为真,为0位假,类似于C程序中的if-else
分支结构。可以应用C的关系运算符以及逻辑运算符。
#if defined(FF)
#define SS 9999
#elif defined(_TEST_H)
#define EE 99
#endif
预定义宏
一些常见的预定义宏:
宏 | 意义 |
---|---|
__DATE__ | 进行预处理操作的日期字符串 |
__FILE__ | 当前源代码文件名的字符串 |
__LINE__ | 当前所在代买行号 |
__TIME__ | 源文件编译时间 |
__STDC_VERSION__ | 当前编译器遵循的C标准版本,当为C99时值为199901L |
#line
和#error
#line
指令用于重置由__LINE__
和__FILE__
宏报告的行号和文件名。
使用方法:
#line 100 //当前行号重置为100
#line 10 "test.c" //把行号重置为10,文件名重置为test.c
printf("%d\n",__LINE__);
#line 1000
printf("%d\n",__LINE__);
printf("%d\n",__LINE__);
#error
指令使预处理器发出一条错误消息,该消息包含指令中的文本。可能的话,编译过程应该中断。
#if __STDC_VERSION__ != 199901L
#error Not C99
#endif
内联函数
内联函数主要是为了把解决函数调用过程中建立调用、传递参数、跳转到函数代码段返回耗费的时间较长的问题。它适用于较短代码块,创建内联函数需要用到inline
关键字。
inline double square(double x);
double square(double x){
return x*x;
}
int main() {
double a = square(3);
printf("%lf\n",a);
}
//内联函数这里其实把函数体直接写到了main里面,不用调用了。
/*
*int main(){
* double a = a * a;
* printf("%lf\n",a);
*}
*/
无法获取内联函数的地址,因为内联函数并没有被分配单独的空间。内联函数适用于短小函数,长的函数调用造成的时间远远小于函数体执行时间,使用内联函数并没什么意义。
内联函数的定义和对该函数的调用必须在同一文件中。因此,内联函数一般具有内部链接。在多文件程序中,要想使用相同的内联函数,必须在每个文件中对内联函数进行定义。为了方便,可以把内联函数的定义放在头文件中。
C Primer Plus--C预处理器和C库(2)的更多相关文章
- C Primer Plus--C预处理器和C库(1)
目录 预处理符号 明显常量 #define 编译程序之前,先由预处理器检查程序(因此称为预处理器).根据程序中使用的预处理器指令,预处理用符号缩略语所代表的内容替换程序中的缩略语. 预处理器可以根据你 ...
- C Primer Plus之C预处理器和C库
编译程序前,先由预处理器检查程序(因此称为预处理器).根据程序中使用的预处理器指令,预处理器用符号缩略语所代表的内容替换程序中的缩略语. 预处理器不能理解C,它一般是接受一些文件并将其转换成其他文本. ...
- C预处理器和C库
#define #include #undef #ifdef #else #endif #if #elif #else #endif 预处理宏: p463 _ _fun_ _是预定义标识符(函数作用域 ...
- 第 16 章 C 预处理器和 C 库(可变参数:stdarg.h)
/*------------------------------------------------- varargs.c -- use variable number of arguments -- ...
- 第 16 章 C 预处理器和 C 库(string.h 库中的 memcpy() 和 memmove())
/*----------------------------------------- mems.c -- 使用 memcpy() 和 memmove() ---------------------- ...
- 第 16 章 C 预处理器和 C 库(qsort() 函数)
/*---------------------------------------- qsorter.c -- 用 qsort() 排序一组数字 --------------------------- ...
- 第 16 章 C 预处理器和 C 库(直角坐标转换极坐标)
/*------------------------------------- rect_pol.c -- 把直角坐标转换为极坐标 ---------------------------------- ...
- 第 16 章 C 预处理器和 C 库(预定义宏)
/*------------------------------------- predef.c -- 预定义宏和预定义标识符 ------------------------------------ ...
- 第 16 章 C 预处理器和 C 库(条件编译)
/*-------------------------------------- names_st.h -- names_st 结构的头文件 ----------------------------- ...
随机推荐
- CentOS7.6离线安装docker
2019/10/24,docker 摘要:CentOS 7.6中离线安装docker 18.06.3以及docker-compose 1.24.1 在线安装可参照 文档 所需环境 1.CentOS 7 ...
- Golang/Go goroutine调度器原理/实现【原】
Go语言在2016年再次拿下TIBOE年度编程语言称号,这充分证明了Go语言这几年在全世界范围内的受欢迎程度.如果要对世界范围内的gopher发起一次“你究竟喜欢Go的哪一点”的调查,我相信很多Gop ...
- 常用正则表达式和一些demo
一.校验数字的表达式 数字:^[0-9]*$ n位的数字:^\d{n}$ 至少n位的数字:^\d{n,}$ m-n位的数字:^\d{m,n}$ 零和非零开头的数字:^(0|[1-9][0-9]*)$ ...
- rabbitmq监控之消息确认ack
rabbitmq springboot ack 监控 一.测试环境 二.启动测试 一直以来,学习rabbitmq都是跟着各种各样的教程.博客.视频和文档,撸起袖子就是干!!!最后,也成功了. 当然,成 ...
- Java自学-类和对象 继承
什么是 Java的 继承 ? 在LOL中,武器是物品的一种,也是有名称和价格的 所以在设计类的时候,可以让武器继承物品,从而继承名称和价格属性 步骤 1 : 物品类Item 物品类Item 有属性 n ...
- 【转载】C#中List集合使用Max()方法查找到最大值
在C#的List集合操作中,有时候需要查找到List集合中的最大值,此时可以使用List集合的扩展方法Max方法,Max方法有2种形式,一种是不带任何参数的形式,适用于一些值类型变量的List集合,另 ...
- 《区块链DAPP开发入门、代码实现、场景应用》笔记5——区块链福利彩票的设计
笔者一直强调,一定要利用区块链的特点来解决行业存在的问题,并且该问题最好用区块链解决或者说只能用区块链解决.彩票行业就是个例子. 在讲解代码之前,首先讲解一下业务设计,如图6.15所示. 图6.15 ...
- 移动端H5长按事件 vue自定义指令
import Vue from 'vue' Vue.directive('longpress', function (el, binding){ var timer = null; var start ...
- iview blur事件
在iview中,失去焦点的写法是: @on-blur="editTitle(item, index)"
- centos7.6初始化python3.6环境
环境: CentOS Linux release 7.6.1810 (Core) Python3.6.x 01.检测yum源 wget -O /etc/yum.repos.d/epel.repo ht ...