C++入门讲解3:数组与指针全面详解

引言

在C++编程中,数组与指针是核心基础知识点,也是后续学习数据结构、算法的重要铺垫。数组提供了高效存储同类型数据的方式,而指针则通过直接操作内存地址,赋予程序更灵活的内存访问能力。两者的结合使用更是C++的精髓所在,能够大幅提升代码的效率与灵活性。本文将从数组(一维、二维、字符数组)、指针基础、指针与数组的关联三个维度,结合实例代码详细讲解,帮助读者彻底掌握这部分知识点。

一、数组详解

数组是由相同数据类型的元素组成的有序集合,在内存中占据连续的存储单元,可通过下标快速访问元素。根据维度不同,分为一维数组、二维数组及特殊的字符数组。

1.1 一维数组

1.1.1 定义格式

cpp 复制代码
类型名 数组名1[常量表达式], 数组名2[常量表达式], ...;
  • 核心规则:
  • 数组名命名遵循普通变量命名规则(字母、数字、下划线,首字符非数字);

  • 方括号内必须是常量或符号常量(不可为变量),其值决定数组元素个数;

  • 示例:

cpp 复制代码
int 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"(避免下标越界);

  • 下标支持整型常量或整型表达式,示例:

cpp 复制代码
a[0] = a[5] + a[7] - a[2*3]; // 下标可为表达式,等价于a[0] = a[5]+a[7]-a[6]

1.1.4 初始化方式

  • 全量初始化:定义时为所有元素赋值,示例:
cpp 复制代码
int a[4] = {1,2,3,4}; // 等价于a[0]=1, a[1]=2, a[2]=3, a[3]=4
  • 部分初始化:仅为前n个元素赋值,剩余元素默认为0,示例:
cpp 复制代码
int b[10] = {1,2}; // b[0]=1, b[1]=2, b[2]-b[9]均为0
  • 省略长度初始化:全量赋值时可省略数组长度,编译器自动计算,示例:
cpp 复制代码
int 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]是错误的);

  • 示例:

cpp 复制代码
int 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,示例:

cpp 复制代码
int a[3][4]; a[2][3] = 10; // 合法(最大行下标2,最大列下标3) 
a[3][0] = 20; // 非法(行下标越界)

1.2.4 初始化方式

  • 按存储顺序初始化:将所有元素按行优先顺序放入大括号,示例:
cpp 复制代码
int 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
  • 按行分组初始化:用嵌套大括号分组,每组对应一行,示例:
cpp 复制代码
int x[2][3] = {{1,2,3}, {4,5,6}}; // 与上面等价,可读性更强
  • 部分初始化:未赋值的元素默认为0,示例:
cpp 复制代码
int 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
  • 省略行数初始化:可省略第一维(行数),编译器根据列数和元素个数自动计算行数,示例:
cpp 复制代码
int 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 数组名[长度];,示例:
cpp 复制代码
char 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';
  • 初始化方式:
  1. 逐个元素初始化:
cpp 复制代码
char c[10] = {'s', 't', 'o', 'r', 'e'}; // 未赋值的c[5]-c[9]默认为'\0'(字符串结束标志,ASCII码为0)
  1. 字符串常量初始化:直接用双引号包裹字符串赋值,编译器自动添加'\0',示例:
cpp 复制代码
char 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类支持直接赋值、连接、比较,示例:
cpp 复制代码
string str1 = "Hello", str2 = "World"; 
string str3 = str1; // 赋值:str3 = "Hello" 
string str4 = str1 + " " + str2; // 连接:str4 = "Hello World" 
if (str1 == str2) cout << "相等" << endl; // 比较:结果为不相等
  • 字符串数组:用string定义存储多个字符串的数组,示例:
cpp 复制代码
string 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 定义格式

类型符 *指针变量名;

  • 说明:
  • 类型符:指针指向的变量的数据类型(指针的"基类型"),决定了指针访问内存时的步长;

  • 示例:

cpp 复制代码
int *p1, *p2; // 指向整型变量的指针

char *ps; // 指向字符变量的指针

float *pf; // 指向浮点型变量的指针

2.2.2 赋值规则

指针变量的值必须是同类型变量的地址,不可直接赋值整型常量(地址是无符号整数,但类型不匹配)。

  • 赋值方式:
  1. 取变量地址赋值(&为取地址运算符):
cpp 复制代码
int a = 10; int *p = &a; // p存储a的地址,p指向a
  1. 同类型指针赋值:
cpp 复制代码
int *p1 = &a, *p2; p2 = p1; // p2与p1指向同一个变量a
  1. 赋空值:指针未指向有效变量时,赋值NULL(或0),避免野指针:
cpp 复制代码
int *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++数组(一维、二维、字符数组)与指针的核心知识点,包括定义、存储、初始化、引用及两者的关联关系,并配套实例代码帮助理解。核心要点总结如下:

  1. 数组:连续内存存储同类型元素,下标从0开始,二维数组按行优先存储;

  2. 字符数组与string:字符数组需以'\0'结尾,string类简化字符串操作,支持直接运算;

  3. 指针:存储地址的变量,通过&取地址、*解引用,避免野指针(赋NULL);

  4. 指针与数组:数组名是首元素地址,指针可通过p+i访问数组第i个元素,效率更高。

数组与指针是C++的基础,建议多通过实例练习(如指针遍历数组、指针操作字符串)加深理解,为后续学习链表、栈、队列等数据结构打下坚实基础。如果有疑问或需要进一步探讨,欢迎在评论区留言!

相关推荐
代码游侠2 小时前
应用——管道与文件描述符
linux·服务器·c语言·学习·算法
一招定胜负2 小时前
决策树开篇
算法·决策树·机器学习
GoWjw2 小时前
C语言高级特性
c语言·开发语言·算法
自己的九又四分之三站台2 小时前
基于Python获取SonarQube的检查报告信息
开发语言·python
carver w2 小时前
说人话版 K-means 解析
算法·机器学习·kmeans
小O的算法实验室2 小时前
2026年SEVC SCI2区,基于差分向量内学习策略的自适应指数交叉差分进化算法,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
方也_arkling2 小时前
【JS】定时器的使用(点击开始计时,再次点击停止计时)
开发语言·前端·javascript
天若有情6732 小时前
我发明的PROTO_V4协议:一个让数据“穿上迷彩服”的发明(整数传输协议)
网络·c++·后端·安全·密码学·密码·数据
一往无前fgs2 小时前
PHP语言开发基础入门实践教程(零基础版)
开发语言·php