编写简单的C++程序

示例

1
2
3
4
int main() 
{
return 0;
}

说明

关于main函数:

  1. 操作系统通过 main 函数返回的值来确定程序是否成功执行完毕。返回 0 值表明程序程序成功执行完毕。
  2. 每个 C++ 程序必须含有 main 函数,且 main 函数是(唯一)被操作系统显式调用的函数。
  3. 定义 main 函数和定义其他函数一样。定义函数必须指定 4 个元素:返回类型、函数名、圆括号内的形参表(可能为空)和函数体。main 函数的形参个数是有限的。本例中定义的 main 函数形参表为空。
  4. main 函数的返回值必须是 int 型 。在大多数系统中,main 函数的返回值是一个状态指示器。返回值 0 往往表示 main 函数成功执行完毕。任何其他非零的返回值都有操作系统定义的含义。通常非零返回值表明有错误出现。每一种操作系统都有自己的方式告诉用户 main 函数返回什么内容。

编译与执行程序

GNU:g++

调用 C++ 编译器的命令因编译器和操作系统的不同而不同,常用的编译器是 GNU 编译器和微软 Visual Studio 编译器。调用 GNU 编译器的默认命令是 g++

1
$ g++ prog1.cc -o prog1     

这里的 $ 是系统提示符。这个命令产生一个为 prog1 或 prog1.exe 的可执行文件。在 UNIX 系统下,可执行文件没有后缀;而在 Windows 下,后缀为 .exe-o prog1 是编译器参数以及用来存放可执行文件的文件名。如果省略 -o prog1,那么编译器在 UNIX 系统下产生名为 a.out 而在 Windows 下产生名为 a.exe 的可执行文件。

微软:cl

微软编译器采用命令 cl 来调用:

1
C:\directory> cl -GX prog1.cpp     

这里的 C:\directory> 是系统提示符,directory 是当前目录名。cl 是调用编译器的命令。-GX 是一个选项,该选项在使用命令行界面编译器程序时是必需的。微软编译器自动产生与源文件同名的可执行文件,这个可执行文件具有 .exe 后缀且与源文件同名。本例中,可执行文件命名为 prog1.exe。

更多的信息请参考你的编译器用户指南。

源文件命名规范

C++ 程序文件的后缀与运行的具体编译器有关。常见的形式包括:

  • prog1.cc
  • prog1.cxx
  • prog1.cpp
  • prog1.cp
  • prog1.C

main函数的返回值

访问main函数的返回值的方式和系统有关。不论UNIX和Windows系统,执行程序后,必须发出一个适当的echo命令。UNIX系统中,通过键入如下命令获取状态:

1
$ echo $?

要在Windows系统下查看状态,键入

1
c:\directory> echo %ERRORLEVEL%

再谈编译

编译器的部分工作是寻找程序代码中的错误。编译器不能查出程序的意义是否正确, 但它可以查出程序形式上的错误。下面是编译器能查出的最普遍的一些错误。

  • 语法错误。程序员犯了 C++ 语言中的语法错误。下面代码段说明常见的语法错误;每个注释描述下一行的错误。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// error: missing ')' in parameter 
