跳转到内容

第二章 程序的基本组成

  1. C++程序的三部分结构(注释、环境设置、主程序)
  2. 程序注释的两种写法(// 和 /* */)
  3. 库包含格式(<> 系统库 vs "" 用户库)
  4. 名字空间的作用与使用(using namespace std;)
  5. 变量定义格式、命名规则、初始化方式
  6. 基本数据类型(整型、实型、字符型、布尔型、枚举)及表示范围
  7. 整型的三种码制(原码、反码、补码)
  8. cin/cout 的使用及格式化输入输出
  9. 算术运算符、优先级、类型转换规则
  10. 强制类型转换与自增自减运算符

  • C++程序的基本组成分为三部分:

    1. 程序注释:描述程序操作过程的说明
    2. 环境设置:预处理命令 + 名字空间声明
    3. 主程序:程序要执行的命令序列

    [来源:第2章 p5,C++程序的基本组成]

  • Hello World 程序结构:

    // 程序注释
    #include <iostream> // 预处理命令
    using namespace std; // 名字空间
    int main(){ // 主程序
    cout << "hello world!" << endl;
    return 0;
    }

    [来源:第2章 p5]

  • 大多数语句(statement)以分号 ; 结束,但预处理指令(如 #include)、复合语句/语句块({})、以及控制结构(如 if/for/while 后接 {})等不以分号结尾;另外 class/struct/enum 的声明末尾需要分号。 [来源:第2章 p5,Notes]

  • 每个程序必须有一个名为 main 的函数,它是程序的入口 [来源:第2章 p11,主程序]

  • 在常见的宿主环境(hosted)下,一个可执行程序整体必须且只能有一个 main 作为入口(多文件工程中也只能在某一个源文件里定义一次);在某些**裸机/嵌入式(freestanding)**环境下标准不强制必须有 main。 [来源:第2章 p11,Notes]


  • 程序注释:描述程序操作过程的说明,会被编译器忽略 [来源:第2章 p6,程序注释]

  • 两种注释方式:

    1. 单行注释:从 // 开始到本行结束
    2. 多行注释:/* 与 */ 之间的所有文字,可跨行

    [来源:第2章 p6]

  • 注释可以出现在程序的任何位置 [来源:第2章 p6]

  • /* */ 方式不能嵌套使用 [来源:第2章 p6,Notes]

  • 良好的注释习惯:

    • 每个程序文件顶端:说明程序文件的功能
    • 每个程序模块:说明功能、输入输出变量
    • 难以理解的地方:及时添加说明

    [来源:第2章 p6,Notes]


  • 预处理命令的主要功能:

    1. 库包含:使用其他地方定义好的功能模块
    2. 宏定义:定义符号常量或复杂操作

    [来源:第2章 p7,编译预处理]

  • 库包含格式:

    #include <filename> // 通常用于:按**系统/配置的包含路径**搜索头文件
    #include "filename" // 通常用于:先按**当前/项目路径**搜索,再按系统包含路径搜索

    [来源:第2章 p8,库包含的格式]

  • 宏定义格式:

    #define 标识符 替换文本
    // 例如:
    #define RADIUS 5
    #define PI 3.14159
    #define AREA (PI*(RADIUS)*(RADIUS)) // 宏替换建议加括号,避免优先级问题

    [来源:第2章 p9,宏定义]

  • **库(头文件)**在预处理时被放入源代码中,用于对库调用做初步检查 [来源:第2章 p8,Notes]
  • ⚠️ 宏定义的缺点:

    1. 编译器不会对宏进行类型检测,程序容易出错
    2. 每个使用部分会被简单替换,造成代码膨胀

    [来源:第2章 p9,宏定义]

  • C++更好的方案:用 const 定义全局常量

    const double PI = 3.14159;

    [来源:第2章 p9]


  • 名字空间:由一组程序实体组合成的一个作用域 [来源:第2章 p10,名字空间]

  • 格式:

    using namespace 名字空间名;

    [来源:第2章 p10]

  • 用于解决多人开发时的重名问题:

    • 同一名字空间中的实体不能重名
    • 不同名字空间中可以定义相同的实体名

    [来源:第2章 p10]

  • 跨名字空间调用时,需用限定符 :::

    std::cout << "hello world!" << endl;

    [来源:第2章 p10]

  • C++标准库中的实体都定义在 std 名字空间中 [来源:第2章 p10]


2.5 主程序(main函数)

跳转到:2.5 主程序(main函数)
  • 函数头:int main()

    • int:函数返回结果的数据类型
    • main:函数名
    • ():函数输入变量(空表示不需要输入)

    [来源:第2章 p11,Notes]

  • 函数体:用大括号 {} 括起来的命令序列 [来源:第2章 p11]


  • 变量:存放可变数据的空间 [来源:第2章 p15,变量定义]

  • 变量的三个重要属性:类型、名称、值 [来源:第2章 p15]

  • 变量定义格式:

    类型名 变量名1, 变量名2, ..., 变量名n;
    // 例如:
    int num1, num2; // 定义两个int类型变量
    double area; // 定义一个double型变量

    [来源:第2章 p15]

  • 在C++中,每个变量在使用前必须被定义,以便编译器检查变量使用的合法性 [来源:第2章 p15]

  • 强制规则:

    1. 必须以字母或下划线开头
    2. 其他字符必须是字母、数字或下划线
    3. 不得使用空格或其他特殊符号
    4. 大写和小写字母被看作不同字符(ABC、Abc、abc 是三个不同名字)
    5. 不可以是系统保留词(如 int, double, for, return 等)

    [来源:第2章 p16,变量命名]

  • 习惯约定:起名应使读者易于明白其用途,做到"见名知意"

    • 常用缩写:number → num,function → func,Number of dimensions → num_dims / nDims

    [来源:第2章 p16]


  • 两种初始化格式:
    类型名 变量名 = 数值; // 等号赋值
    类型名 变量名(数值); // 括号赋值
    // 例如:
    int count = 0; // 或 int count(0);
    int sum = 0, count = 0, num; // 部分变量赋初值
    [来源:第2章 p17,变量初始化]
  • ⚠️ 使用未初始化变量有危险! 对于函数内的局部自动变量(非 static),定义后若不显式初始化,其值通常不确定;但全局变量/静态存储期变量(如全局变量、static 局部变量)会被零初始化。 [来源:第2章 p17]

  • 数据类型的作用:定规则

    1. 怎么把二进制数翻译成各种字母、数字、特殊符号
    2. 怎么对变量进行计算上的操作

    [来源:第2章 p18,数据类型]

  • 数据类型种类:

    1. 基本类型(内置类型)
    2. 自定义类型

    [来源:第2章 p18]

  • 基本类型分类:

大类具体类型
整型int, short, long, unsigned int, unsigned short, unsigned long
实型float, double, long double
字符型char
布尔型bool

[来源:第2章 p18]


  • 整型占用内存与表示范围(与平台/编译器/ABI 有关,标准只规定下限;可用 sizeof/numeric_limits 实测):
类型标准保证(至少)常见实现举例(仅供参考)
short≥ 16 位常见:2 字节
int≥ short,且 ≥ 16 位常见:4 字节
long≥ 32 位,且 ≥ intWindows 64 位常见:4 字节(LLP64);Linux/macOS 64 位常见:8 字节(LP64)
long long≥ 64 位常见:8 字节
  • 无符号整数:范围为 0 ~ (2^N - 1),其中 N 为对应类型的位数(例如 unsigned int 若为 32 位则为 0~2^32-1;若为 16 位则为 0~2^16-1)。

[来源:第2章 p20]

  • 整数支持的运算:

    • 算术运算:++, --, -(负号), *, /, %, +, -
    • 逻辑运算:&&, ||, !
    • 关系运算:>, <, ==, >=, <=, !=

    [来源:第2章 p21,整型数的运算]

  • ⚠️ 整数溢出/越界:在 C++ 中,有符号整型发生溢出属于未定义行为(不能依赖“会回绕成 -32768”这类结果);而无符号整型溢出是按模 2^N 回绕(语言有明确定义)。编译器通常也不会主动提示,需要程序员避免或显式处理。

    [来源:第2章 p21]


  • 符号位数字化:0表示正数,1表示负数 [来源:第2章 p22]

  • 三种编码方式(以一个字节为例):

编码定义+62-62
原码符号位+数值的二进制0 01111101 0111110
反码正数同原码,负数=绝对值原码取反0 01111101 1000001
补码正数同原码,负数=反码+10 01111101 1000010

[来源:第2章 p22]

  • 【码制转换练习】

    • [90]原=01011010[90]_原 = 01011010,[−78]原=11001110[-78]_原 = 11001110
    • [90]反=01011010[90]_反 = 01011010,[−78]反=10110001[-78]_反 = 10110001
    • [90]补=01011010[90]_补 = 01011010,[−78]补=10110010[-78]_补 = 10110010

    [来源:第2章 p22,Quiz]

  • 为什么用补码:

    1. 符号位可以和数值位一起参加运算
    2. 0的表示唯一(00000...00),多出一个最小负数

    [来源:第2章 p22,Notes]


2.12 实型(浮点型)

跳转到:2.12 实型(浮点型)
  • 实型数表示:a×2ba \times 2^b 的形式(aa 为尾数,bb 为指数)

    • 尾数表示有效数值
    • 指数表示小数点位置
    • 又称浮点表示法

    [来源:第2章 p23]

  • 实型占用内存:

类型占用空间精确度范围
float4字节7位−1038-10^{38} ~ 103810^{38}
double8字节16位−10308-10^{308} ~ 1030810^{308}

[来源:第2章 p24]

  • ⚠️ 浮点数很多情况下只是近似表示,而不是精确值 [来源:第2章 p24,Notes]

  • 字符类型(char):存放一个字母或符号,占一个字节 [来源:第2章 p25,数据类型-字符型]

  • 字符的机内表示用字符编码,常用的有 ASCII [来源:第2章 p25]

  • 字符赋值方式:

    char c1, c2;
    c1 = 'a'; // 字符输入(用单引号)
    c1 = 97; // 编码输入(ASCII码值)

    [来源:第2章 p27,可打印字符的使用]

  • 字符型变量可执行的运算与整型雷同 [来源:第2章 p26]
  • 【字符运算】

    • c = 9 与 c = '9' 不同:前者是整数9,后者是字符'9'(ASCII=57)
    • 若 c1 = 'a',则 c1 + 2 为 'c'(即第几个字母+2)
    • 若 c 是小写字母,则 c - 'a' + 1 表示第几个字母
    • 若 c 是数字字符('0'~'9'),则 c - '0' 表示数字本身
    • 若 c1, c2 是小写字母,则 c2 - c1 表示字母间的距离

    [来源:第2章 p27]


  • 转义字符:用反斜杠 \ 加字符表示非打印或难打印的字符
字符形式含义
\n换行
\t水平制表
\b退一格
\r回车
\f换页

[来源:第2章 p28,转义字符]

  • '\n' 虽然写成两个符号(反斜杠 + 字母),但它表示一个字符常量(换行字符),具体编码值取决于执行字符集/平台。 [来源:第2章 p28]

  • 布尔型(bool):表示真伪,占一个字节 [来源:第2章 p29]

  • 布尔值:true(内部表示为1)、false(内部表示为0) [来源:第2章 p29]

  • 布尔型数据可以进行算术、关系、逻辑运算 [来源:第2章 p29]

  • ✅ bool 可以直接输入/输出:默认情况下 cout 输出 0/1;配合 std::boolalpha 可输出/输入 true/false。有时也会用整型做“周转”来实现自定义格式或兼容旧接口,但不是必须。 [来源:第2章 p29]


  • 定义枚举类型:

    enum 枚举类型名 {元素表};
    // 例如:
    enum weekdayT {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday};

    [来源:第2章 p31,定义新的枚举类型]

  • 定义枚举变量:

    weekdayT weekday;

    [来源:第2章 p31]

  • 内部表示:采用整数编码,默认从0开始

    // 默认:0代表Sunday,1代表Monday,...,6代表Saturday

    [来源:第2章 p32,枚举类型的内部表示]

  • 枚举变量使用:

    • 赋值:weekday = Friday;
    • 比较:Monday < Friday(比较内部整数值)

    [来源:第2章 p32]

  • ⚠️ 枚举的输入输出要分情况:**非限定枚举(enum)**通常可隐式转为整数,cout << e; 往往会输出其底层整数值;**限定枚举(enum class)**不允许隐式转换,需 static_cast 输出/比较底层值。输入一般用“读整数再转换”或自定义映射更稳妥。 [来源:第2章 p32]


  • 常量:在编写程序时,值已确定且不允许修改的量 [来源:第2章 p33,整型常量]

  • 整型常量表示:

    • 十进制:123, -234
    • 八进制:以 0 开头,如 0123
    • 十六进制:以 0x 或 0X 开头,如 0x123, 0x3a2f

    [来源:第2章 p33]

  • 浮点数常量表示:

    • 十进制:1.23, 3.14, -5.988
    • 科学计数法:123e2(=12300),2.25e-3(=0.00225)

    [来源:第2章 p34,浮点数常量]

  • 字符常量:用一对单引号括起来,如 'a', 'S', '2' [来源:第2章 p35,字符常量]

  • ⚠️ 科学计数法要求:

    • 尾数/指数不能为空:e3, 2e 是非法的
    • 指数必须为整数:2.5e2.3 是非法的

    [来源:第2章 p34]


2.18 符号常量(const)

跳转到:2.18 符号常量(const)
  • C++建议用 const 定义符号常量:
    const <类型名> <常量名> = <值>;
    // 例如:
    const double PI = 3.1415926;
    [来源:第2章 p36,符号常量]
  • const 定义后,内容不能被改写 [来源:第2章 p36,Notes]

  • 相比宏定义,const 能解决类型检查、代码执行效率低的问题 [来源:第2章 p36,Notes]


2.19 类型别名与sizeof

跳转到:2.19 类型别名与sizeof
  • 类型别名:给类型重新命名

    typedef 原名 现名;
    // 例如:
    typedef int INTEGER;
    typedef double REAL;
    INTEGER n1 = 10; // 等价于 int n1 = 10;

    [来源:第2章 p37,类型别名]

  • sizeof运算符:了解某类型或表达式占用的内存量(字节)

    sizeof(类型名) // 或 sizeof(表达式)
    sizeof(float) // float类型占用的内存量
    sizeof('a' + 15) // 表达式结果所占内存量

    [来源:第2章 p38,了解占用的内存量]


  • 输出流对象 cout:代表标准输出设备(一般是显示器) [来源:第2章 p40,Cout输出]

  • 流运算符:<<(箭头指向数据流动方向) [来源:第2章 p40]

  • 输出格式:

    #include <iostream>
    cout << a; // 输出一个变量
    cout << a << b << c; // 输出多个变量
    cout << 12345; // 输出常量
    cout << a << '+' << b << '=' << a+b << endl; // 组合输出

    [来源:第2章 p40]


  • 输入流对象 cin:代表标准输入设备(一般是键盘) [来源:第2章 p42,Cin输入]

  • 流运算符:>> [来源:第2章 p42]

  • 输入格式:

    #include <iostream>
    cin >> 变量;
    cin >> 变量1 >> 变量2 >> ... >> 变量n;

    [来源:第2章 p42]

  • 程序执行到输入语句时会停下来等待用户输入 [来源:第2章 p42]

  • 用回车结束输入 [来源:第2章 p42]

  • 多个输入数据时,用空白字符(空格、制表符、回车)分隔 [来源:第2章 p42]


  • 从键盘接受一个字符(不支持其他数据类型):
    char ch;
    cin.get(ch); // 方式1
    ch = cin.get(); // 方式2
    [来源:第2章 p43,cin.get]
  • 【执行结果分析】
    char a, b, c;
    a = cin.get();
    b = cin.get();
    c = cin.get();
    // 输入 "1 2 3↙",结果:a='1', b=' ', c='2'
    cin >> a >> b >> c;
    // 输入 "1 2 3↙",结果:a='1', b='2', c='3'
    要点:cin.get() 读取包括空格在内的每个字符;cin >> 会跳过空白字符 [来源:第2章 p43]

2.23 格式化输入/输出

跳转到:2.23 格式化输入/输出
  • 使用带参数的流操纵符需包含:

    #include <iomanip>

    [来源:第2章 p44]

  • 设置域宽(setw):

    int x, y;
    cin >> x >> y;
    cout << setw(5) << x << setw(5) << y << endl;
    // 输出 " 123 456"(各占5位)

    [来源:第2章 p44,格式化输入/输出]

  • 设置精度(setprecision 或 precision):

    double x = 123.456789;
    cout.precision(8);
    cout << x << endl; // 输出 123.45679
    cout << setprecision(5) << x; // 输出 123.46

    [来源:第2章 p45-46]

  • 设置基数:

    cout << hex << n; // 十六进制输出
    cout << oct << n; // 八进制输出
    cout << dec << n; // 十进制输出
    cout << setbase(8); // 设置为八进制

    [来源:第2章 p47-48]

  • setw 设置的宽度仅适用于下一次输入或输出,之后恢复默认 [来源:第2章 p44]

  • 精度设置会影响之后所有浮点数输出,直到下一次设置 [来源:第2章 p45]

  • 基数设置会持续有效,直到被更改 [来源:第2章 p47]


  • 算术运算符:+, -, *, /, % [来源:第2章 p53,算术表达式]

  • 优先级:

    • 高:*, /, %
    • 低:+, -
    • 括号内优先,同级从左到右

    [来源:第2章 p53]

  • 运算对象可以是整型、浮点型、字符型和布尔型 [来源:第2章 p53]


2.25 混合运算与类型转换

跳转到:2.25 混合运算与类型转换
  • C++允许不同类型进行算术运算:编译器会按“整型提升 + 常规算术转换(usual arithmetic conversions)”把运算数转换为共同类型后再计算;结果类型通常是该共同类型(如 int + double 会把 int 转为 double,结果为 double)。 [来源:第2章 p55,各种类型的数据的混合运算]

  • 转换总原则:

    1. 非标准类型转换成标准类型(bool, char, short → int)
    2. 占用空间少的转换成占用空间多的
    3. 数值范围小的转换成数值范围大的

    [来源:第2章 p55]

  • 具体规则:

    • int 和 long long 运算时,int → long long
    • int 和 double 运算时,int → double

    [来源:第2章 p55]

  • 【混合运算示例】

    • 5/6*11.0 结果为 0.0(5/6先算得0,再转double)
    • 11.0*5/6 结果为 9.166...(11.0*5先算得55.0,再除6)

    [来源:第2章 p55,Notes]


  • 赋值表达式格式:

    <变量> = <表达式>

    [来源:第2章 p56,变量赋值]

  • 作用:将右边表达式的值存入左边的变量 [来源:第2章 p56]

  • x = x + 2 是正确的表达式(被赋值变量可出现在右侧) [来源:第2章 p56]

  • 赋值时的自动类型转换规则:

转换更准确的规则(按 C++ 常见语义表述)
浮点 → 整型截断小数部分(向 0 取整);若结果超出目标整型可表示范围,行为可能未定义/实现定义(需避免)。
整型 → 浮点数值尽量保持;若目标浮点精度不足,会发生舍入;数值过大可能变为 inf。
double → float舍入到最接近的 float 可表示值(精度变差;可能溢出为 inf)。
float → double精确值不变(double 能精确表示所有 float 值),只是表示范围/精度更大。
char/signed char/unsigned char → int发生整型提升(integral promotion),结果为对应字符的数值;符号扩展/零扩展取决于原类型是否有符号。
int → char若值可表示则得到对应字符值;若不可表示,结果为实现定义/未定义风险(不应依赖“取低 8 位”这种假设)。

[来源:第2章 p57,赋值时的自动类型转换]


  • 强制类型转换格式:

    (类型名) 表达式 // C风格
    类型名 (表达式) // 函数风格
    // 例如:
    int x = 5, y = 8;
    double z;
    z = (double) x / y; // z = 0.625

    [来源:第2章 p59,强制类型转换]

  • C++指明转换性质的方式:

    • static_cast<T>(expr):用于有明确定义的类型转换(数值类型转换、上行/下行转换的一部分场景等);比 C 风格转换更可控,但仍可能丢精度/截断。
    • reinterpret_cast<T>(expr):用于低层次重解释(多用于指针/位级别场景),可移植性差,风险高。
    • const_cast<T>(expr):仅用于添加/移除 const/volatile 限定;若对象本来就是常量,对其“去 const 后写入”是未定义行为。
    • dynamic_cast<T>(expr):用于**多态类型(含虚函数)**的安全向下转型/运行期检查;失败会返回 nullptr(指针)或抛异常(引用)。

    [来源:第2章 p60,转换类型]

  • 【强制转换练习】
    double x, y;
    x = (double)5 + 6/11; // x = 5.0(6/11=0)
    y = (double)(5 + 6/11); // y = 5.0(5+0=5,再转double)
    [来源:第2章 p59]
  • ⚠️ 强制类型转换很可能导致错误,尤其涉及自定义类和指针时 [来源:第2章 p60]

  • 建议:定义变量时尽量回避,报错后才考虑使用 [来源:第2章 p60,Notes]


2.28 复合赋值与多重赋值

跳转到:2.28 复合赋值与多重赋值
  • 多重赋值:

    int a, b, c;
    a = b = c = 5; // 给a, b, c均赋值5

    [来源:第2章 p61,更多关于赋值和运算]

  • 复合赋值运算:

    balance += deposit; // balance = balance + deposit;
    balance -= surcharge; // balance = balance - surcharge;
    x /= 10; // x = x / 10;
    salary *= 2; // salary = salary * 2;

    [来源:第2章 p61]

  • ⚠️ 赋值是右结合运算符:
    double d;
    int i;
    d = i = 1.5; // i等于1,d等于1.0(先算i=1.5得i=1,再d=i得d=1.0)
    [来源:第2章 p61]

2.29 自增自减运算符

跳转到:2.29 自增自减运算符
  • 自增(++)、自减(--):让变量 +1 或 -1 [来源:第2章 p62,自增、自减运算符]

  • 前缀与后缀的区别:

表达式执行后 i 的值表达式的值(赋给 j)
j = i++(i=3)i=4j=3(先用后加)
j = ++i(i=3)i=4j=4(先加后用)
j = i--(i=3)i=2j=3(先用后减)
j = --i(i=3)i=2j=2(先减后用)

[来源:第2章 p62]


  • 使用数学函数需包含:

    #include <cmath>

    [来源:第2章 p63,数学函数库]

  • 常用函数:

功能函数
绝对值推荐使用 std::abs(x)(对整数/浮点均有重载);std::fabs(x) 也可用于浮点(更偏 C 风格接口)。
exe^xdouble exp(double x);
xyx^ydouble pow(double x, double y);
x\sqrt{x}double sqrt(double x);
ln⁡x\ln xdouble log(double x);
log⁡10x\log_{10} xdouble log10(double x);
三角函数sin(x), cos(x), tan(x)
反三角函数asin(x), acos(x), atan(x)

[来源:第2章 p64,cmath的主要内容]


本章介绍了C++程序的总体结构及工作方式,具体包括:

  1. 如何保存用户的数据(变量定义)
  2. 读取用户提供的数值数据(cin 输入)
  3. 在屏幕上显示文本和数据(cout 输出)
  4. 对现有数据进行算术运算得到新的结果

[来源:第2章 p65,总结]


附:运算符优先级表

跳转到:附:运算符优先级表

详见 [来源:第2章 p66-68]