搜索

C++ 教程

C++基础

第1章 搭建开发环境与第一个程序 第2章 基本数据类型与变量 第3章 运算符与表达式 第4章 流程控制语句 第5章 数组与字符串 第6章 函数基础 第7章 指针基础 第8章 引用与const限定 第9章 动态内存管理 第10章 结构体与联合体 第11章 函数进阶

面向对象

第12章 面向对象编程入门 第13章 封装与访问控制 第14章 运算符重载 第15章 继承与派生 第16章 多态与虚函数

STL与进阶

第17章 模板编程基础 第18章 标准模板库(STL)容器 第19章 STL算法与迭代器 第20章 异常处理机制 第21章 文件输入输出 第22章 字符串类string 第23章 命名空间与作用域 第24章 类型转换操作符

现代C++

第25章 智能指针 第26章 移动语义与右值引用 第27章 Lambda表达式 第28章 多线程编程基础 第29章 模板元编程入门 第30章 设计模式基础 第31章 C++11/14新特性 第32章 C++17/20核心特性

实战进阶

第33章 性能优化技巧 第34章 调试与测试 第35章 项目结构与构建 第36章 网络编程入门 第37章 数据库连接操作 第38章 图形界面开发基础 第39章 综合项目实战 第40章 进阶学习路线

第5章 数组与字符串

第5章 数组与字符串

入门 👤 已掌握变量和循环,想批量处理数据的初学者 🔧 管理学生成绩、处理用户输入、游戏地图数据存储
本章你将学到
  • 如何声明和使用数组存储同类型数据
  • 如何用字符数组表示和操作字符串
  • 理解数组的内存布局和下标访问机制
核心概念数组字符串

想象一下你有一排整齐的储物柜,每个柜子都有编号,里面可以放东西。在C++里,数组就是这排储物柜,它让你能用一个名字管理多个同类型的变量。

数组是什么?

生活类比:一栋公寓楼里连续的房间,每个房间号(下标)对应一个住户(数据)。
技术定义:数组是一种数据结构,用于在连续的内存空间中存储固定数量的、相同数据类型的元素集合。

5.1 一维数组:你的第一个数据集合

一维数组是最简单的形式,就像一条直线上的点。它的声明语法是:数据类型 数组名[元素个数];。

// 声明一个能存放5个整数的数组
int scores[5];

// 声明并初始化数组
int primes[5] = {2, 3, 5, 7, 11};

// 可以省略数组大小,编译器会自动计算
int morePrimes[] = {13, 17, 19, 23};

数组元素通过下标访问,下标从0开始。这意味着第一个元素是array[0],最后一个元素是array[数组长度-1]。我在项目中踩过这个坑,一个新手工程师因为把scores[5]当成第五个成绩,导致程序访问了非法内存,直接崩溃。

⚠️ 常见误区:数组下标越界

这是新手最容易犯的错误,编译器通常不会报错,但会导致程序行为异常甚至崩溃。

  • 错误1:访问arr[10],而数组大小只有10。最后一个有效下标是arr[9]。
  • 错误2:在循环中使用for(int i=0; i<=10; i++)遍历大小为10的数组,i=10时越界。
  • 错误3:使用负数下标,如arr[-1],这完全是非法的。

5.2 数组在内存中如何排列

理解内存布局对调试和性能优化至关重要。数组元素在内存中是连续存储的。对于一个int arr[3] = {10, 20, 30};的数组,假设int占4字节,其内存布局如下:

元素内存地址(示例)存储的值字节占用
arr[0]0x1000100x1000 - 0x1003
arr[1]0x1004200x1004 - 0x1007
arr[2]0x1008300x1008 - 0x100B

这种连续存储的特性使得数组的访问速度极快,因为CPU缓存可以高效地预加载相邻数据。实际开发中,对于需要频繁遍历的固定大小数据集,我们首选数组。

关键点:数组大小必须是编译期常量

在标准C++中,数组的大小必须在编译时确定。你不能写int n = 5; int arr[n];(虽然某些编译器扩展允许)。正确的做法是使用常量:const int SIZE = 5; int arr[SIZE];。如果需要运行时决定大小,你需要等待第9章介绍的动态内存分配。

5.3 字符数组与C风格字符串

在C++中,字符串最初是通过字符数组实现的,称为C风格字符串。它本质上是一个以空字符‘\0’(ASCII码为0)结尾的字符数组。

char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
// 更简洁的初始化方式(编译器会自动添加'\0')
char greeting2[] = "Hello";

字符串是什么?

生活类比:一列火车,车头是第一个字符,车尾有一个特殊的“终点”标志(‘\0’)。
技术定义:字符串是由字符组成的序列,在C风格字符串中以空字符作为终止符,标识字符串的结束。

双引号“Hello”被称为字符串字面量。它会在内存中创建一个包含6个字符(5个字母+1个‘\0’)的常量数组。这里有个隐藏的坑:字符串字面量是不可修改的。尝试char* ptr = “Hello”; ptr[0] = ‘h’;会导致未定义行为(通常是程序崩溃)。

