文档章节

第16章 C预处理器和C库 16.5 文件包含: #include

idreamo
 idreamo
发布于 2017/07/22 07:42
字数 1678
阅读 20
收藏 0

预处理器发现#includ指令后,就会寻找后跟的文件名并把这个文件的内容包含到当前文件中

被包含文件中的文本将替换源代码文件中的#include指令,就像您把被包含文件中的全部内容键入到源文件中的这个特定位置一样。

#include 指令的两种使用形式

#include <stdio.h> 文件名放在尖括号中
#include "mystuff.H" 文件名放在双引号中

在UNIX系统中,尖括号告诉预处理器在一个或多个标准系统目录中寻找文件。双引号告诉预处理器先在当前目录(或文件名指定的其他目录)中寻找文件,然后在标准位置寻找文件。

使用#include指令的一些例子

#include <stdio.h> 搜索系统目录
#include  "hot.h" 搜索当前目录
#include  "/usr/biff/p.h" 搜索/usr/biff目录

对于系统头文件,集成开发环境(IDE)具有标准搜索路径。许多集成开发环境提供菜单选项用于指定使用尖括号时搜索的其他路径。对于UNIX,使用双引号意味着首先搜索本地目录,但是具体搜索哪个目录依赖于编译器。有些编译器搜索源代码所在目录,有些搜索当前工作目录,还有些搜索工程文件所在目录

一般而言,命名文件的方法依赖于系统,但是尖括号与双引号的使用则与系统无关。

习惯上使用后缀.h表示头文件(header file),这类文件包含置于程序头部的信息。头文件经常包含预处理器语句。有些头文件(如stdio.h)由系统提供。您也可以自由创建您自己的头文件。

包含大型头文件并不一定显著增加程序的大小。很多情况下,头文件中的内容是编译器产生最终代码所需的信息,而不是加到最终代码里的具体语句。

16.5.1  头文件:一个实例

假设您要开发一个存放人名的结构,并编写一些使用该结构的函数。您可以在一个头文件中集中进行各种声明。程序清单16.6给出了这个例子:

//names_st.h -- names_st 结构的头文件
//常量
#define SLEN 32

//结构声明
struct names_st
{
    char first[SLEN];
    char last[SLEN];
}

//类型定义
typedef struct names_st names; 

//函数原型定义
void get_names(names *);
void show_names(const names *);

这个文件中含有在头文件中常见的许多内容:#define指令、结构声明、typedef语句和函数原型。

注意这些内容都不是可执行代码,而是编译器用于产生可执行代码的信息。

可执行代码通常在源代码文件中,而不在头文件中。例如,程序清单16.7显示了头文件中函数原型的函数定义。源文件包含了头文件,因此编译器将知道关于names类型的信息。

程序清单16.7  names_st.c源文件

//names_st.c --定义names_st函数
#include <stdio.h>
#include "names_st.h"  //包含头文件

//函数定义
void get_names(names *pn)
{
    int i;

    printf("Please enter your first name: ");
    fgets(pn->first,SLEN,stdin);
    i=0;
    while(pn->first[i] != '\n' && pn->first[i] != '/0')
        i++;
    if(pn->first[i]=='\n')
        pn->first[i]='\0';
    else 
        while(getchar()!='\n')
            continue;

    printf("Please enter your last name: ");
    fgets(pn->last,SLEN,stdin);
    i=0;
    while(pn->last[i] != '\n' && pn->last[i] != '\0')
        i++;
    if(pn->last[i] == '\n')
        pn->last[i]='\0';
    else
        while(getchar()!='\n')
              continue;
}

void show_names(const names * pn)
{
    printf("%s %s\n",pn->first,pn->last);
}

get_names()函数使用fgets()以避免目标数组溢出。如果被保存的字符串中存在换行符,则用空字符代替换行符。如果没有换行符,则表明fgets()在到达行尾之前就被停止,代码会去掉该行中剩余的输入。

程序清单16.8是使用前面的头文件和源代码文件的示例程序。

程序清单16.8  useheader.c程序

//useheader.c --使用names_st结构
#include <stdio.h>
#include "names_st.h"
//记住链接names_st.c文件

int main(void)
{
    name candidate;
    get_names(&candidate);
    printf("Let's welcome ");
    show_names(&candidate);
    printf(" to this program!\n");

    return 0;
}

下面是一个运行示例:

Please enter your first name: Ian
Please enter your last name: Smersh
Let's welcome Ian Smersh to this program!

关于该程序,应该注意以下几点:

**两个源代码文件都使用了names_st结构,因此,它们都必须包含头文件names_st.h。

**需要编译、链接names_st.c和useheader.c两个源代码文件。

**声明以及类似语句放在头文件names_st.h中,函数定义放在源代码文件names_st.c中。

16.5.2  使用头文件

头文件内容的最常见形式包括:

**明显常量--例如,典型的stdio.h文件定义EOF、NULL和BUFSIZE(标准I/O缓冲区的大小)。

