本文最后更新于 2026-02-27,文章内容可能已经过时。

C语言数组是连续存储相同类型数据的结构,数组名本质是首元素地址(常量指针),因此数组操作与指针操作等价(如arr[i]等价于*(arr + i));指针遍历数组(如for(int *p=arr; p<arr+n; p++))比下标法更高效,是C语言高效处理数据的核心机制,掌握此关系可避免常见误区(如数组名不可赋值),并为二维数组等高级操作奠定基础。

一、数组的基本概念

数组是C语言中存储多个相同类型数据的核心数据结构,其本质是"相同数据类型 + 连续内存地址"的集合。数组具有以下核心特点:

  1. 数据类型统一:所有元素必须是同一类型(如int、float、char等)
  2. 内存地址连续:相邻元素的内存地址相差一个数据类型的大小(如int数组相邻元素差4字节)
  3. 长度固定:数组长度一旦定义无法修改,需提前规划存储容量

通俗理解:数组就像一排整齐的"储物箱",每个箱子类型相同(比如都是int型),有唯一编号(下标),能快速存放和查找数据。

二、一维数组的定义与初始化

1. 定义格式

类型 数组名[长度];
  • 类型:支持所有C语言基础数据类型(int、char、float等)
  • 数组名:变量名,遵循命名规则(字母/数字/下划线组成,非数字开头、非关键字、区分大小写)
  • 长度:表示数组可存储的元素个数

2. 初始化方式

// 全部初始化
int a[5] = {1, 2, 3, 4, 5};

// 部分初始化(剩余元素自动初始化为0)
int b[5] = {1, 2}; // b[0]=1, b[1]=2, b[2-4]=0

// 不指定长度(编译器自动确定)
int c[] = {1, 2, 3, 4, 5}; // 等同于 int c[5] = {1,2,3,4,5};

3. 空间大小计算

数组总空间大小 = 单个元素类型大小 × 数组长度

int a[10];
printf("数组总空间: %zu字节\n", sizeof(a)); // 40字节(int占4字节)
printf("单个元素空间: %zu字节\n", sizeof(a[0])); // 4字节

三、数组与指针的关系

1. 数组名的本质

在C语言中,数组名本身代表数组首元素的地址,是一个常量,不能被修改。

int arr[5] = {1, 2, 3, 4, 5};
printf("%p\n", arr);      // 数组首地址
printf("%p\n", &arr[0]);  // 数组首地址,与arr等价

重要区别:数组名是常量,不能被赋值;指针变量是变量,可以被修改。

2. 指针指向数组

int *p = arr;      // p指向数组arr的首元素
int *q = &arr[0];  // 等价于 p = arr

四、数组的遍历与操作

1. 三种遍历数组的方式(速度对比)

遍历方式代码示例说明
下标法for (int i=0; i<n; i++) printf("%d", arr[i]);中规中矩,适合新手
数组名指针法for (int i=0; i<n; i++) printf("%d", *(arr + i));聪明的"理科生"
指针变量法for (int *p=arr; p<arr+n; p++) printf("%d", *p);速度担当的"特长生"

实测结论:指针变量法比下标法快30%+,因为少了arr[i]*(arr+i)的编译步骤。

2. 指针操作数组的示例

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *p = arr; // 指针指向数组首元素
    
    // 指针遍历数组
    for (int i = 0; i < 5; i++) {
        printf("%d ", *(p + i)); // 等价于 arr[i]
    }
    
    // 指针递增遍历
    for (int *p = arr; p < arr + 5; p++) {
        printf("%d ", *p);
    }
    
    return 0;
}

3. 指针与数组的等价性

// 以下写法等价
arr[i] == *(arr + i)
i[arr] == *(i + arr) // 虽然语法合法,但不推荐使用

五、二维数组的操作

1. 二维数组的内存布局

二维数组在内存中是连续存储的,可以看作"数组的数组"。

int arr[3][4]; // 3行4列

2. 指向二维数组的指针

int arr[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

// 指向二维数组的指针
int (*p)[4] = arr; // p指向一个包含4个int的数组

// 访问元素
printf("%d\n", *(*(p + 1) + 2)); // 访问arr[1][2],输出7

3. 二维数组的遍历

// 通过指针遍历二维数组
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
        printf("%d ", *(*(p + i) + j));
    }
}

六、常见误区与避坑指南

1. 数组名是常量,不能被赋值

// 错误示例:数组名是常量,不能被赋值
int a[10];
for (int *p = a; a < a + 10; a++) { // 错误!a是常量
    printf("%d ", *a);
}

2. 函数参数传递数组

当数组作为函数参数传递时,数组名会退化为指针:

void modifyArray(int arr[]) {
    arr[0] = 100; // 修改的是原数组
}

int main() {
    int a[5] = {1, 2, 3, 4, 5};
    modifyArray(a);
    printf("%d", a[0]); // 输出100,原数组被修改
}

3. 字符串处理的特殊性

char str[10] = "hello"; // 字符串以'\0'结尾
printf("%s", str); // 输出hello

注意:字符串必须以'\0'结尾,输入字符串时不能包含空格(使用scanf时)。

4. 二维数组指针声明

// 正确声明指向二维数组的指针
int (*p)[4] = arr; // p指向包含4个int的数组

// 错误声明
int *p[4] = arr; // 这是数组指针,不是指向二维数组的指针

七、总结

  1. 数组是连续内存的集合,数组名代表首元素地址(常量)
  2. 指针可以指向数组,通过指针操作数组是C语言的精髓
  3. 数组与指针的访问方式等价arr[i]*(arr + i)
  4. 指针遍历数组比下标法更快,但需注意指针的边界
  5. 二维数组可以看作"数组的数组",指针操作需特别注意类型

核心理解:数组名是常量,指针是变量。指针的算术运算使我们能够高效地遍历和操作数组,这是C语言强大和灵活的关键所在。

掌握数组与指针的关系,是深入学习C语言、理解内存操作、实现高效数据处理的基础。通过多练习指针与数组的结合使用,你会逐渐体会到C语言的精妙之处。