字符数组声明
→
分配连续内存
→
以'\0'填充结尾
↓
字符串字面量
→
只读内存区

5.4 操作字符串:输入、输出与常用函数

对于C风格字符串,你不能直接用==比较,也不能用=直接赋值。必须使用C标准库中的函数。

#include 
#include  // 引入字符串操作函数
using namespace std;

int main() {
    char name[20];
    char copy[20];

    // 输入字符串(遇到空格会停止)
    cin >> name;
    // 输入带空格的字符串(更安全,指定最大长度)
    cin.getline(name, 20);

    // 复制字符串
    strcpy(copy, name);
    // 连接字符串
    strcat(name, "先生");
    // 获取字符串长度(不包括'\0')
    int len = strlen(name);
    // 比较字符串(相等返回0)
    if (strcmp(name, copy) == 0) {
        cout << "字符串相同" << endl;
    }
    return 0;
}

🔧 工程师经验:strcpy的安全隐患

strcpy函数不会检查目标数组的大小。如果源字符串比目标数组长,会导致缓冲区溢出,这是严重的安全漏洞。实际开发中,我们使用更安全的strncpy,它可以指定最大复制字符数:strncpy(dest, src, dest_size - 1); dest[dest_size - 1] = ‘\0’;。在第22章,你会看到C++的std::string类完全避免了这些问题。

编者提示:本章学习的数组是静态的,大小固定。当你需要在程序运行时动态创建大小可变的数据集合时,请参考第9章《动态内存管理》和第18章《标准模板库(STL)容器》中的vector。

5.5 二维数组:表格数据的管理者

二维数组可以看作一个矩阵或表格,它有两个维度:行和列。声明语法:数据类型 数组名[行数][列数];。

// 一个3行4列的整数表格(例如:3个学生,4门成绩)
int gradebook[3][4] = {
    {85, 90, 78, 92}, // 学生1的成绩
    {88, 76, 95, 80}, // 学生2的成绩
    {70, 82, 88, 75}  // 学生3的成绩
};

// 访问第2个学生(行索引1)的第3门成绩(列索引2)
int score = gradebook[1][2]; // score = 95

在内存中,二维数组仍然是按行连续存储的。上面的gradebook在内存中的排列顺序是:85, 90, 78, 92, 88, 76, 95, 80, 70, 82, 88, 75。理解这一点对性能优化很重要:按行顺序遍历(外层循环行,内层循环列)比按列顺序遍历更快,因为它符合内存的连续性,缓存命中率高。

游戏地图

  • 用二维数组int map[10][10]
  • 每个元素代表一个格子类型(0=空地,1=墙,2=水源)

图像像素

  • 黑白图像:bool pixels[480][640]
  • RGB图像:int pixels[480][640][3](三维数组)

矩阵计算

  • 存储矩阵数据float matrixA[3][3]
  • 用于图形变换、物理模拟等

数据表格

  • 存储传感器读数float sensorData[100][5]
  • 100个时间点,5个传感器通道

动手试一试

1. 数组反转:编写程序,声明一个包含6个整数的数组{1,2,3,4,5,6},在不创建新数组的情况下,将数组元素顺序反转,变成{6,5,4,3,2,1}。提示:使用两个下标,一个从头部开始,一个从尾部开始,交换它们指向的元素。

2. 单词统计:编写程序,使用cin.getline()读取一行英文句子(不超过100字符),统计其中有多少个单词(单词之间用空格分隔)。例如,输入“Hello C++ world”,输出3。

3. 查找最高分:使用二维数组存储3个学生4门课的成绩,计算并输出每个学生的平均分,以及找出所有成绩中的最高分是几分,属于哪个学生(行)的哪门课(列)。

检验你的理解

  1. 以下代码片段有什么问题?
    char buffer[5]; strcpy(buffer, “HelloWorld”);
  2. 数组int arr[10];的有效下标范围是多少?arr[10]访问的是什么?
  3. 如何初始化一个字符数组,使其内容为字符串“C++”?写出两种方法。
  4. 如果int matrix[2][3] = {{1,2,3}, {4,5,6}};,那么matrix[1][2]的值是多少?
  5. 判断:字符串“A”和字符‘A’在内存中占用相同的字节数。

本章小结

  • 数组是连续内存中相同类型元素的集合,通过下标(从0开始)访问,大小必须在编译时确定。
  • C风格字符串是以空字符‘\0’结尾的字符数组,使用中的函数(如strcpy, strcat, strlen, strcmp)进行操作,需注意缓冲区溢出风险。
  • 二维数组用于表示表格数据,内存中按行连续存储,按行遍历效率更高。
  • 数组下标越界是常见且危险的错误,会导致未定义行为或安全漏洞。
  • 字符串字面量(如“text”)存储在只读内存区,试图修改它们会导致程序崩溃。

意见反馈

微信二维码 微信扫码关注 抖音二维码 抖音扫码关注