每个 C++ 程序都包含一个或多个函数(function),其中一个必须命名为 main
操作系统通过调用 main
来运行 C++ 程序
在多数系统中, main
函数的返回值用于指示状态:返回值 0 表示成功,非 0 返回值通常用来指出错误类型
当 return
语句包括一个返回值时,该返回值的类型必须与函数返回类型相容,如 main
函数的返回值 0 是 int
类型的值
# 编译、运行程序
源文件:后缀为 .c
、 .cxx
、 .cpp
、 .cp
、 .c
的程序文件
可执行文件:
- Windows 系统:
.exe
文件 - UNIX 系统:
.out
文件
windows 系统运行一个可执行文件时,需提供可执行文件的文件名,可以忽略其拓展名
.exe
,而 UNIX 系统则需要使用完整的文件名(包含文件拓展名)
运行 GUN 编译器的命令: g++ -o prog1 prog1.cc
-o prog1
是编译器参数,指定可执行文件的文件名- 此命令生成名为
prog1
(UNIX) 或prog1.exe
(windows) 的可执行文件 - 若省略
-o prog1
,则生成a.out
(UNIX) 或a.exe
(windows) 可执行文件
# 输入与输出
C++ 语言并未定义任何输入输出(IO)语句,取而代之,包含了一个全面的标准库(standard library)来提供 IO 机制(以及很多其他设施)
标准库 iostream
- 输入流
istream
- 输出流
ostream
标准输入输出对象
cin
标准输入cout
标准输出cerr
输出警告与错误信息clog
输出程序运行时的一般性信息
<<
: 输出运算符,将给定值写到给定的 ostream
对象中,如: std::cout << "Enter two number";
>>
: 输入运算符,从给定的 istream
读入数据,并存入给定对象中,如: std::cin >> v1;
endl
: 操纵符,写入 endl
的效果是结束当前行,并将与设备关联的缓冲区 (buffer) 中的内容刷到设备中。缓冲刷新操作可以保证到目前为止程序所产生的所有输出都真正写入输出流中,而不是仅停留在内存中等待写入流 (stream)
流 (stream) :一个流就相当于一个字符序列,是从 IO 设备读出或写入 IO 设备的。术语 “流” 想要表达的是,随着时间的推移,字符是顺序生成或消耗的
#include <iostream> | |
int main() { | |
std::cout << "Enter two number" << std::endl; | |
int v1 = 0, v2 = 0; | |
std::cin >> v1 >> v2; | |
std::cout << "The sum of " << v1 << " and " << v2 << " is " << v1 + v2 << std::endl; | |
return 0; | |
} |
# include
程序使用标准库时,必须包含相关的头文件,如: #include <iostream>
告诉编译器使用 iostream
库
尖括号 <>
内的名字即指出了一个头文件,如 iostream
、 array
、 cmath
、 algorithm
等
#include
指令和头文件的名字必须写在同一行中,不用以分号结束
通常情况下, #include
指令必须出现在所有函数之外
一般将一个程序的所有 #include
指令都放在源文件的开始位置
# 命名空间
命名空间 (namespace) 可以避免 名字定义冲突 以及 使用库中相同名字导致的冲突
标准库定义的所有名字都在命名空间 std
中
通过命名空间使用标准库有一个副作用:当使用标准库中的一个名字时,必须显式说明使用的是命名空间 std
中的名字
::
: 作用域操作符,如: std::cin
、 std::cout
、 std::endl
分别指明 cin
、 cout
、 endl
是命名空间 std
中的
using
声明:一开始对 cin
、 cout
、 endl
使用 using
声明,以后无需再添加 std::
形式的前缀
每行的
using
声明都要以分号结束
例如:
#include <iostream> | |
using std::cout; | |
using std::cin; | |
using std::endl; | |
void main() | |
{ | |
cout << "Enter two number" << endl; | |
int v1 = 0, v2 = 0; | |
cin >> v1 >> v2; | |
cout << "The product of " << v1 << " and " << v2 << " is " << v1 * v2 << endl; | |
} |
# 注释
单行注释:
- 以双斜线
//
开始,以换行符结束 - 这种注释可以包含任何文本,包括额外的双斜线
界定符对注释:
- 以界定符
/*
开始,以界定符*/
结束 - 可以包含除
*/
外的任意内容,包括换行符 - 界定符对注释可以跨越程序中的多行
注释界定符可以放置于任何允许放置制表符、空格符或换行符的地方
当注释界定符跨越多行时,最好能显式地指出其内部的程序行都属于多行注释的一部分
C++ Primer 所采用的风格是:注释内的每行都以一个星号开头,从而指出整个范围都是多行注释的一部分
例如:
# include <iostream> | |
/* | |
* 简单主函数 | |
* 读取两个数,求它们的和 | |
*/ | |
int main() | |
{ | |
// 提示用户输入两个数 | |
std::cout << "Enter two numbers:" << std::endl; | |
int v1 = 0, v2 = 0; // 保存用于读取输入数据的变量 | |
std::cin >> v1 >> v2; // 读取输入数据 | |
std::cout << "The sum of " << v1 << " and " << v2 << " is " << v1 + v2 << std::endl; | |
return 0; | |
} |
界定符对注释通常用于多行解释,而双斜线注释常用于半行和单行附注
注释界定符不能嵌套,因为界定符对以
/*
开始,以*/
结束
调试时,通常需要注释掉一些代码,由于这些代码本身可能包含界定符对形式的注释,若继续采用界定符对进行注释则可能导致注释嵌套错误,因此,调试时注释的最好的方式是用单行注释方式注释掉代码段每一行
例如:
// /* | |
// * 单行注释中的任何内容都会被忽略 | |
// * 包括内部的注释对也一样会被忽略 | |
// */ |
# 控制语句
语句一般是顺序执行的:语句块的第一条语句首先执行,然后是第二条语句,依此类推
但程序设计语言也提供了多种不同的控制流语句,允许我们写出更为复杂的执行路径
# while
while
语句反复执行一段代码,直至给定条件为假为止
while
语句的形式:
while (condition) | |
statement |
while
语句的执行过程:交替检测 condition
条件 和 执行 statement
语句,直至 condition
为假时停止
其中, statement
可以是一条语句,也可以是多条语句组成的语句块
# 语句块
语句块 (block) ,是指用花括号 {}
包围的零条或多条语句的序列
语句块也是语句的一种,在任何要求使用语句的地方都可以使用语句块,如:
int sum = 0, val = 1; | |
while (val <= 10) { | |
sum = sum + val; // 等价于 sum += val; | |
val = val + 1; // 等价于 ++ val; | |
} |
# for
for
语句包含 循环头 和 循环体
循环头控制循环体的执行次数
循环头由三部分组成:
- 初始化语句 (init-statement)
- 循环条件 (condition)
- 表达式 (expression)
即, for (init-statement; condition; expression)
初始化语句只在 for
循环入口处执行一次
在初始化语句中定义的变量,在循环结束之后是不能使用的
每次执行循环体前都会先检查循环条件
表达式在 for
循环体之后执行
若循环体不被执行,表达式亦不会被执行
执行完表达式后, for
语句重新检测循环条件,循环持续至循环条件为假
int sum = 0; | |
for (int val = 1; val <= 10; ++ val) { | |
sum += val; | |
} |
上例中,初始化语句中定义的变量 val
在 for
语句结束后即失效
# for 与 while
for
循环:
- 一般用于循环次数已知的情况
for
循环可以节省内存,在初始化语句中定义一个局部变量,循环结束后,局部变量即被释放
while
循环:
- 一般用于循环次数不确定的情况,并且通常需要在循环之前定义变量
# 读取数量不定的输入数据
当循环次数不确定时,采用 while
循环
以读取数量不定的输入数据为例,其代码实现如下:
#include <iostream> | |
int main() | |
{ | |
int sum = 0, value = 0; | |
// 读取数据直至遇到文件尾,计算所有读入的值的和 | |
while (std::cin >> value) | |
sum += value; | |
std::cout << "Sum is: " << sum << std::endl; | |
return 0; | |
} |
例中,数据读取操作是在 while
的循环条件中完成的
while
循环条件的求值就是执行表达式 std::cin >> value
,表达式 std::cin >> value
返回输入运算符 >>
左侧运算对象,即 std::cin
,因此,该循环条件实际上是检测 std::cin
当我们使用一个
istream
对象,如cin
,作为条件时,其效果是检测流的状态:当遇到文件结束符 (end-of-file) 或遇到一个无效输入时(例如读入的值不是一个整数),istream
对象的状态会变为无效。进一步地,无效状态的istream
对象会使条件变为假
键盘输入文件结束符:
- windows 系统:
Ctrl + Z
,然后按Enter
或Return
键 - UNIX 系统、Mac OS X 系统:
Ctrl + D
# if
if
是条件语句,其语句形式为:
if (condition) { | |
statement; | |
} |
条件 condition
为真,执行条件之后的语句块 statement
,否则不予执行
或者:
if (condition) { | |
statement1; | |
} else { | |
statement2; | |
} |
条件 condition
为真时,执行 statement1
,否则,执行 statement2
# 用 if 语句统计输入序列中每个值连续出现了多少次
代码实现:
#include <iostream> | |
using std::cin; | |
using std::cout; | |
using std::endl; | |
int main() { | |
int currentNum, num; //currentNum 为当前统计的数字,num 为新读取的数字 | |
if (cin >> currentNum) { // 初始化当前统计数字 | |
int count = 1; // 初始化当前统计数字的出现次数 | |
while (cin >> num) { // 读取下一个数字 | |
if (num == currentNum) // 如果新输入数字与当前统计的数字相同,出现次数加一 | |
++ count; | |
else { // 如果不同,输出当前统计数字的出现次数,并更新所统计的数字,重置计数器 | |
cout << currentNum << " occurs " << count << " times " << endl; | |
currentNum = num; | |
count = 1; | |
} | |
} | |
cout << currentNum << " occurs " << count << " times " << endl; // 输出文件最后一个数字的出现次数 | |
} | |
return 0; | |
} |
# 类
类 (class) :一个类定义了一个类型 (class type) 以及与其关联的一组操作,其中,类型名就是类名
如: sales_item
类定义了一个名为 sales_item
的类型
与内置类型(例如 int
)一样,我们可以定义 类 类型的变量
如: sales_item item
表示 item
是一个 sales_item
类型的对象,通常简单表述为 “一个 sale-item
对象” ,或者,“一个 sales_item
”
# 头文件
C++ 通过使用头文件来使用标准库设施,或者是访问某个特定的类
头文件:通常根据其中定义的类的名字来命名,通常使用 .h
作为头文件的后缀,但也有些习惯用 .H
、 .hpp
或 .hxx
C++ 标准库头文件通常不带后缀
通过 #include
指令使用头文件
- 若是标准库的头文件,则用尖括号
<>
包围头文件名,如#include <iostream>
- 若是不属于标准库的头文件,则用双引号
""
包围,如#include "sales_item"
# 标准库头文件
C++ 标准库中除了定义 C++ 语言特有的功能外,也兼容了 C 语言的标准库
建议使用 C++ 版本的 C 标准库头文件
C 语言的头文件形如 name.h
,C++ 则将这些文件命名为 cname
。也就是去掉了 .h
后缀,而在文件名 name
之前添加了字母 c
,这里的 c
表示这是一个属于 C 语言标准库的头文件
一般来说,C++ 程序应该使用名为 cname
的头文件而不使用 name.h
的形式
定义在 cname
头文件中的名字从属于命名空间 std
,而定义在 .h
头文件中的名字不输于命名空间 std
# 术语表
参数(实参,argument)
:向函数传递的值
赋值(assignment)
:抹去一个对象的当前值,用一个新值取代
程序块(block)
:零条或多条语句的序列,用花括号包围
缓冲区(buffer)
:一个存储区域,用于保存数据
- IO 设施通常将输入(或输出)数据保存在一个缓冲区中,读写缓冲区的动作与程序中的动作是无关的
- 我们可以显式地刷新输出缓冲,以便强制将缓冲区中的数据写入输出设备
- 默认情况下,读
cin
会刷新cout
;程序非正常终止时也会刷新cout
内置类型(built-in type)
:由语言定义的类型,如 int
Cerr
:一个 ostream
对象,关联到标准错误,通常写入到与标准输出相同的设备
- 默认情况下,写到
cerr
的数据是不缓冲的 cerr
通常用于输出错误信息或其他不属于程序正常逻辑的输出内容
字符串字面值常量(character string literal)
:术语 string literal 的另一种叫法
cin
:一个 istream
对象,用来从标准输入读取数据
类(class)
:一种用于定义自己的数据结构及其相关操作的机制。类是 C++ 中最基本的特性之一。在标准库中, istream
和 ostream
都是类
类类型(class type)
:类定义的类型。类名即为类型名
clog
:一个 ostream
对象,关联到标准错误
- 默认情况下,写到
clog
的数据是被缓冲的 clog
通常用于报告程序的执行信息,存入一个日志文件中
注释(comment)
:被编译器忽略的程序文本
- C++ 有两种类型的注释:单行注释 和 界定符对注释
- 单行注释:以
//
开始,从//
到行尾的所有内容都是注释 - 界定符对注释:以
/*
开始,其后的所有内容都是注释,直至遇到*/
为止
条件(condition)
:求值结果为真或假的表达式。通常,值 0 表示假,非零值表示真
cout
:一个 ostream
对象,用于将数据写入标准输出。通常用于程序的正常输出内容
花括号(curly brace)
:花括号用于划定程序块边界。左花括号 {
为程序块开始,右花括号 }
为结束
数据结构(data structure)
:数据及其上所允许的操作的一种逻辑组合
编辑-编译-调试(edit-compile-debug)
:使程序能正确执行的开发过程
文件结束符(end-of-file)
:系统特定的标识,指出文件中无更多数据了
表达式(expression)
:最小的计算单元。一个表达式包含一个或多个运算对象,通常还包含一个或多个运算符。表达式求值会产生一个结果。例如,假设 i
和 j
是 int
对象,则 i + j
是一个表达式,它产生两个 int
值的和
for语句(for statement)
:迭代语句,提供重复执行能力。通常用来将一个计算反复执行指定次数
函数(function)
:具名的计算单元
函数体(function body)
:语句块,定义了函数所执行的动作
函数名(function name)
:函数为人所知的名字,也用来进行函数调用
头文件(header)
:使类或其他名字的定义可被多个程序使用的一种机制。程序通过 #include
指令使用头文件
if语句(if statement)
:根据一个特定条件的值进行条件执行的语句。如果条件为真,执行 if
语句体。否则,执行 else
语句体(如果存在的话)
初始化(initialize)
:在一个对象创建的时候赋予它一个值
iostream
:头文件,提供了面向流的输入输出的标准库类型
istream
:提供了面向流的输入的库类型
库类型(library type)
:标准库定义的类型,如 istream
main
:操作系统执行一个 C++ 程序时所调用的函数。每个程序必须有且只有一个命名为 main
的函数
操纵符(manipulator)对象
,如 std::endl
,在读写流的时候用来 “操纵” 流本身
成员函数(member function)
:类定义的操作。通常通过调用成员函数来操作特定对象
方法(method)
:成员函数的同义术语
命名空间(namespace)
:将库定义的名字放在一个单一位置的机制。命名空间可以帮助避免不经意的名字冲突。C++ 标准库定义的名字在命名空间 std
中
ostream
:标准库类型,提供面向流的输出
形参列表(parameter list)
:函数定义的一部分,指出调用函数时可以使用什么样的实参,可能为空列表
返回类型(return type)
:函数返回值的类型
源文件(source file)
:包含 C++ 程序的文件
标准错误(standard error)
:输出流,用于报告错误。标准输出和标准错误通常关联到程序执行所在的窗口
标准输入(standard input)
:输入流,通常与程序执行所在窗口相关联
标准库(standard library)
:一个类型和函数的集合,每个 C++ 编译器都必须支持
- 标准库提供了支持 IO 操作的类型
- C++ 程序员倾向于用 “库” 指代整个标准库,还倾向于用库类型表示标准库的特定部分,例如,用 “
iostream
库” 表示标准库中定义 IO 类的部分
标准输出(standard output)
:输出流,通常与程序执行所在窗口相关联
语句(statement)
:程序的一部分,指定了当程序执行时进行什么动作。一个表达式接一个分号就是一条语句;其他类型的语句包括语句块、 if
语句、 for
语句和 while
语句,所有这些语句内都包含其他语句
std
:标准库所使用的命名空间。 std::cout
表示我们要使用定义在命名空间 std
中的名字 cout
字符串常量(string literal)
:零或多个字符组成的序列,用双引号包围,例如, "a string literal"
未初始化的变量(uninitialized variable)
:未赋予初值的变量
- 类类型的变量如果未指定初值,则按类定义指定的方式进行初始化
- 定义在函数内部的内置类型变量默认是不初始化的,除非有显式的初始化语句
- 试图使用一个未初始化变量的值是错误的
- 未初始化变量是 bug 的常见成因
变量(variable)
:具名对象
while语句(while statement)
:迭代语句,提供重复执行直至一个特定条件为假的机制。循环体会执行零次或多次,依赖于循环条件求值结果
() 运算符(() operator)
:调用运算符。跟随在函数名之后的一对括号 “ ()
”,起到调用函数的效果。传递给函数的实参放置在括号内
++ 运算符(++ operator)
:递增运算符。将运算对象的值加 1, ++i
等价于 i = i + 1
+= 运算符(+= operator)
:复合赋值运算符,将右侧运算对象加到左侧运算对象上; a += b
等价于 a = a + b
. 运算符(.operator)
:点运算符。左侧运算对象必须是一个类类型对象,右侧运算对象必须是此对象的一个成员的名字。运算结果即为该对象的这个成员
:: 运算符(:: operator)
:作用域运算符。其用处之一是访问命名空间中的名字。例如, std::cout
表示命名空间 std
中的名字 cout
= 运算符(= operator)
:将右侧运算对象的值赋予左侧运算对象所表示的对象
-- 运算符(-- operator)
:递减运算符。将运算对象的值减 1, --i
等价于 i = i - 1
<< 运算符(<< operator)
:输出运算符
- 将右侧运算对象的值写到左侧运算对象表示的输出流,例如,
cout << "hi"
表示将 hi 写到标准输出 - 输出运算符可以连接,例如,
cout << "hi" << "bye"
表示将输出 hibye
>> 运算符(>> operator)
:输入运算符
- 从左侧运算对象所指定的输入流读取数据,存入右侧运算对象中。例如,
cin >> i
表示从标准输入读取下一个值,存入i
中 - 输入运算符可以连接。例如,
cin >> i >> j
表示先读取一个值存入i
,再读取一个值存入j
\#include
:头文件包含指令,使头文件中代码可被程序使用
== 运算符(== operator)
:相等运算符。检测左侧运算对象是否 等于 右侧运算对象
!= 运算符(!= operator)
:不等运算符。检测左侧运算对象是否 不等于 右侧运算对象
<= 运算符(<= operator)
:小于等于运算符。检测左侧运算对象是否 小于等于 右侧运算对象
< 运算符(< operator)
:小于运算符。检测左侧运算对象是否 小于 右侧运算对象
>= 运算符(>= operator)
:大于等于运算符。检测左侧运算对象是否 大于等于 右侧运算对象
> 运算符(> operator)
:大于运算符。检测左侧运算对象是否 大于 右侧运算对象
参考:C++ Primer 中文版(第 5 版)