**宏函数--例如,getchar()通常被定义为getc(stdin),getc( )通常被定义为较复杂的宏,而头文件ctype.h通常包含ctype函数的宏定义。

**函数声明--例如,头文件string.h包含字符串函数系列的函数声明。在ANSI C中,声明采用函数原型形式。

**结构模板定义--标准I/O函数使用FILE结构,该结构包含文件及文件相关缓冲区的信息。头文件stdio.h中存放FILE结构的声明。

**类型定义--可以使用指向FILE的指针作为参数调用I/O函数。通常,stdio.h用#define或typedef使得FILE代表指向FILE结构的指针。与之类似,size_t和time_t类型也在头文件中定义。

另外,可以使用头文件来声明多个文件共享的外部变量。此时,可以在包含函数声明的源代码文件中定义一个具有文件作用域、外部链接的变量。

int status=0;    //文件作用域,源代码文件中

接着,可以在与源代码文件相关联的头文件中进行引用声明

extern int status;  //头文件中

该行代码会出现在包含了头文件的任何文件中并使得使用这个函数系列的文件可以使用该变量。在源代码文件中包含该头文件后该声明也会出现但是只要声明类型一致,那么在同一文件中使用定义声明和引用声明就不会出现问题。

需要包含头文件的另一种情况是:使用具有文件作用域、内部链接和const限定词的变量或数组。使用const可以避免值被意外改变。使用static后,每个包含该头文件的文件都获得一份该常量的副本。

© 著作权归作者所有

idreamo
粉丝 18
博文 139
码字总数 224743
作品 0
青岛
产品经理
私信 提问
C Primer Plus 第11章 11.7 ctype.h字符函数和字符串

第7章“C控制语句 分支和跳转”介绍了ctype.h系列字符相关的函数。这些函数不能被 应用于整个字符串,但是可以被应用于字符串中的个别字符。程序清单11.26定义了一个函数,它把toupper( )函数...

idreamo
2016/08/27
29
0
libCURL开源库在VS2010环境下编译安装,配置详解

CURL开源库VS2010环境下编译安装,配置详解 一 准备 1.1 CURL官网下载地址:http://curl.haxx.se/download.html 1.2 找到源码包,我这里下载的是7.32.0版:http://curl.haxx.se/download/cu...

goodslaver
2014/02/10
0
0
#define 中的“ # 运算符”和“ ## 运算符”

利用宏参数创建字符串:# 运算符 在类函数宏(function-like macro)的替换部分中,“#”符号用作一个预处理运算符,它可以把语言符号(token)转化为字符串。例如,如果 x 是一个宏参量,那...

TMDJoJo
2012/07/07
0
0
Objective-C中的预处理器指令与宏

引 什么是预处理器,跟我有什么关系? 预处理器是在OC源文件编译过程中的一个部分,而且是第一个处理部分,预处理器的预也由此可见。 整个编译过程可以大致分为:预处理器进行词法分析 -> 语...

cloudox_
2017/04/26
0
0
三、第一个C程序代码分析

说明:这个C语言专题,是学习iOS开发的前奏。也为了让有面向对象语言开发经验的程序员,能够快速上手C语言。如果你还没有编程经验,或者对C语言、iOS开发不感兴趣,请忽略 在上一篇中我们已经...

长平狐
2013/03/28
352
0

没有更多内容

加载失败,请刷新页面

加载更多

解决问题&发现问题

作为一个程序员非常重要的的能力就是解决问题的能力,当然除了解决问题之外,还有一个经常被疏忽的能力-发现问题的能力。 解决问题 一套有效的解决问题的能力非常重要,下面是一个解决问题的...

Lubby
25分钟前
6
0
Leetcode PHP题解--D104 167. Two Sum II - Input array is sorted

D104 167. Two Sum II - Input array is sorted 题目链接 167. Two Sum II - Input array is sorted 题目分析 给定一个已经排序好的整数数组,从中寻找两个数字,使其相加之后等于给定的一个...

skys215
32分钟前
5
0
IntelliJ IDEA Spring Boot 2.x 多模块项目创建

在学习Spring Boot 2的时候顺便来学习创建下Maven下的多模块项目创建。方便学习使用整套开发流程。 第一步,检查IDEA,新版本的IDEA可能没有Spring Assistant可通过插件安装(Preferences->P...

被猪拱了的JAVA
32分钟前
6
0
Java运行状态分析2:获取线程堆栈信息

Java运行状态分析2:获取线程堆栈信息 基本概念 出现内存泄漏或者运行缓慢场景,有时候无法直接从业务日志看出问题时候,需要分析jvm内存和线程堆栈 线程堆栈信息主要记录jvm线程在某时刻线程...

indi_yugj
32分钟前
12
0
解决java编译错误:编码GBK的不可映射字符

https://www.cnblogs.com/charleswong/p/8481593.html 新建java文件,存储时Encoding选择了UTF-8, 由于语句中包含中文,javac编译时报错,提示"编码GBK的不可映射字符": 解决办法: 方法一...

时刻在奔跑
39分钟前
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部