2.1 进入C++
本节将引导你编写第一个C++程序,并介绍一些基本概念。
2.1.1 main()函数
每个C++程序都必须包含一个名为 main
的函数。操作系统通过调用 main
函数来启动C++程序。main
函数是程序的入口点。
基本结构:
1 | int main() |
-
int main()
: 这是main
函数的函数头。int
表示main
函数执行完毕后将返回一个整数值给操作系统。括号()
表示这是一个函数。 -
{ ... }
: 花括号标记了函数体的开始和结束。函数体包含了程序要执行的指令(语句)。 -
return 0;
: 这条语句表示main
函数执行完毕。返回值0
通常表示程序成功执行。非零返回值通常表示程序遇到了错误。
示例:
一个最简单的C++程序:
1 | int main() |
这个程序什么也不做,但它是一个完整的、可以编译和运行的C++程序。
2.1.2 C++注释
注释是程序中用于解释代码的部分,它们会被编译器忽略,不会影响程序的执行。注释可以提高代码的可读性。
C++支持两种类型的注释:
- 单行注释: 以
//
开始,直到该行结束。 - 多行注释: 以
/*
开始,以*/
结束,可以跨越多行。
用法与示例:
1 |
|
2.1.3 C++预处理器和iostream文件
在编译C++程序之前,预处理器会首先处理源代码。预处理器指令以 #
符号开头。
#include
是一个常见的预处理器指令,它告诉预处理器将另一个文件的内容包含到当前文件中。
iostream
文件是C++标准库的一部分,包含了进行输入(input)和输出(output)操作所需的信息。例如,要使用 cout
进行输出,就需要包含 iostream
文件。
用法与示例:
1 |
|
-
#include <iostream>
: 这条指令告诉预处理器查找名为iostream
的标准头文件,并将其内容插入到该指令所在的位置。
2.1.4 头文件名
头文件(Header Files)包含了函数、类、对象等的声明,使得我们可以在程序中使用它们。C++标准库提供了许多头文件。
- 标准库头文件: 通常使用尖括号
<>
括起来,例如<iostream>
,<cmath>
,<string>
。编译器会在标准库的包含路径中查找这些文件。 - 用户自定义头文件: 通常使用双引号
""
括起来,例如"myheader.h"
。编译器会首先在当前源文件所在的目录查找,然后在标准包含路径中查找。
C++98之前的头文件: 以前的C++头文件可能带有 .h
后缀(如 <iostream.h>
)。现代C++(C++98及以后)推荐使用不带 .h
后缀的标准头文件(如 <iostream>
),这些头文件的内容位于 std
命名空间中。
示例:
1 |
|
2.1.5 名称空间
名称空间(Namespace)是C++中避免命名冲突的一种机制。不同的名称空间可以包含同名的函数、类或变量。
标准C++库中的所有内容(如 cout
, cin
, endl
, string
等)都定义在名为 std
的名称空间中。
要使用 std
名称空间中的元素,有几种方法:
- 使用作用域解析运算符
::
: 在每个元素前加上std::
。1
std::cout << "Hello!" << std::endl;
- 使用
using
声明: 将特定的名称引入当前作用域。1
2
3
4
5
6
7
8
9
10
using std::cout; // 只引入 cout
using std::endl; // 只引入 endl
int main() {
cout << "Hello!" << endl; // 不需要 std:: 前缀
// std::cin >> variable; // 如果要用 cin,仍需 std:: 或 using std::cin;
return 0;
} - 使用
using
编译指令: 将整个名称空间的所有名称引入当前作用域。(不推荐在头文件中或全局作用域中使用,可能导致命名冲突)1
2
3
4
5
6
7
8
9
10
using namespace std; // 引入 std 中的所有名称
int main() {
cout << "Hello!" << endl; // 不需要 std:: 前缀
int x;
cin >> x; // cin 也不需要 std:: 前缀
return 0;
}
推荐做法:
- 在
.cpp
文件的函数内部或较小作用域内,可以使用using
声明或using namespace std;
。 - 在头文件 (
.h
) 中,绝对不要使用using namespace std;
,应始终使用std::
前缀。 - 在简单的示例或小型项目中,
using namespace std;
可以简化代码,但在大型项目中,坚持使用std::::
或using
声明是更安全的做法。
2.1.6 使用cout进行C++输出
cout
是 iostream
库中预定义的一个对象,代表标准输出流,通常连接到控制台(屏幕)。
<<
运算符(插入运算符)用于将数据发送给 cout
对象,使其显示在屏幕上。
用法与示例:
1 |
|
-
std::endl
: 是一个特殊的控制符(manipulator),它会输出一个换行符,并刷新输出缓冲区(确保内容立即显示)。 -
\n
: 是一个转义字符,代表换行符。它只输出换行,通常不保证立即刷新缓冲区。在多数情况下,\n
比std::endl
效率稍高。
2.1.7 C++源代码的格式化
C++语言对代码格式(如空格、缩进、换行)的要求相对宽松,但良好的格式化对于代码的可读性和可维护性至关重要。
基本规则和建议:
- 语句分隔: C++使用分号
;
来结束大多数语句。 - 空格:
- 通常在运算符(
=
,+
,-
,*
,/
,<<
,>>
,==
等)两边添加空格。 - 在逗号
,
后面添加空格。 - 在函数名和后面的括号
()
之间通常不加空格。
- 通常在运算符(
- 缩进: 使用一致的缩进(通常是4个空格或一个制表符)来表示代码块(如
main
函数体、循环体、条件语句体)。这极大地提高了代码结构的可读性。 - 换行:
- 通常每行只写一条语句。
- 可以在合适的地方(如运算符之后、逗号之后)将长语句分成多行。
- 花括号
{}
: 对于代码块(如函数体、if
语句块等),花括号的放置风格有多种(如 K&R 风格、Allman 风格),选择一种并保持一致即可。
示例 (良好格式):
1 |
|
示例 (不良格式,但语法正确):
1 |
|
虽然第二个示例也能编译运行,但极难阅读和理解。遵循一致的、清晰的格式化风格是专业编程的重要组成部分。
2.2 C++语句
C++程序由一系列语句组成。语句是C++程序的基本执行单元,通常以分号 ;
结尾。本节将介绍两种基本的语句:声明语句和赋值语句,并进一步探讨 cout
的用法。
2.2.1 声明语句和变量
声明语句 (Declaration Statement) 用于向编译器声明程序中将要使用的变量 (Variable) 的名称和类型。
变量 是计算机内存中用于存储数据的一块区域,并且有一个名字(标识符)。通过变量名,我们可以访问和修改存储在内存中的数据。在使用变量之前,必须先声明它。
声明变量的语法:
1 | typeName variableName; |
-
typeName
: 指定变量要存储的数据类型(例如int
表示整数,double
表示浮点数)。 -
variableName
: 你为变量选择的名称(标识符)。
用法与示例:
1 |
|
- 声明 (Declaration): 告诉编译器变量的名称和类型。
- 定义 (Definition): 声明通常也是定义,因为它会为变量分配内存空间。
- 初始化 (Initialization): 在声明变量的同时给它赋一个初始值。这是一个好习惯,可以避免使用未定义的值。
2.2.2 赋值语句
赋值语句 (Assignment Statement) 用于将一个值赋给一个变量。它使用赋值运算符 =
。
语法:
1 | variableName = value; |
-
variableName
: 要接收值的变量的名称(必须是已声明的变量)。 -
value
: 要赋给变量的值。这可以是一个字面常量(如25
)、另一个变量、或一个表达式的结果。
重要概念:
- 赋值操作是将右侧的值复制到左侧的变量中。
- 左侧必须是一个可修改的**左值 (lvalue)**,通常就是一个变量名。
- 右侧可以是一个**右值 (rvalue)**,即一个可以产生值的表达式。
用法与示例:
1 |
|
2.2.3 cout的新花样
我们在 2.1.6 节已经学习了如何使用 cout
输出字符串和使用 endl
换行。cout
的一个强大之处在于它的“智能”,它可以识别并正确显示多种不同类型的数据。
cout
对象与插入运算符 <<
结合使用,可以自动处理 C++ 的内置数据类型,如整数 (int
)、浮点数 (double
, float
)、字符 (char
) 以及 C 风格字符串和 std::string
对象。
用法与示例:
1 |
|
cout
之所以能做到这一点,是因为 <<
运算符针对不同的数据类型进行了**重载 (Overloading)**(我们将在后续章节详细学习)。简单来说,就是为 <<
运算符定义了多个版本,每个版本知道如何处理特定类型的数据,并将它们转换为适合输出的字符序列。
2.3 其他C++语句
本节将介绍更多C++语句,包括如何从用户那里获取输入,如何更灵活地使用 cout
,并对C++的核心概念——类进行初步介绍。
2.3.1 使用cin
与 cout
用于输出类似,cin
是 iostream
库中预定义的一个对象,代表标准输入流,通常连接到键盘。我们可以使用 cin
来读取用户输入的数据。
>>
运算符(提取运算符)用于从 cin
对象获取数据,并将其存储到变量中。
用法与示例:
1 |
|
-
#include <iostream>
: 使用cin
同样需要包含此头文件。 -
std::cin
:cin
对象也位于std
名称空间中。 -
cin >> variable;
: 提取运算符>>
从输入流(键盘)中读取数据,并根据variable
的类型进行解释,然后将值存入variable
。cin
也会根据读取的数据类型自动进行转换。 - 输入分隔:
cin
通常使用空白(空格、制表符、换行符)来分隔不同的输入项。例如,如果程序期望读取两个整数cin >> a >> b;
,用户可以输入10 20
然后按 Enter,或者输入10
按 Enter 再输入20
按 Enter。
2.3.2 使用cout进行拼接
我们在前面已经看到如何使用 cout
和插入运算符 <<
输出单个值或字符串。cout
的一个便捷之处在于,你可以在一条语句中连续使用 <<
运算符,将多个输出项“拼接”在一起。这称为**链式输出 (Chaining Output)**。
用法与示例:
1 |
|
-
cout << item1 << item2 << item3;
:cout
对象在处理完第一个<< item1
后,会返回自身 (cout
),因此可以继续处理下一个<< item2
,以此类推。 - 这种链式调用使得将变量值、字符串字面量和表达式结果组合输出变得非常方便和易读。
2.3.3 类简介
类 (Class) 是C++的核心概念,也是面向对象编程(OOP)的基础。可以把类看作是创建对象 (Object) 的蓝图或模板。
- 封装 (Encapsulation): 类将数据(称为成员变量或属性)和操作这些数据的函数(称为成员函数或方法)捆绑在一起。
- 抽象 (Abstraction): 类提供了一个接口(通过其公有成员函数),隐藏了内部实现的复杂细节。
我们已经在使用类的对象了!cout
和 cin
就是 C++ 标准库中定义的类的对象:
-
cout
是ostream
类(输出流类)的一个对象。 -
cin
是istream
类(输入流类)的一个对象。
ostream
类定义了如何处理输出,包括 <<
运算符如何针对不同数据类型工作。istream
类定义了如何处理输入,包括 >>
运算符如何读取数据。
概念理解:
想象一下 “汽车” 这个类:
- 数据/属性 (成员变量): 颜色、品牌、型号、当前速度、油量等。
- 操作/行为 (成员函数): 启动()、加速()、刹车()、鸣笛()、获取当前速度() 等。
根据这个 “汽车” 类,我们可以创建具体的对象,比如 “我的蓝色丰田卡罗拉” 或 “邻居的红色法拉利”。每个对象都有自己的属性值(不同的颜色、品牌等),但它们都共享类定义的行为(都可以启动、加速、刹车)。
示例 (概念性,非完整代码):
1 | // 这是一个非常简化的概念展示,不是完整的 C++ 类定义 |
在后续章节中,我们将深入学习如何定义和使用自己的类。目前,只需理解类是定义数据和相关操作的一种方式,而对象是类的具体实例,cout
和 cin
就是我们已经接触到的对象实例。
2.4 函数
函数是C++程序的构建块,它们是执行特定任务的命名代码段。使用函数可以使程序模块化、更易于理解和维护。本节将介绍如何使用和定义函数。
2.4.1 使用有返回值的函数
许多C++函数会执行一个操作并返回一个值给调用它的代码。这种函数被称为**有返回值的函数 (Function with Return Value)**。
我们已经使用过一些有返回值的函数,例如 C++ 标准库 <cmath>
(或 C 语言的 <math.h>
) 中提供的 sqrt()
函数,它计算一个数的平方根并返回结果。
使用方法:
- 包含头文件: 确保包含了提供该函数声明的头文件(例如
<cmath>
)。 - 函数调用: 使用函数名,并在括号
()
内提供所需的**参数 (Argument)**(传递给函数的值)。 - 处理返回值: 函数调用本身就是一个表达式,其值就是函数的返回值。可以将这个返回值赋给变量、用在更复杂的表达式中或直接输出。
用法与示例:
1 |
|
-
std::sqrt(area)
: 这是一个函数调用。std::sqrt
是函数名,area
是传递给函数的参数。 -
double side = ...
:sqrt()
函数返回一个double
类型的值,这个值被用来初始化side
变量。
2.4.2 函数变体
函数可以有多种形式:
- 有参数,有返回值: 如
sqrt(double x)
,接收一个double
参数,返回一个double
值。 - 无参数,有返回值: 例如,某些库函数可能读取系统时间并返回一个值,不需要用户提供参数。
- 有参数,无返回值: 这种函数执行一个操作(如打印到屏幕),但不需要返回任何计算结果。这种函数的返回类型通常声明为
void
。我们将在 2.4.3 节看到例子。 - 无参数,无返回值: 执行一个固定的操作,不接受输入参数也不返回结果,返回类型也是
void
。
示例 (概念性):
1 |
|
2.4.3 用户定义的函数
除了使用库函数,我们还可以定义自己的函数来执行特定任务。这有助于组织代码和重用代码。
定义函数的基本结构:
1 | returnType functionName(parameterList) |
-
returnType
: 函数执行完毕后返回的数据类型。如果函数不返回值,则使用void
。 -
functionName
: 你为函数选择的名称。 -
parameterList
: 函数接受的参数列表,包括每个参数的类型和名称。如果没有参数,括号()
仍然需要,但内部为空。 -
{ ... }
: 函数体,包含函数的代码。
函数原型 (Function Prototype):
在使用函数之前,编译器需要知道函数的接口(返回类型、名称、参数列表)。通常将函数原型放在 main()
函数之前或单独的头文件中。原型看起来像函数头,但以分号 ;
结尾,可以省略参数名。
1 | returnType functionName(parameterTypeList); // 函数原型 |
用法与示例 (定义一个无返回值的函数):
1 |
|
- 原型:
void cheers(int n);
告诉编译器main
函数后面会定义一个名为cheers
的函数。 - 调用:
cheers(count);
执行cheers
函数的代码,并将count
的值复制给cheers
函数的参数n
(这称为按值传递)。 - 定义: 提供了
cheers
函数的具体实现。
2.4.4 用户定义的有返回值的函数
我们可以定义自己的函数来执行计算并返回结果。只需将 returnType
指定为期望的返回类型,并在函数体中使用 return
语句返回一个该类型的值。
用法与示例:
1 |
|
- 原型:
double cube(double x);
声明了函数的接口。 - 调用:
cube(side)
调用函数,side
的值被传递给参数x
。函数执行后返回一个double
值。 - 定义:
double cube(double x)
提供了函数的实现。return result;
将计算出的立方值返回给调用者。
2.4.5 在多函数程序中使用using编译指令
当程序包含多个函数时,每个函数都需要访问 std
名称空间中的元素(如 cout
, cin
, endl
)。有几种处理方式:
- 在每个函数中都使用
std::
前缀: 这是最安全的方式,尤其是在头文件中,但可能使代码冗长。1
2
3
4
5
6
7
8void func1() {
std::cout << "Hello from func1\n";
}
void func2() {
int x;
std::cin >> x;
std::cout << "Input in func2: " << x << std::endl;
} - 在每个需要访问
std
的函数内部使用using
声明或using namespace std;
: 这将using
的作用域限制在函数内部,减少了命名冲突的风险。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void func1() {
using namespace std; // using 指令只在 func1 内部有效
cout << "Hello from func1\n";
}
void func2() {
using std::cin; // using 声明只引入 cin
using std::cout;
using std::endl;
int x;
cin >> x;
cout << "Input in func2: " << x << endl;
}
int main() {
// 在 main 中也需要访问 std
using namespace std;
func1();
func2();
cout << "Back in main." << endl;
return 0;
} - 在所有函数定义之前(通常是在所有
#include
之后)放置一个using namespace std;
指令: 这使得文件中的所有后续代码都可以直接使用std
中的名称,无需std::
前缀。这种方式最简单,但在大型项目中或编写头文件时不推荐,因为它可能引入全局命名冲突。 对于学习和小型项目,这通常是可接受的。
用法与示例 (全局 using 指令):
1 |
|
选择哪种方式取决于项目的规模和个人/团队的编码规范。对于初学者编写的简单多函数程序,将 using namespace std;
放在 #include
之后是一种常见的简化方法。
2.5 总结
本章引导我们迈出了学习C++的第一步,涵盖了编写、编译和理解一个基本C++程序所需的 foundational concepts。
我们从C++程序的核心——main()
函数开始,它是程序的入口点。了解了如何使用注释(//
和 /* */
)来提高代码的可读性。接着,我们接触了C++预处理器,特别是 #include
指令,它用于包含头文件(如 <iostream>
),这些头文件提供了函数和对象的声明。我们区分了标准库头文件(用 <>
)和用户自定义头文件(用 ""
)。
名称空间的概念被引入,特别是 std
名称空间,它包含了C++标准库的大部分内容。我们学习了访问 std
中元素的三种方式:使用 std::
前缀、using
声明和 using
编译指令,并讨论了它们的适用场景和潜在风险。
我们重点学习了如何使用 iostream
库中的 cout
对象和插入运算符 <<
来显示各种类型的数据(字符串、整数、浮点数等),以及如何使用 endl
或 \n
进行换行。代码格式化的重要性也被强调,以保证代码清晰、易于维护。
随后,我们学习了C++的基本语句类型。声明语句用于创建变量,指定其类型和名称,并可以选择在声明时进行初始化。赋值语句使用 =
运算符将值存储到变量中。我们还看到了 cout
如何智能地处理不同数据类型,以及如何通过链式调用 <<
来拼接输出。
输入操作通过 cin
对象和提取运算符 >>
实现,允许程序从用户那里读取数据并存储到变量中。
最后,我们初步探讨了函数。我们学习了如何调用库函数(如 <cmath>
中的 sqrt()
)并使用它们的返回值。我们了解了函数的不同变体(有/无参数,有/无返回值)。更重要的是,我们学习了如何定义自己的函数,包括编写函数原型(声明)和函数定义(实现),以及如何通过函数调用来执行它们。我们还讨论了如何在包含多个函数的程序中管理 std
名称空间的使用。
通过本章的学习,我们已经能够编写简单的C++程序,实现基本的输入、处理和输出功能,并对C++程序的结构和一些核心概念有了初步的认识。