C++ Primer 第2章:变量和基本类型
2.1 基本内置类型
2.1.1 算术类型
C++ 定义了一套包括算术类型 和空类型在内的基本数据类型。
算术类型分类:
┌─────────────────────────────────────────────┐
│ 整型(含字符和布尔类型) │
│ bool / char / wchar_t / char16_t / char32_t │
│ short / int / long / long long │
├─────────────────────────────────────────────┤
│ 浮点型 │
│ float / double / long double │
└─────────────────────────────────────────────┘
算术类型的大小(典型64位系统):
| 类型 | 含义 | 最小尺寸 |
|---|---|---|
bool |
布尔类型 | 未定义 |
char |
字符 | 8位 |
wchar_t |
宽字符 | 16位 |
char16_t |
Unicode字符 | 16位 |
char32_t |
Unicode字符 | 32位 |
short |
短整型 | 16位 |
int |
整型 | 16位 |
long |
长整型 | 32位 |
long long |
长长整型 | 64位 |
float |
单精度浮点 | 6位有效数字 |
double |
双精度浮点 | 10位有效数字 |
long double |
扩展精度浮点 | 10位有效数字 |
cpp
// type_sizes.cpp -- 查看各类型大小
#include <iostream>
#include <climits>
#include <cfloat>
int main()
{
using namespace std;
cout << "===== 整型大小 =====" << endl;
cout << "bool: " << sizeof(bool) << " 字节" << endl;
cout << "char: " << sizeof(char) << " 字节" << endl;
cout << "short: " << sizeof(short) << " 字节" << endl;
cout << "int: " << sizeof(int) << " 字节" << endl;
cout << "long: " << sizeof(long) << " 字节" << endl;
cout << "long long: " << sizeof(long long) << " 字节" << endl;
cout << "\n===== 浮点型大小 =====" << endl;
cout << "float: " << sizeof(float) << " 字节" << endl;
cout << "double: " << sizeof(double) << " 字节" << endl;
cout << "long double: " << sizeof(long double) << " 字节" << endl;
cout << "\n===== 整型范围 =====" << endl;
cout << "int 最小值:" << INT_MIN << endl;
cout << "int 最大值:" << INT_MAX << endl;
cout << "unsigned int 最大值:" << UINT_MAX << endl;
cout << "long long 最大值:" << LLONG_MAX << endl;
cout << "\n===== 浮点型精度 =====" << endl;
cout << "float 有效位数:" << FLT_DIG << endl;
cout << "double 有效位数:" << DBL_DIG << endl;
return 0;
}
2.1.2 类型选择建议
cpp
// type_selection.cpp -- 类型选择示例
#include <iostream>
int main()
{
// ✅ 建议:
// 1. 明确非负时,使用 unsigned
// 2. 整数运算用 int,超出范围用 long long
// 3. 浮点运算用 double(float精度不够,long double开销大)
// 4. 算术表达式中不要混用 signed 和 unsigned
unsigned int u = 10;
int i = -1;
// ⚠️ 危险:signed 和 unsigned 混用
// i < u 中,i 被转换为 unsigned,-1 变成很大的正数!
if (i < (int)u) // 正确:显式转换
std::cout << "i < u" << std::endl;
// unsigned 下溢
unsigned int x = 0;
x--; // 变成 4294967295(UINT_MAX)
std::cout << "0u - 1 = " << x << std::endl;
return 0;
}
2.1.3 类型转换
cpp
// type_conversion.cpp -- 类型转换
#include <iostream>
int main()
{
using namespace std;
// 隐式类型转换
bool b = 42; // b = true(非零即真)
int i = b; // i = 1
double d = i; // d = 1.0
unsigned char uc = -1; // uc = 255(取模)
cout << "bool(42) = " << b << endl; // 1
cout << "int(true) = " << i << endl; // 1
cout << "double(1) = " << d << endl; // 1
cout << "uchar(-1) = " << (int)uc << endl; // 255
// 浮点转整数:截断小数部分
int truncated = 3.99;
cout << "int(3.99) = " << truncated << endl; // 3
// 整数转浮点:可能损失精度
int big = 123456789;
float f = big;
cout << "float(123456789) = " << f << endl; // 可能不精确
// 赋值给 bool
bool b1 = 0; // false
bool b2 = -1; // true(任何非零值)
bool b3 = 3.14; // true
cout << boolalpha;
cout << "bool(0) = " << b1 << endl;
cout << "bool(-1) = " << b2 << endl;
cout << "bool(3.14) = " << b3 << endl;
return 0;
}
2.1.4 字面值常量
cpp
// literals.cpp -- 字面值常量
#include <iostream>
int main()
{
using namespace std;
// ===== 整型字面值 =====
int decimal = 20; // 十进制
int octal = 024; // 八进制(0开头)
int hex = 0x14; // 十六进制(0x开头)
cout << "十进制 20 = " << decimal << endl;
cout << "八进制 024 = " << octal << endl; // 20
cout << "十六进制 0x14 = " << hex << endl; // 20
// 整型字面值后缀
auto a = 42; // int
auto b = 42u; // unsigned int
auto c = 42l; // long
auto d = 42ul; // unsigned long
auto e = 42ll; // long long
auto f = 42ull; // unsigned long long
// ===== 浮点字面值 =====
double d1 = 3.14; // double(默认)
float f1 = 3.14f; // float(f/F后缀)
double d2 = 3.14e2; // 科学计数法:314.0
double d3 = 3.14E-2; // 0.0314
long double ld = 3.14L; // long double(L后缀)
cout << "3.14e2 = " << d2 << endl;
cout << "3.14E-2 = " << d3 << endl;
// ===== 字符和字符串字面值 =====
char c1 = 'a'; // 字符字面值(单引号)
char c2 = '\n'; // 转义字符
char c3 = '\x41'; // 十六进制转义:'A'
char c4 = '\101'; // 八进制转义:'A'
const char* s1 = "Hello"; // 字符串字面值(双引号)
// 字符串字面值末尾自动添加 '\0'
cout << "字符:" << c1 << " " << c3 << " " << c4 << endl;
cout << "字符串:" << s1 << endl;
// 字符串字面值拼接(相邻字符串自动合并)
const char* longStr = "Hello, "
"World"
"!";
cout << longStr << endl;
// ===== 布尔字面值 =====
bool t = true;
bool fa = false;
cout << boolalpha << t << " " << fa << endl;
// ===== 指针字面值 =====
int* p = nullptr; // 空指针字面值(C++11)
return 0;
}
常用转义字符:
| 转义字符 | 含义 | 转义字符 | 含义 |
|---|---|---|---|
\n |
换行 | \\ |
反斜线 |
\t |
横向制表符 | \' |
单引号 |
\r |
回车 | \" |
双引号 |
\0 |
空字符 | \a |
响铃 |
\b |
退格 | \v |
纵向制表符 |
2.2 变量
2.2.1 变量定义
cpp
// variable_def.cpp -- 变量定义
#include <iostream>
#include <string>
int main()
{
using namespace std;
// 基本定义
int sum = 0;
int value; // 未初始化(局部变量,值未定义!)
double salary = 9999.99;
string name = "Alice";
// 列表初始化(C++11,推荐)
int units_sold{0}; // 花括号初始化
double price{1.99};
string greeting{"Hello"};
// 列表初始化的优势:防止窄化转换
// int x{3.14}; // ❌ 编译错误!double→int是窄化转换
int x = 3.14; // ✅ 但会截断(x=3),有警告
// 默认初始化
// 全局变量:自动初始化为0
// 局部变量:未初始化,值未定义(危险!)
// 同时定义多个变量
int i = 0, j = 0, k; // i和j初始化,k未初始化
double price1 = 1.0, price2 = 2.0;
cout << "sum = " << sum << endl;
cout << "salary = " << salary << endl;
cout << "name = " << name << endl;
return 0;
}
⚠️ 初始化 vs 赋值:
- 初始化:创建变量时赋予初始值
- 赋值:擦除当前值,用新值替代
- 两者在语法上相似,但概念不同
2.2.2 变量声明与定义
cpp
// declaration_definition.cpp -- 声明与定义
// 声明:使名字为程序所知
// 定义:创建与名字关联的实体
// file1.cpp(定义)
int global_var = 42; // 定义并初始化
// file2.cpp(声明,使用extern)
extern int global_var; // 声明,不是定义
// extern int x = 10; // 有初始化器的extern是定义
// 在同一文件中演示
extern int declared_var; // 声明(定义在其他地方)
// 函数内的声明与定义
int main()
{
using namespace std;
int defined_var = 10; // 定义(同时也是声明)
// extern 在函数内部
extern int global_var; // 声明外部变量
std::cout << "global_var = " << global_var << std::endl;
return 0;
}
// 全局定义
int global_var = 42;
int declared_var = 100;
💡 关键区别:
- 变量只能定义一次 ,但可以声明多次
extern声明不分配内存,定义才分配内存- 在多文件程序中,变量在一个文件中定义,在其他文件中用
extern声明
2.2.3 标识符命名规则
cpp
// naming_rules.cpp -- 命名规则示例
#include <iostream>
int main()
{
// ✅ 合法的标识符
int myVariable = 1;
int _private = 2;
int var123 = 3;
int camelCase = 4;
int snake_case = 5;
// ❌ 非法的标识符(注释掉)
// int 123abc = 1; // 不能以数字开头
// int my-var = 2; // 不能含连字符
// int int = 3; // 不能是关键字
// ⚠️ 保留名称(不要使用)
// 以下划线开头的名称保留给实现使用
// int _Reserved = 1; // 危险!
// int __reserved = 2; // 危险!
// C++ 命名惯例
int localVariable = 0; // 驼峰命名(局部变量)
int local_variable = 0; // 下划线命名(也常用)
const int MAX_SIZE = 100; // 全大写(常量)
// 类名通常首字母大写
// class MyClass { ... };
return 0;
}
2.2.4 名字的作用域
cpp
// scope_demo.cpp -- 作用域示例
#include <iostream>
int reused = 42; // 全局作用域
int main()
{
using namespace std;
// 局部作用域
int unique = 0;
cout << "全局 reused = " << reused << endl; // 42
cout << "局部 unique = " << unique << endl; // 0
{
// 嵌套作用域
int reused = 0; // 遮蔽全局变量
cout << "\n内层 reused = " << reused << endl; // 0(局部)
cout << "全局 reused = " << ::reused << endl; // 42(::访问全局)
cout << "外层 unique = " << unique << endl; // 0(可见)
}
// 内层 reused 已销毁
cout << "\n外层 reused = " << reused << endl; // 42(全局)
return 0;
}
2.3 复合类型
2.3.1 引用
引用是对象的别名,必须初始化,且不能重新绑定。
cpp
// reference_demo.cpp -- 引用详解
#include <iostream>
int main()
{
using namespace std;
int ival = 1024;
int& refVal = ival; // refVal 是 ival 的引用(别名)
// 引用不是对象,没有自己的地址
// 对引用的所有操作都作用于被引用的对象
refVal = 2; // 等价于 ival = 2
int ii = refVal; // 等价于 int ii = ival
cout << "ival = " << ival << endl; // 2
cout << "refVal = " << refVal << endl; // 2
cout << "ii = " << ii << endl; // 2
// 引用的地址就是被引用对象的地址
cout << "&ival = " << &ival << endl;
cout << "&refVal = " << &refVal << endl; // 相同!
// 引用必须初始化
// int& ref2; // ❌ 错误:引用必须初始化
// 引用只能绑定到对象,不能绑定到字面值或表达式
// int& ref3 = 10; // ❌ 错误(非const引用不能绑定右值)
const int& ref4 = 10; // ✅ const引用可以绑定字面值
// 引用的类型必须与被引用对象的类型匹配
double dval = 3.14;
// int& ref5 = dval; // ❌ 错误:类型不匹配
const int& ref6 = dval; // ✅ const引用可以(会创建临时对象)
cout << "ref6 = " << ref6 << endl; // 3(截断)
// 多个引用可以绑定到同一对象
int& r1 = ival;
int& r2 = ival;
int& r3 = r1; // r3 也是 ival 的引用
return 0;
}
2.3.2 指针
指针是指向另一种类型的复合类型,存储对象的内存地址。
cpp
// pointer_demo.cpp -- 指针详解
#include <iostream>
int main()
{
using namespace std;
int ival = 42;
// 声明指针
int* p1; // 未初始化的指针(危险!)
int* p2 = &ival; // p2 指向 ival
int* p3 = p2; // p3 也指向 ival
// 取地址运算符 &
cout << "ival 的地址:" << &ival << endl;
cout << "p2 存储的地址:" << p2 << endl; // 相同
// 解引用运算符 *
cout << "*p2 = " << *p2 << endl; // 42
*p2 = 100; // 通过指针修改 ival
cout << "ival = " << ival << endl; // 100
// 空指针
int* null1 = nullptr; // C++11 推荐
int* null2 = 0; // 等价
// int* null3 = NULL; // C风格,不推荐
if (null1 == nullptr)
cout << "null1 是空指针" << endl;
// void* 指针:可以存放任意类型的地址
double dval = 3.14;
void* pv = &ival; // 存放 int 的地址
pv = &dval; // 存放 double 的地址
// 不能直接解引用 void* 指针
// 指针的指针
int** pp = &p2; // pp 指向 p2(p2 指向 ival)
cout << "**pp = " << **pp << endl; // 100(ival的值)
// 指针与引用的区别
// 1. 引用不是对象,指针是对象
// 2. 引用必须初始化,指针可以不初始化(但危险)
// 3. 引用不能重新绑定,指针可以改变指向
return 0;
}
2.3.3 指针与 const
cpp
// pointer_const.cpp -- 指针与const
#include <iostream>
int main()
{
using namespace std;
int i = 0;
const int ci = 42;
// 1. 指向常量的指针(底层const)
// 不能通过指针修改所指对象的值
const int* p1 = &ci; // 指向const int
const int* p2 = &i; // 也可以指向普通int(但不能通过p2修改i)
// *p1 = 10; // ❌ 错误:不能修改const对象
p1 = &i; // ✅ 可以改变指向
// 2. 常量指针(顶层const)
// 指针本身是常量,不能改变指向
int* const p3 = &i; // const指针,指向int
*p3 = 20; // ✅ 可以修改所指对象
// p3 = &ci; // ❌ 错误:不能改变指向
// 3. 指向常量的常量指针(顶层+底层const)
const int* const p4 = &ci;
// *p4 = 10; // ❌ 不能修改值
// p4 = &i; // ❌ 不能改变指向
cout << "i = " << i << endl;
cout << "ci = " << ci << endl;
cout << "*p3 = " << *p3 << endl; // 20
// 记忆方法:从右向左读
// const int* p → p是指针,指向const int(底层const)
// int* const p → p是const指针,指向int(顶层const)
// const int* const p → p是const指针,指向const int
return 0;
}
2.4 const 限定符
2.4.1 const 基础
cpp
// const_basic.cpp -- const基础
#include <iostream>
int main()
{
using namespace std;
// const 对象必须初始化
const int bufSize = 512;
// bufSize = 512; // ❌ 错误:不能修改const对象
// const 对象默认仅在文件内有效
// 如果要在多个文件间共享:
// 在头文件中:extern const int bufSize = 512;
// 在其他文件中:extern const int bufSize;
// const 与初始化
const int i = 42;
const int j = i; // ✅ 用const初始化const
const int k = i * 2; // ✅ 用const表达式初始化
int x = 10;
const int cx = x; // ✅ 用变量初始化const(运行时确定)
// const 引用
const int& r1 = i; // 对const的引用
// r1 = 20; // ❌ 不能通过const引用修改值
// const 引用可以绑定非const对象
int val = 100;
const int& r2 = val; // ✅
// r2 = 200; // ❌ 不能通过const引用修改
val = 200; // ✅ 可以直接修改val
cout << "r2 = " << r2 << endl; // 200(val改变了)
return 0;
}
2.4.2 constexpr 与常量表达式
cpp
// constexpr_demo.cpp -- constexpr
#include <iostream>
// constexpr 函数:编译时计算
constexpr int square(int x) { return x * x; }
constexpr int cube(int x) { return x * x * x; }
int main()
{
using namespace std;
// 常量表达式:值不会改变,编译时就能得到结果
const int max_files = 20; // 常量表达式
const int limit = max_files + 1; // 常量表达式
int staff_size = 27; // 不是常量表达式(变量)
// const int sz = get_size(); // 不是常量表达式(运行时才知道)
// constexpr 变量:由编译器验证是否是常量表达式
constexpr int mf = 20; // 20是常量表达式
constexpr int lim = mf + 1; // mf+1是常量表达式
// constexpr int sz = staff_size; // ❌ staff_size不是常量表达式
// constexpr 函数
constexpr int s = square(5); // 编译时计算:25
constexpr int c = cube(3); // 编译时计算:27
cout << "square(5) = " << s << endl;
cout << "cube(3) = " << c << endl;
// 用于数组大小(必须是常量表达式)
constexpr int SIZE = 10;
int arr[SIZE]; // ✅
// constexpr 指针:指针本身是常量(顶层const)
constexpr int* p = nullptr; // p是常量指针,指向int
return 0;
}
2.5 处理类型
2.5.1 类型别名
cpp
// type_alias.cpp -- 类型别名
#include <iostream>
#include <string>
// 方式1:typedef(传统方式)
typedef double wages; // wages 是 double 的别名
typedef wages base, *p; // base 是 double,p 是 double*
// 方式2:using(C++11,推荐)
using SI = int; // SI 是 int 的别名
using PtrToChar = char*; // PtrToChar 是 char* 的别名
// 复杂类型别名
typedef int (*FuncPtr)(int, int); // 函数指针类型
using FuncPtr2 = int(*)(int, int); // 等价的using写法
int add(int a, int b) { return a + b; }
int main()
{
using namespace std;
wages hourly = 9.99; // double
base annual = 50000; // double
p ptr = &hourly; // double*
SI count = 42; // int
cout << "hourly = " << hourly << endl;
cout << "annual = " << annual << endl;
cout << "*ptr = " << *ptr << endl;
cout << "count = " << count << endl;
// 函数指针
FuncPtr fp1 = add;
FuncPtr2 fp2 = add;
cout << "fp1(3,4) = " << fp1(3, 4) << endl;
cout << "fp2(3,4) = " << fp2(3, 4) << endl;
// ⚠️ 注意:typedef 与指针的陷阱
typedef char* pstring;
const pstring cstr = nullptr; // cstr 是 char* const(顶层const)
// 不是 const char*!
return 0;
}
2.5.2 auto 类型说明符
cpp
// auto_demo.cpp -- auto类型推断
#include <iostream>
#include <vector>
#include <map>
#include <string>
int main()
{
using namespace std;
// auto 让编译器推断类型
auto i = 0; // int
auto d = 3.14; // double
auto s = string("Hi"); // string
// auto 在复杂类型中的优势
vector<int> v = {1, 2, 3, 4, 5};
auto it = v.begin(); // vector<int>::iterator(无需写长类型名)
map<string, int> m = {{"a", 1}, {"b", 2}};
for (auto& pair : m) // auto& 推断为 pair<const string, int>&
cout << pair.first << "=" << pair.second << " ";
cout << endl;
// auto 会忽略顶层 const
const int ci = 42;
auto a1 = ci; // a1 是 int(忽略顶层const)
auto& a2 = ci; // a2 是 const int&(保留底层const)
// 要保留顶层const,需要显式指定
const auto a3 = ci; // a3 是 const int
// auto 与引用
int x = 10;
auto& r = x; // r 是 int&
r = 20;
cout << "x = " << x << endl; // 20
// auto 推断多个变量(类型必须一致)
auto p = &x, q = &x; // 都是 int*
// auto p2 = &x, d2 = 3.14; // ❌ 错误:类型不一致
return 0;
}
2.5.3 decltype 类型指示符
cpp
// decltype_demo.cpp -- decltype
#include <iostream>
int f() { return 42; }
int main()
{
using namespace std;
// decltype:推断表达式的类型,但不计算表达式
decltype(f()) sum = 0; // sum 的类型是 f() 的返回类型:int
int i = 42, *p = &i, &r = i;
// decltype 与 auto 的区别:
// decltype 保留顶层 const 和引用
const int ci = 0;
decltype(ci) x = 0; // x 是 const int
// x = 1; // ❌ 错误:x是const
decltype(r) y = i; // y 是 int&(r是引用,decltype保留引用)
y = 10;
cout << "i = " << i << endl; // 10(通过y修改了i)
// decltype 与表达式
decltype(i) d1 = 0; // int(i是变量)
decltype((i)) d2 = i; // int&((i)是表达式,结果是左值)
// 注意:decltype((变量)) 总是引用类型!
decltype(i + 0) d3 = 0; // int(i+0是右值表达式)
decltype(*p) d4 = i; // int&(*p是左值)
cout << "d1=" << d1 << " d3=" << d3 << endl;
// 实际应用:模板中推断返回类型
auto add = [](int a, int b) -> decltype(a + b) {
return a + b;
};
cout << "add(3,4) = " << add(3, 4) << endl;
return 0;
}
2.6 自定义数据结构
2.6.1 struct 定义与使用
cpp
// struct_demo.cpp -- 自定义数据结构
#include <iostream>
#include <string>
// 定义结构体
struct Sales_data
{
// 数据成员
std::string bookNo; // 书号
unsigned units_sold = 0; // 销售量(C++11:类内初始值)
double revenue = 0.0; // 收入
// 成员函数
double avg_price() const
{
if (units_sold)
return revenue / units_sold;
return 0;
}
};
// 结构体可以在定义时声明变量
struct Point
{
double x = 0.0;
double y = 0.0;
} origin, *pPoint; // 同时声明变量
int main()
{
using namespace std;
// 初始化结构体
Sales_data data1; // 使用类内初始值
Sales_data data2 = {"978-0-596-52068-7", // 列表初始化
25, 15.99 * 25};
// 访问成员
data1.bookNo = "978-0-201-78345-4";
data1.units_sold = 3;
data1.revenue = 20.97;
cout << "书号:" << data1.bookNo << endl;
cout << "销量:" << data1.units_sold << endl;
cout << "均价:" << data1.avg_price() << endl;
// 结构体赋值
Sales_data data3 = data1; // 逐成员复制
cout << "data3书号:" << data3.bookNo << endl;
// 指针访问
Sales_data* pData = &data1;
cout << "通过指针:" << pData->bookNo << endl;
cout << "通过指针:" << (*pData).bookNo << endl;
// Point 使用
origin.x = 0; origin.y = 0;
Point p1 = {3.0, 4.0};
cout << "p1: (" << p1.x << ", " << p1.y << ")" << endl;
return 0;
}
2.6.2 头文件与预处理器
cpp
// Sales_data.h -- 头文件示例
#ifndef SALES_DATA_H // 头文件保护符
#define SALES_DATA_H
#include <string>
struct Sales_data
{
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
double avg_price() const
{
return units_sold ? revenue / units_sold : 0;
}
};
#endif // SALES_DATA_H
cpp
// use_sales_data.cpp -- 使用头文件
#include <iostream>
#include "Sales_data.h" // 自定义头文件用引号
int main()
{
using namespace std;
Sales_data total;
if (cin >> total.bookNo >> total.units_sold >> total.revenue)
{
Sales_data trans;
while (cin >> trans.bookNo >> trans.units_sold >> trans.revenue)
{
if (total.bookNo == trans.bookNo)
{
total.units_sold += trans.units_sold;
total.revenue += trans.revenue;
}
else
{
cout << total.bookNo << " "
<< total.units_sold << " "
<< total.avg_price() << endl;
total = trans;
}
}
cout << total.bookNo << " "
<< total.units_sold << " "
<< total.avg_price() << endl;
}
else
{
cerr << "没有数据!" << endl;
return -1;
}
return 0;
}
2.7 综合示例:类型系统综合运用
cpp
// type_system.cpp -- 综合示例
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
// 使用 using 定义类型别名
using Score = double;
using Name = std::string;
using ScoreVec = std::vector<Score>;
// 使用 constexpr 定义常量
constexpr int MAX_STUDENTS = 100;
constexpr Score PASS_SCORE = 60.0;
constexpr Score HONOR_SCORE = 90.0;
// 结构体
struct Student
{
Name name;
int id;
ScoreVec scores;
// 计算平均分
Score average() const
{
if (scores.empty()) return 0.0;
Score sum = 0;
for (const Score& s : scores)
sum += s;
return sum / scores.size();
}
// 是否通过
bool passed() const { return average() >= PASS_SCORE; }
// 是否优秀
bool honored() const { return average() >= HONOR_SCORE; }
};
// 使用 auto 和 decltype
auto calcGrade(Score avg) -> char
{
if (avg >= 90) return 'A';
if (avg >= 80) return 'B';
if (avg >= 70) return 'C';
if (avg >= 60) return 'D';
return 'F';
}
int main()
{
using namespace std;
// 初始化列表创建学生
Student students[] = {
{"张三", 1001, {85.0, 92.0, 78.0, 96.0}},
{"李四", 1002, {70.0, 65.0, 72.0, 68.0}},
{"王五", 1003, {95.0, 98.0, 92.0, 97.0}},
{"赵六", 1004, {55.0, 48.0, 60.0, 52.0}}
};
constexpr int N = sizeof(students) / sizeof(students[0]);
cout << "===== 成绩报告 =====" << endl;
cout << left;
// 使用 auto 遍历
for (const auto& s : students)
{
Score avg = s.average();
char grade = calcGrade(avg);
cout << "学号:" << s.id
<< " 姓名:" << s.name
<< " 平均分:" << avg
<< " 等级:" << grade;
if (s.honored()) cout << " ⭐优秀";
else if (s.passed()) cout << " ✓通过";
else cout << " ✗未通过";
cout << endl;
}
// 统计
int passCount = 0, honorCount = 0;
Score totalAvg = 0;
for (const auto& s : students)
{
Score avg = s.average();
totalAvg += avg;
if (s.passed()) passCount++;
if (s.honored()) honorCount++;
}
cout << "\n===== 统计信息 =====" << endl;
cout << "总人数:" << N << endl;
cout << "通过人数:" << passCount << endl;
cout << "优秀人数:" << honorCount << endl;
cout << "班级平均分:" << totalAvg / N << endl;
return 0;
}
输出:
===== 成绩报告 =====
学号:1001 姓名:张三 平均分:87.75 等级:B ✓通过
学号:1002 姓名:李四 平均分:68.75 等级:D ✓通过
学号:1003 姓名:王五 平均分:95.5 等级:A ⭐优秀
学号:1004 姓名:赵六 平均分:53.75 等级:F ✗未通过
===== 统计信息 =====
总人数:4
通过人数:3
优秀人数:1
班级平均分:76.4375
📝 第2章知识点总结
| 知识点 | 核心要点 |
|---|---|
| 算术类型 | 整型(bool/char/short/int/long/long long)、浮点型(float/double/long double) |
| 类型转换 | 隐式转换可能丢失精度,signed/unsigned混用危险,bool非零即真 |
| 字面值 | 整型(十进制/八进制0/十六进制0x)、浮点(默认double)、字符(单引号)、字符串(双引号+\0) |
| 变量初始化 | 列表初始化{}防止窄化转换,局部变量未初始化值未定义 |
| 声明 vs 定义 | 声明使名字可见,定义分配内存;extern声明不定义 |
| 引用 | 对象的别名,必须初始化,不能重新绑定,不是对象 |
| 指针 | 存储地址,可以为空(nullptr),可以改变指向 |
| 顶层/底层const | 顶层const:对象本身是常量;底层const:指向的对象是常量 |
| constexpr | 编译时常量,由编译器验证,可用于数组大小等 |
| typedef/using | 类型别名,using更清晰(C++11推荐) |
| auto | 类型推断,忽略顶层const,简化复杂类型声明 |
| decltype | 推断类型但不计算,保留顶层const和引用,decltype((var))是引用 |
| struct | 自定义数据结构,支持类内初始值(C++11),成员默认public |
| 头文件保护 | #ifndef/#define/#endif 防止重复包含 |