03-数组与字符串:一组数据怎么放¶
目录页:见 C++ 小白教程目录
目标¶
- 用数组保存一组数字,并能遍历、统计、查找
- 理解下标从 0 开始,以及“越界”为什么危险
- 会用
std::string处理字符串:拼接、长度、访问字符、读整行
先决知识¶
- 会用 if/for/while 写基础逻辑(见 01 章 4、5)
- 会写函数并能调用(见 02 章 2、3)
1. 数组(array):把很多同类型数据排成一排¶
你现在遇到的第一个“容器”,通常就是数组。
- 专业名称:数组(array)
- 类比:一排连续编号的储物格,每个格子装同一种东西(同一种类型)
- 作用:用一个名字存一组数据,方便用循环批量处理
- 规则/坑点:下标从 0 开始;数组大小固定;越界访问会产生严重问题
1.1 怎么声明与初始化¶
最常见的写法:
#include <iostream>
int main() {
int a[5] = {10, 20, 30, 40, 50};
std::cout << "a[0]=" << a[0] << '\n';
std::cout << "a[4]=" << a[4] << '\n';
return 0;
}
你需要抓住 3 个点:
int a[5]:表示有 5 个int格子a[0]是第 1 个格子,a[4]是第 5 个格子a[5]不存在(越界)
再看两种很实用的初始化:
含义:把第 1 个元素设为 0,剩下的也会被置为 0(新手阶段可以把它理解成“全清零”)。
含义:把所有元素都置为 0(更直观)。
1.2 下标(index):为什么从 0 开始¶
- 专业名称:下标/索引(index)
- 类比:储物格的编号,从 0 号开始贴标签
- 作用:用
a[i]精确定位第 i 个元素 - 规则/坑点:合法范围永远是
0 ~ (n-1);一旦写到a[n]就越界
你先把这句当成“铁律”:
- 长度是
n的数组,最大下标永远是n-1
2. 遍历数组:用循环把整排格子走一遍¶
2.1 读入 N 个数,求和与最大值(可运行)¶
#include <iostream>
int main() {
const int n = 5;
int a[n] = {};
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
int sum = 0;
int maxValue = a[0];
for (int i = 0; i < n; i++) {
sum += a[i];
if (a[i] > maxValue) {
maxValue = a[i];
}
}
double avg = static_cast<double>(sum) / n;
std::cout << "sum=" << sum << '\n';
std::cout << "max=" << maxValue << '\n';
std::cout << "avg=" << avg << '\n';
return 0;
}
这里你第一次看到 static_cast<double>(sum):
- 专业名称:显式类型转换(explicit cast)
- 类比:给数据换一个“包装规格”,让它按新的规则参与计算
- 作用:避免整数除法丢小数,让结果按浮点数算
- 规则/坑点:不要迷信“自动帮你转对”,新手阶段遇到“整数除法”就主动转成
double
3. 数组和函数:为什么通常要“数组 + 长度”一起传¶
你一定会想把“求最大值”封装成函数。问题是:函数拿到数组时,通常还需要数组长度。
入门先记结论(原因会在第 04 篇指针里讲透):
- 把数组交给函数时,常见做法是:传数组 + 传长度
可运行例子:
#include <iostream>
int maxOf(const int a[], int n) {
int maxValue = a[0];
for (int i = 1; i < n; i++) {
if (a[i] > maxValue) {
maxValue = a[i];
}
}
return maxValue;
}
int main() {
const int n = 5;
int a[n] = {};
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
std::cout << "max=" << maxOf(a, n) << '\n';
return 0;
}
注意这句:
const int a[]:表示函数承诺“不修改数组内容”
4. 字符串(std::string):一串字符,不是“单个字符”¶
前面你见过 'Q'(一个字符),现在学的是 "qtcraft" 这种一串字符。
- 专业名称:字符串(string),标准库字符串类型:
std::string - 类比:一串连续的字符珠子,你可以数长度、拼接、访问第几个珠子
- 作用:保存和处理文本(用户名、路径、提示语、整行输入……)
- 规则/坑点:
cin >> s读到空格就停;读整行要用getline
4.1 基础操作:拼接、长度、访问字符(可运行)¶
#include <iostream>
#include <string>
int main() {
std::string name = "qtcraft";
std::string msg = "hello, " + name;
std::cout << "msg=" << msg << '\n';
std::cout << "len=" << msg.size() << '\n';
if (!msg.empty()) {
std::cout << "first=" << msg[0] << '\n';
std::cout << "last=" << msg[msg.size() - 1] << '\n';
}
return 0;
}
这里 size() 和 empty() 你只记两条规则就够用:
s.size()返回字符个数(长度)s.empty()判断是否为空串(长度是否为 0)
访问字符时的坑点:
s[i]不会帮你检查越界;i超了就会出问题- 你想要“带检查”,可以用
s.at(i)(越界会抛异常),新手阶段先知道有这个选项即可
4.2 读字符串:读单词 vs 读整行¶
4.2.1 cin >> s:读一个“词”¶
它会跳过前导空白,然后读到下一个空白为止,所以输入 hello world,读到的是 hello。
4.2.2 getline(cin, line):读一整行¶
#include <iostream>
#include <string>
int main() {
std::string line;
std::getline(std::cin, line);
std::cout << "line=" << line << '\n';
return 0;
}
如果你前面已经用过 cin >> 读数字/单词,再用 getline 读整行,最容易踩到一个经典坑:上一句输入留下的换行会被 getline 立刻读走,导致读到空行。
一个新手好用的写法是这样:
这里 std::ws 也是操纵符(见 01 章 0.4 对“操纵符”的解释),作用是:先吃掉前导空白(包括那个残留的换行),再开始读整行。
常见坑¶
-
错误:数组下标从 1 开始用
结果:要么漏掉第一个元素,要么访问越界
正确:长度 n 的数组,下标永远是0 ~ n-1 -
错误:访问
a[n](越界)
结果:输出乱、程序崩溃、结果不稳定(越界是典型“玄学 bug”)
正确:循环条件用i < n,不要写i <= n -
错误:把
cin >> s当成“读一整行”
结果:遇到空格就截断
正确:读整行用std::getline -
错误:
cin >>后直接getline
结果:读到空行
正确:用std::getline(std::cin >> std::ws, line)或先清掉换行 -
错误:用
s[i]访问字符串但 i 可能越界
结果:行为不稳定
正确:先判断范围,或用s.at(i)做越界检查
小练习¶
练习 1:统计 5 个数(数组基础)¶
- 题目:读入 5 个整数,输出 sum、max、min、avg
- 输入/输出:输入 5 个整数,输出四个统计值
- 约束:avg 用小数输出
- 提示:
avg = (double)sum / n或static_cast<double>(sum) / n - 目标:练数组 + 循环 + 基础统计
练习 2:反转数组(下标练习)¶
- 题目:读入 6 个整数,把数组原地反转并输出
- 输入/输出:输入 6 个整数,输出反转后的 6 个整数
- 约束:必须原地反转(不额外开第二个数组)
- 提示:交换
a[l]和a[r],然后l++、r-- - 目标:练下标边界与交换逻辑
练习 3:冒泡排序(算法入门)¶
- 题目:读入 8 个整数,使用冒泡排序从小到大排序并输出
- 输入/输出:输入 8 个整数,输出排序后的序列
- 约束:只能用数组、循环、if;先不要用库排序
- 提示:每一轮把最大的“冒”到末尾
- 目标:练双重循环与比较交换
练习 4:统计整行文本(字符串遍历)¶
- 题目:读入一整行字符串,统计字母、数字、空格的数量
- 输入/输出:输入一行文本,输出三个计数
- 约束:把整行读进来(必须用
getline) - 提示:判断字符范围:
'0'~'9','a'~'z','A'~'Z',空格是' ' - 目标:练 string + 遍历 + 条件判断
练习 5:回文串判断(综合)¶
- 题目:读入一行字符串,判断是否回文(从左读和从右读一样)
- 输入/输出:输入一行,输出
palindrome=1或palindrome=0 - 约束:先只处理原样比较(不忽略空格、不忽略大小写)
- 提示:双指针:
l从左、r从右,向中间走 - 目标:练 string 下标、边界控制、循环条件
小结¶
你现在应该掌握:
- 数组的声明与初始化,以及下标从 0 开始的规则
- 用循环遍历数组做统计、查找,并把逻辑封装成函数(数组 + 长度)
std::string的常用操作:拼接、长度、访问字符- 读单词用
cin >>,读整行用getline,并知道如何处理残留换行
下一篇会进入指针与引用:你会真正理解“数组 + 长度为什么要一起传”,以及内存地址到底是什么。