引言
在C++编程中,数组与指针是核心基础知识点,也是后续学习数据结构、算法的重要铺垫。数组提供了高效存储同类型数据的方式,而指针则通过直接操作内存地址,赋予程序更灵活的内存访问能力。两者的结合使用更是C++的精髓所在,能够大幅提升代码的效率与灵活性。本文将从数组(一维、二维、字符数组)、指针基础、指针与数组的关联三个维度,结合实例代码详细讲解,帮助读者彻底掌握这部分知识点。
一、数组详解
数组是由相同数据类型的元素组成的有序集合,在内存中占据连续的存储单元,可通过下标快速访问元素。根据维度不同,分为一维数组、二维数组及特殊的字符数组。
1.1 一维数组
1.1.1 定义格式
cpp类型名 数组名1[常量表达式], 数组名2[常量表达式], ...;
- 核心规则:
数组名命名遵循普通变量命名规则(字母、数字、下划线,首字符非数字);
方括号内必须是常量或符号常量(不可为变量),其值决定数组元素个数;
示例:
cppint A[10], b[5]; // 整型数组,A含10个元素,b含5个元素 char c[8]; // 字符数组,含8个元素 #define M 3 float y[4*M+1]; // 符号常量参与表达式,y含13个元素
1.1.2 存储特性
一维数组的元素在内存中连续存放,低地址存储首元素,高地址存储尾元素;
总占用字节数计算公式:
总字节数 = sizeof(基本类型) * 元素个数;示例:
int a[100];中,int类型占4字节(32位系统),总字节数 = 4*100 = 400字节。
1.1.3 元素引用
规则:数组必须先定义后引用,只能逐个访问元素,不能直接访问整个数组;
引用格式:
数组名[下标],下标从0开始,最大下标为"元素个数-1"(避免下标越界);下标支持整型常量或整型表达式,示例:
cppa[0] = a[5] + a[7] - a[2*3]; // 下标可为表达式,等价于a[0] = a[5]+a[7]-a[6]
1.1.4 初始化方式
- 全量初始化:定义时为所有元素赋值,示例:
cppint a[4] = {1,2,3,4}; // 等价于a[0]=1, a[1]=2, a[2]=3, a[3]=4
- 部分初始化:仅为前n个元素赋值,剩余元素默认为0,示例:
cppint b[10] = {1,2}; // b[0]=1, b[1]=2, b[2]-b[9]均为0
- 省略长度初始化:全量赋值时可省略数组长度,编译器自动计算,示例:
cppint c[] = {1,2,3,4,5}; // 自动识别长度为5
- 注意:若初始化元素个数与数组长度不一致,不可省略长度 (如
int a[10]={1,2,3}合法,int a[]={1,2,3,4,5,6}合法,但int a[]={1,2,3,4,5,6,7,8,9,10,11}不合法)。
1.1.5 实例代码
实例1:逆序输出数组
cpp#include <iostream> using namespace std; int main() { int a[10], i; // 输入10个元素 for (i = 0; i < 10; ++i) cin >> a[i]; // 逆序输出 for (i = 9; i >= 0; --i) cout << a[i] << " "; cout << endl; return 0; }
- 运行结果:输入
1 2 3 4 5 6 7 8 9 10,输出10 9 8 7 6 5 4 3 2 1。实例2:求数组的最大值、最小值和平均值
cpp#include <iostream> using namespace std; #define SIZE 10 // 定义数组长度为10 int main() { int a[SIZE] = {9, 9, 8, 3, 7, 15, 6, 1, 3, 2}; int max, min, sum, i; double ave; max = min = sum = a[0]; // 初始化max、min、sum为第一个元素 for (i = 1; i <= SIZE - 1; ++i) { if (max < a[i]) max = a[i]; // 更新最大值 if (min > a[i]) min = a[i]; // 更新最小值 sum += a[i]; // 累加求和 } ave = double(sum) / SIZE; // 计算平均值(强制类型转换) cout << "The max is " << max << endl; cout << "The min is " << min << endl; cout << "The average is " << ave << endl; return 0; }
- 运行结果:
The max is 15 The min is 1 The average is 6.3
1.2 二维数组
二维数组可理解为"数组的数组",常用于存储表格型数据(如矩阵)。
1.2.1 定义格式
cpp类型名 数组名[常量表达式1][常量表达式2], ...;
- 核心规则:
常量表达式1表示"行数",常量表达式2表示"列数",不可用逗号分隔(如
int a[3,4]是错误的);示例:
cppint a[3][4], b[4][M]; // a是3行4列整型数组,b是4行M列整型数组(M为符号常量)
- 本质:二维数组是特殊的一维数组,其每个元素是一个一维数组。例如
int a[3][4]中,a[0]、a[1]、a[2]是3个元素,每个元素是含4个元素的一维数组。
1.2.2 存储特性
内存中按"行优先"顺序存储:先存储第一行所有元素,再存储第二行,依次类推;
示例:
int a[2][3]的存储顺序为a[0][0] → a[0][1] → a[0][2] → a[1][0] → a[1][1] → a[1][2]。
1.2.3 元素引用
引用格式:
数组名[行下标][列下标];下标范围:行下标0~行数-1,列下标0~列数-1,示例:
cppint a[3][4]; a[2][3] = 10; // 合法(最大行下标2,最大列下标3) a[3][0] = 20; // 非法(行下标越界)
1.2.4 初始化方式
- 按存储顺序初始化:将所有元素按行优先顺序放入大括号,示例:
cppint x[2][3] = {1,2,3,4,5,6}; // 等价于x[0][0]=1, x[0][1]=2, x[0][2]=3, x[1][0]=4, x[1][1]=5, x[1][2]=6
- 按行分组初始化:用嵌套大括号分组,每组对应一行,示例:
cppint x[2][3] = {{1,2,3}, {4,5,6}}; // 与上面等价,可读性更强
- 部分初始化:未赋值的元素默认为0,示例:
cppint x[2][3] = {{1,2}, {4}}; // 等价于x[0][0]=1, x[0][1]=2, x[0][2]=0, x[1][0]=4, x[1][1]=0, x[1][2]=0
- 省略行数初始化:可省略第一维(行数),编译器根据列数和元素个数自动计算行数,示例:
cppint x[][3] = {1,2,3,4,5,6,7}; // 自动计算行数为3,等价于x[3][3],未赋值元素为0
- 注意:不可省略列数(编译器无法确定每行的元素个数)。
1.2.5 实例代码:二维数组的输入与输出
cpp#include <iostream> #include <iomanip> // 用于setw格式化输出 using namespace std; int main() { int a[2][3], i, j; // 输入2行3列元素 for (i = 0; i < 2; ++i) { for (j = 0; j < 3; ++j) cin >> a[i][j]; } // 按行格式化输出(左对齐,每个元素占5个字符宽度) cout << "按行输出如下:" << endl; for (i = 0; i < 2; ++i) { for (j = 0; j < 3; ++j) cout << left << setw(5) << a[i][j]; cout << endl; // 每行结束换行 } return 0; }
- 运行结果:
1 2 3 4 5 6 按行输出如下: 1 2 3 4 5 6
1.3 字符数组与字符串
字符数组是专门存储字符数据的数组,也是C++中处理字符串的基础方式(另一种是string类)。
1.3.1 字符数组的定义与初始化
- 定义格式:
char 数组名[长度];,示例:
cppchar c[10]; c[0] = 'I'; c[1] = ' '; c[2] = 'a'; c[3] = 'm'; c[4] = ' '; c[5] = 'h'; c[6] = 'a'; c[7] = 'p'; c[8] = 'p'; c[9] = 'y';
- 初始化方式:
- 逐个元素初始化:
cppchar c[10] = {'s', 't', 'o', 'r', 'e'}; // 未赋值的c[5]-c[9]默认为'\0'(字符串结束标志,ASCII码为0)
- 字符串常量初始化:直接用双引号包裹字符串赋值,编译器自动添加'\0',示例:
cppchar c[6] = "china"; // 数组长度需≥字符串长度+1('\0'占1位) char c[] = "china"; // 自动计算长度为6(含'\0')
1.3.2 字符串结束标志'\0'
核心作用:用于标识字符串的结束,避免访问数组越界;
注意:
若字符数组未以'\0'结尾,使用
cout输出时会出现乱码(直到遇到内存中的'\0');字符串长度≠数组长度:字符串长度是从首元素到'\0'的字符个数(不含'\0'),数组长度是定义时的长度。
1.3.3 string类(字符串变量)
C++提供
string类,简化字符串操作,需包含头文件<string>。
- 定义与输入输出:
cpp#include <iostream> #include <string> using namespace std; int main() { string str1, str2; cin >> str1; // 输入字符串(遇空格结束) getline(cin, str2); // 输入整行字符串(含空格) cout << "str1: " << str1 << endl; cout << "str2: " << str2 << endl; return 0; }
- 常用运算:
string类支持直接赋值、连接、比较,示例:
cppstring str1 = "Hello", str2 = "World"; string str3 = str1; // 赋值:str3 = "Hello" string str4 = str1 + " " + str2; // 连接:str4 = "Hello World" if (str1 == str2) cout << "相等" << endl; // 比较:结果为不相等
- 字符串数组:用
string定义存储多个字符串的数组,示例:
cppstring course[4] = {"C", "C++", "Java", "Python"}; cout << course[1] << endl; // 输出:C++
1.3.4 实例代码:字符数组与string类对比
cpp#include <iostream> #include <iomanip> #include <string> using namespace std; int main() { // 字符数组 char ch1[5]; cout << "请输入5个字符:" << endl; for (int i = 0; i < 5; ++i) cin >> ch1[i]; cout << "字符数组输出:" << endl; for (int i = 0; i < 5; ++i) cout << left << setw(3) << ch1[i]; // string类 string ch2; cout << endl << "请输入一个字符串(可含空格):" << endl; cin.ignore(); // 忽略之前的换行符 getline(cin, ch2); cout << "string类输出:" << endl; cout << ch2 << endl; return 0; }
- 运行结果:
请输入5个字符: abcde 字符数组输出: a b c d e 请输入一个字符串(可含空格): Hello C++ string类输出: Hello C++
二、指针基础
指针是存储内存地址的变量,通过指针可间接访问内存中的数据,是C++中灵活操作内存的核心工具。
2.1 核心概念
内存地址:内存被划分为字节级存储单元,每个单元有唯一的编号(地址),用于标识单元位置;
变量地址:系统为变量分配的内存单元的首地址(如
int a占4字节,其地址是第一个字节的编号);指针变量:专门用于存储地址的变量,其值是某变量的地址,称"指针指向该变量"。
2.2 指针变量的定义与赋值
2.2.1 定义格式
类型符 *指针变量名;
- 说明:
类型符:指针指向的变量的数据类型(指针的"基类型"),决定了指针访问内存时的步长;
示例:
cppint *p1, *p2; // 指向整型变量的指针 char *ps; // 指向字符变量的指针 float *pf; // 指向浮点型变量的指针
2.2.2 赋值规则
指针变量的值必须是同类型变量的地址,不可直接赋值整型常量(地址是无符号整数,但类型不匹配)。
- 赋值方式:
- 取变量地址赋值(&为取地址运算符):
cppint a = 10; int *p = &a; // p存储a的地址,p指向a
- 同类型指针赋值:
cppint *p1 = &a, *p2; p2 = p1; // p2与p1指向同一个变量a
- 赋空值:指针未指向有效变量时,赋值
NULL(或0),避免野指针:
cppint *p = NULL; // 空指针,不指向任何有效内存
2.2.3 指针的引用(*运算符)
*为指针运算符(解引用运算符),用于访问指针指向的变量的值。
- 核心用法:
取内容:
*指针变量表示指针指向的变量的值(出现在=右侧);存内容:
*指针变量 = 值表示将值存入指针指向的变量(出现在=左侧);
- 示例代码:
cpp#include <iostream> using namespace std; int main() { int a = 5, b = 3; int *p; p = &a; // p指向a cout << "a=" << a << ", *p=" << *p << endl; // 取内容:a=5, *p=5 b = *p + 5; // 取内容:b=5+5=10 cout << "b=" << b << endl; // 输出b=10 *p = 4; // 存内容:将4存入a的内存,a=4 cout << "a=" << a << ", *p=" << *p << endl; // 输出a=4, *p=4 return 0; }
- 运行结果:
a=5, *p=5 b=10 a=4, *p=4
2.3 指针的运算规则
优先级:
*(解引用)与&(取地址)优先级相同,且右结合,与++、--等单目运算符优先级一致;常见表达式:
&*p:等价于p(先解引用*p,再取地址&,结果为p本身);
*&a:等价于a(先取a的地址&a,再解引用*,结果为a的值);
- 示例代码:
cpp#include <iostream> using namespace std; int main() { int a, b, c; int *pa, *pb, *pc; pa = &a, pb = &b, pc = &c; cin >> *pa >> *pb; // 输入2和3,等价于cin >> a >> b c = a + b; cout << "c=" << *pc << endl; // 输出c=5 *pc = a + *pb; // 等价于c = a + b,仍为5 cout << "c=" << c << endl; // 输出c=5 c = ++*pa + (*pb)++; // ++*pa等价于++a(a=3),(*pb)++等价于b++(b=3,后自增为4) cout << "c=" << c << endl; // 输出c=3+3=6 return 0; }
- 运行结果:
2 3 c=5 c=5 c=6
三、指针与数组的关联
数组名的本质是数组首元素的地址(常量指针,不可修改),因此指针可与数组结合,通过指针操作数组元素,效率更高。
3.1 核心关联关系
对于一维数组
int a[10], *p = a;(p指向数组首元素a[0]),以下关系成立:
地址表示(指向第i个元素) 内容表示(第i个元素的值) &a[i]a[i]a + i*(a + i)p + i*(p + i)
- 说明:
a + i:数组名a是首地址,+i表示偏移i个元素的地址(偏移量= i * sizeof(元素类型));
*(p + i):指针p偏移i个元素后解引用,等价于a[i];数组下标
a[i]的本质是*(a + i),编译器会自动转换为指针运算。
3.2 指针操作数组的优势
代码更灵活:指针可移动(
p++指向next元素),而数组名不可修改;效率更高:指针运算直接操作地址,避免下标计算的开销;
示例代码:用指针遍历数组
cpp#include <iostream> using namespace std; int main() { int a[5] = {1,2,3,4,5}; int *p = a; // p指向数组首元素 // 指针遍历数组 cout << "数组元素:"; for (int i = 0; i < 5; ++i) { cout << *(p + i) << " "; // 等价于a[i] } cout << endl; // 指针移动遍历 cout << "指针移动遍历:"; p = a; // 重置指针到首元素 while (p < a + 5) { cout << *p << " "; p++; // 指针移动到下一个元素 } cout << endl; return 0; }
- 运行结果:
数组元素:1 2 3 4 5 指针移动遍历:1 2 3 4 5
3.3 二维数组与指针
对于二维数组
int a[3][4],其本质是"数组的数组",a是首行(a[0])的地址,a[i]是第i行首元素的地址。
- 核心关系:
第i行第j列元素的地址:
&a[i][j] = a[i] + j = *(a + i) + j;第i行第j列元素的值:
a[i][j] = *(a[i] + j) = *(*(a + i) + j);
- 示例代码:用指针访问二维数组
cpp#include <iostream> using namespace std; int main() { int a[2][3] = {{1,2,3}, {4,5,6}}; int *p = &a[0][0]; // 指向二维数组首元素 // 遍历二维数组 cout << "二维数组元素:"; for (int i = 0; i < 2; ++i) { for (int j = 0; j < 3; ++j) { cout << *(*(a + i) + j) << " "; // 等价于a[i][j] } } cout << endl; return 0; }
- 运行结果:
二维数组元素:1 2 3 4 5 6
四、总结
本文详细讲解了C++数组(一维、二维、字符数组)与指针的核心知识点,包括定义、存储、初始化、引用及两者的关联关系,并配套实例代码帮助理解。核心要点总结如下:
数组:连续内存存储同类型元素,下标从0开始,二维数组按行优先存储;
字符数组与string:字符数组需以'\0'结尾,string类简化字符串操作,支持直接运算;
指针:存储地址的变量,通过&取地址、
*解引用,避免野指针(赋NULL);指针与数组:数组名是首元素地址,指针可通过
p+i访问数组第i个元素,效率更高。数组与指针是C++的基础,建议多通过实例练习(如指针遍历数组、指针操作字符串)加深理解,为后续学习链表、栈、队列等数据结构打下坚实基础。如果有疑问或需要进一步探讨,欢迎在评论区留言!