list for main
int main ( {
// error: used colon, not a semicolon
after endl
std::cout << "Read each file." << std::endl:
// error: missing quotes around
string literal
std::cout << Update master. << std::endl;
// ok: no errors on this line
std::cout << "Write new master." <<std::endl;
// error: missing ';' on return
statement
return 0
}
  • 类型错误。C++ 中每个数据项都有其相关联的类型。例如,值 10 是一个整数。用双引号标注起来的单词“hello”是字符串字面值。类型错误的一个实例是传递了字符串字面值给应该得到整型参数的函数。
  • 声明错误。C++ 程序中使用的每个名字必须在使用之前声明。没有声明名字通常会导致错误信息。最常见的两种声明错误,是从标准库中访问名字时忘记使用“std::”,以及由于疏忽而拼错标识符名:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream> 
int main()
{
int v1, v2;
std::cin >> v >> v2; // error: uses " v "not" v1"
// cout not defined, should be std::cout

35
cout << v1 + v2 << std::endl;
return 0;
}
```

错误信息包含行号和编译器对我们所犯错误的简要描述。按错误报告的顺序改正错误是个好习惯,通常一个错误可能会产生一连串的影响,并导致编译器报告比实际多得多的错误。最好在每次修改后或最多改正了一些显而易见的错误后,就重新编译代码。

## 初窥输入/输出 ##

### 标准输入与输出对象 ###

标准库定义了4个IO对象:

#### istream 类型对象 ####

| 对象名 | 作用 |
|--------|--------------|
| cin | 标准输入对象 |

#### ostream 类型对象 ####

| 对象名 | 作用 |
|--------|------------------------------------------------------|
| cout | 标准输出对象 |
| cerr | 标准错误,通常用来输出警告和错误信息给程序的使用者。 |
| clog | 用于产生程序执行过程中的一般信息。 |

### 一个使用IO库的程序 ###

#### 示例 ####

``` cpp
#include <iostream>
int main()
{
std::cout << "Enter two numbers:" << std::endl;
int v1, v2;
std::cin >> v1 >> v2;
std::cout << "The sum of " << v1 << " and " << v2
<< " is " << v1 + v2 << std::endl;
return 0;
}

说明

程序的第一行是一个 预处理指示

1
#include <iostream>     

告诉编译器要使用 iostream 库。尖括号里的名字是一个。头文件。程序使用库工具时必须包含相关的头文件。#include 指示必须单独写成一行——头文件名和 #include 必须在同一行。通常,#include 指示应出现在任何函数的外部。而且习惯上,程序的所有 #include 指示都在文件开头部分出现。

main 函数体中第一条语句执行了一个表达式。C++ 中,一个表达式由一个或几个操作数和通常是一个操作符组成。该语句的表达式使用输出操作符(<< 操作符),在标准输出上输出提示语:

1
std::cout << "Enter two numbers:" << std::endl;    

这个语句用了两次输出操作符。每个输出操作符实例都接受两个操作数:左操作数必须是 ostream 对象;右操作数是要输出的值。操作符将其右操作数写到作为其左操作数的 ostream 对象。

C++ 中,每个表达式都会产生一个结果,通常是将操作符作用到其操作数所产生的值。当操作符是输出操作符时,结果是左操作数的值。也就是说,输出操作返回的值是输出流本身。

endl 是一个特殊值,称为 操纵符 ,将它写入输出流时,具有输出换行的效果,并刷新与设备相关联的缓冲区。通过刷新缓冲区,用户可立即看到写入到流中的输出。

程序员经常在调试过程中插入输出语句,这些语句都应该刷新输出流。忘记刷新输出流可能会造成输出停留在缓冲区中,如果程序崩溃,将会导致程序错误推断崩溃位置。

使用标准库中的名字

细心的读者会注意到这个程序中使用的是 std::coutstd::endl ,而不是 coutendl 。前缀 std:: 表明 coutendl 是定义在命名空间 std 中的。使用命名空间程序员可以避免与库中定义的名字相同而引起无意冲突。因为标准库定义的名字是定义在命名空间中,所以我们可以按自己的意图使用相同的名字。

标准库使用命名空间的副作用是,当我们使用标准库中的名字时,必须显式地表达出使用的是命名空间 std 下的名字。std::cout 的写法使用了作用域操作符(scope operator,::操作符),表示使用的是定义在命名空间 std 中的 cout。我们将在 第 3.1 节学习到程序中经常使用的避免这种冗长句法的方法。

读入流

在输出提示语后,将读入用户输入的数据。先定义两个名为 v1 和 v2 的 变量来保存输入:

1
int v1, v2; 

将这些变量定义为 int 类型,int 类型是一种代表整数值的内置类型。这些变量 未初始化,表示没有赋给它们初始值。这些变量在首次使用时会读入一个值,因此可以没有初始值。

下一条语句读取输入:

1
std::cin >> v1 >> v2;

输入操作符(>> 操作符)行为与输出操作符相似。它接受一个 istream 对象作为其左操作数,接受一个对象作为其右操作数,它从 istream 操作数读取数据并保存到右操作数中。

注释

C++ 中有单行注释和成对注释两种类型的注释。

  • 单行注释以双斜线(//)开头,行中处于双斜线右边的内容是注释,被编译器忽略。
  • 成对注释符号(/* */),是从 C 语言继承过来的。这种注释以/*开头,以*/结尾。编译器把落入注释对/**/之间的内容作为注释。

成对注释符号不可嵌套

成对注释总是以 /* 开始并以 */ 结束。这意味着,一对注释符号不能出现在另一对注释符号中。

临时忽略一段代码更好的方法,是用编辑器在要忽略的每一行代码前面插入单行注释。

尽量使用 C++ 风格的注释

旧的 C 注释语法在 C++ 里还可以用,C++新发明的行尾注释语法也有其过人之处。例如下面这种情形:

1
2
3
4
5
if ( a > b ) {
// int temp = a; // swap a and b
// a = b;
// b = temp;
}

假设你出于某种原因要注释掉这个代码块。从软件工程的角度看,写这段代码的程序员也做得很好,他最初的代码里也写了一个注释,以解释代码在做什么。用 C++形式的句法来注释掉这个程序块时,嵌在里面的最初的注释不受影响,但如果选择 C 风格的注释就会发生严重的错误:

1
2
3
4
5
6
if ( a > b ) {
/* int temp = a; /* swap a and b */
a = b;
b = temp;
*/
}

请注意嵌在代码块里的注释是怎么无意间使本来想注释掉整个代码块的注释提前结束的。

C 风格的注释当然还有它存在的价值。例如,它们在 C 和 C++ 编译器都要处理的头文件中是无法替代的。尽管如此, 只要有可能,你最好尽量用 C++ 风格的注释

值得指出的是,有些老的专门为 C 写的预处理程序不知道处理 C++ 风格的注释,所以象下面这种情形时,事情就不会象预想的那样:

1
#define LIGHT_SPEED   3e8    // m/sec (in a vacuum)    

Doxygen推荐的注释风格

Doxygen 是一个强大的程序文档生成工具,只要注释符合Doxygen的规范,这些注释就可以用来生成程序的文档。这里 总结了Doxygen推荐的注释风格。

Comments