在前一篇提到了一串字符#include,这串字符就是预处理器指示符,它用“#”来标识。而处理这些指示符的程序被称做预处理器(通常捆绑在编译器中)。#include读入文件有两种格式:
#include<some_file.h>表明这个文件是一个工程或标准头文件,查找过程会检查预定义的目录
#include”some_file.h”表明该文件是用户提供的头文件,查找该文件时将从当前文件目录开始
被包含的文件还可以含有#include 指示符由于嵌套包含文件的原因一个头文件可能会被多次包含在一个源文件中条件指示符可防止这种头文件的重复处理例如:
#ifndef BOOKSTORE_H
#define BOOKSTORE_H
/* Bookstore.h 的内容 */
#endif
条件指示符#ifndef 检查BOOKSTORE_H 在前面是否已经被定义这里BOOKSTORE_H,是一个预编译器常量习惯上预编译器常量往往被写成大写字母如果BOOKSTORE_H在前面没有被定义则条件指示符的值为真于是从#ifndef 到#endif 之间的所有语句都被包含进来进行处理相反如果#ifndef 指示符的值为假则它与#endif 指示符之间的行将被忽略。
为了保证头文件只被处理一次把如下#define 指示符
#define BOOKSTORE_H
放在#ifndef 后面这样在头文件的内容第一次被处理时BOOKSTORE_H 将被定义从而防止了在程序文本文件中以后#ifndef 指示符的值为真。
#ifdef 指示符常被用来判断一个预处理器常量是否已被定义以便有条件地包含程序代码例如:
int main()
{
#ifdef DEBUG
cout << "Beginning execution of main()\n";
#endif
string word;
vector< string > text;
while ( cin >> word )
{
#ifdef DEBUG
cout << "word read: " << word << "\n";
#endif
text.push_back( word );
}
// ...
}
本例中如果没有定义DEBUG 实际被编译的程序代码如下
int main()
{
string word;
vector< string > text;
while ( cin >> word )
{
text.push_back( word );
}
// ...
}
反之如果定义了DEBUG 则传给编译器的程序代码是
int main()
{
cout << "Beginning execution of main()\n";
string word;
vector< string > text;
while ( cin >> word )
{
cout << "word read: " << word << "\n";
text.push_back( word );
}
// ...
}
也可以在程序中用#define 指示符定义预处理器常量。
编译C++程序时编译器自动定义了一个预处理器名字__cplusplus 注意前面有两个下划线因此我们可以根据它来判断该程序是否是C++程序以便有条件地包含一些代码例如
#ifdef __cplusplus
// 不错我们要编译C++
// extern "C"
extern "C"
#endif
int min( int, int );
在编译标准C 时编译器将自动定义名字__STDC__ 当然__cplusplus 与__STDC__不会同时被定义。另外两个比较有用的预定义名字是__LINE__和__FILE__ __LINE__记录文件已经被编译的行数__FILE__包含正在被编译的文件的名字可以这样使用它们
if ( element_count == 0 )
cerr << "Error: " << __FILE__
<< " : line " << __LINE__
<< "element_count must be non-zero.\n";
另外两个预定义名字分别包含当前被编译文件的编译时间__TIME__ 和日期
__DATE__ 时间格式为hh:mm:ss 因此如果在上午8 点17 分编译一个文件则时间表示为08:17:05 如果这一天是1996 年10 月31 日星期四则日期表示为
Oct 31 1996
若当前处理的行或文件发生变化则__LINE__和__FILE__的值将分别被改变其他四个预定义名字在编译期间保持不变它们的值也不能被修改
assert()是C 语台标准库中提供的一个通用预处理器宏在代码中常利用assert()来判断一个必需的前提条件以便程序能够正确执行例如假定我们要读入一个文本文件并对其中的词进行排序必需的前提条件是文件名已经提供给我们了这样我们才能打开这个文件为了使用assert() 必须包含与之相关联的头文件
#include <assert.h>
下面是一个简单的使用示例
assert( filename != 0 );
assert()将测试filename 不等于0 的条件是否满足这表示为了后面的程序能够正确执行我们必须断言一个必需的前提条件如果这个条件为假即filename 等于0 断言失败则程序将输出诊断消息然后终止assert.h 是C 库头文件的C 名字C++程序可以通过C 库的C 名字或C++名字来使用它。
这个头文件的C++名字是cassert C 库头文件的C++名字总是以字母C 开头后面是去掉后缀.h 的C 名字正如前面所解释的由于在各种C++实现中头文件的后缀各不相同因此标准C++头文件没有指定后缀使用头文件的C 名字或者C++名字两种情况下头文件的#include 预处理器指示符的效果也会不同下面的#include 指示符
#include <cassert>
将cassert 的内容被读入到我们的文本文件中但是由于所有的C++库名字是在名字空间std 中被定义的因而在我们的程序文本文件中它们是不可见的除非用下面的using 指示符显式地使其可见
using namespace std;
使用C 头文件的#include 指示符
#include <assert.h>
就可以直接在程序文本文件中使用名字assert() 而无需使用using 指示符库文件厂商用名字空间来控制全局名字空间污染即名字冲突问题以避免它们的库污染了用户程序的名字空间。
在写程序的时候还有一个重要的部分就是注释,这是一种程序礼仪。可以更好帮助程序员来读程序。在c++中注释有两种:1、注释对用/* */来表示 2、单行注释用//表示。在程序编写过程中合理使用注释符可以帮助自己对程序的理解
在上面的程序例子中,有提到iostream,这是一种标准的输入输出流库,任何要用到输入输出的时候就必须要包含相关系统头文件:#include<iostream>。例如:终端输入与终端输出分别要用cin和cout来表示。(<<是输出操作符,>>是输入操作符,例如cin>>file_name,cout<<file_name)。这些都是简单的字符输入与输出,那么文件的输入输出是怎样的?有个例子:
#include <iostream>
#include <fstream>//打开一个文件供输入或输出
#include <string>
int main()
{
ofstream outfile( "out_file" );
//为了打开一个输出文件,必须声明ofstream类型的对象
ifstream infile( "in_file" );
//为了打开一个文件供输入,必须声明ifstream类型的对象
if ( ! infile ) {
cerr << "error: unable to open input file!\n";
return -1;
}
if ( ! outfile ) {
cerr << "error: unable to open output file!\n";
return -2;
}
string word;
while ( infile >> word )
outfile << word << ' ';
return 0;
}
这段程序代码的意思就是从一个名为in_file 的文本文件中读取单词,然后把每个词写到一个名为out_file 的输出文件中并且每个词之间用空格分开。