#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)的更多相关文章

  1. C Primer Plus--C预处理器和C库(1)

    目录 预处理符号 明显常量 #define 编译程序之前,先由预处理器检查程序(因此称为预处理器).根据程序中使用的预处理器指令,预处理用符号缩略语所代表的内容替换程序中的缩略语. 预处理器可以根据你 ...

  2. C Primer Plus之C预处理器和C库

    编译程序前,先由预处理器检查程序(因此称为预处理器).根据程序中使用的预处理器指令,预处理器用符号缩略语所代表的内容替换程序中的缩略语. 预处理器不能理解C,它一般是接受一些文件并将其转换成其他文本. ...

  3. C预处理器和C库

    #define #include #undef #ifdef #else #endif #if #elif #else #endif 预处理宏: p463 _ _fun_ _是预定义标识符(函数作用域 ...

  4. 第 16 章 C 预处理器和 C 库(可变参数:stdarg.h)

    /*------------------------------------------------- varargs.c -- use variable number of arguments -- ...

  5. 第 16 章 C 预处理器和 C 库(string.h 库中的 memcpy() 和 memmove())

    /*----------------------------------------- mems.c -- 使用 memcpy() 和 memmove() ---------------------- ...

  6. 第 16 章 C 预处理器和 C 库(qsort() 函数)

    /*---------------------------------------- qsorter.c -- 用 qsort() 排序一组数字 --------------------------- ...

  7. 第 16 章 C 预处理器和 C 库(直角坐标转换极坐标)

    /*------------------------------------- rect_pol.c -- 把直角坐标转换为极坐标 ---------------------------------- ...

  8. 第 16 章 C 预处理器和 C 库(预定义宏)

    /*------------------------------------- predef.c -- 预定义宏和预定义标识符 ------------------------------------ ...

  9. 第 16 章 C 预处理器和 C 库(条件编译)

    /*-------------------------------------- names_st.h -- names_st 结构的头文件 ----------------------------- ...

随机推荐

  1. win add static arp

    win add static arp > arp -s "192.168.0.2" "00-0a-35-01-fe-c0" > arp -a | f ...

  2. RabbitMQ知识梳理

    RabbitMQ 基本概念 交换机类型: RabbitMQ 运转流程: AMQP协议 入门使用 安装环境: 交换机和队列: 进阶使用 消息去从 消息确认投递 消息防止丢失 过期时间 (TTL) 消息分 ...

  3. Java调用Http/Https接口(6)--RestTemplate调用Http/Https接口

    RestTemplate是Spring提供的用于访问Http接口的客户端,提供同步的API:在将来的Spring版本中可能会过时,将逐渐被WebClient替代.文中所使用到的软件版本:Java 1. ...

  4. jquery datatable如何动态分页

    展开全部 一.分页 分页的基本思想是根据datatable的页码及每页显示的行数,将数据从数据库分段提出,然后再填充到表格中,以达到分页的效果. 这里需要用到datatable插件的几个属性: &qu ...

  5. Jmeter学习笔记(二十)——后置处理器XPath Extractor使用

    一.背景 在使用过程某些操作步骤与其相邻步骤存在一定的依赖关系,需要需要将上一个请求的响应结果作为下一个请求的参数. Jmeter中后置处理器正则表达式提取器和XPath Extractor都可以将页 ...

  6. 在Spring中使用AspectJ实现AOP

    在Spring中,最常用的AOP框架是AspectJ,使用AspectJ实现AOP有2种方式: 基于XML的声明式AspectJ 基于注解的声明式AspectJ 基于XML的声明式AspectJ 1. ...

  7. Linu如何查看磁盘占用情况及处理办法

    free -h: 查看当前剩余的内存大小 df: 查看文件系统磁盘使用率,可能free -h得到的剩余空间还有很多,但是df查询得到的部分文件系统磁盘使用率较高 当发现磁盘使用率较高的时候,可以: 先 ...

  8. Eclipse搭建maven web项目

    最近在做做一个小实验,搭建ssm框架,要求使用maven来统一管理jar包,接下来就看如何建立maven项目,首先必须有要有相应的开发环境:JDK和maven,以及配置tomcat. 开发环境搭建可以 ...

  9. luoguU60884 【模板】动态点分治套线段树

    题目连接:https://www.luogu.org/problemnew/show/U60884 题意:有N个点,标号为1∼N,用N−1条双向带权通道连接,保证任意两个点能互相到达. Q次询问,问从 ...

  10. BZOJ-1975: 魔法猪学院 (K短路:A*+SPFA)

    题意:有N种化学元素,有M种转化关系,(u,v,L)表示化学物质由u变为v需要L能量,现在你有E能量,问最多有多少种不同的途径,使得1转为为N,且总能量不超过E. 思路:可以转为为带权有向图,即是求前 ...