详细介绍:数组初阶(2)

目录

1.数组的本质

2.数组的边界与安全问题

3.数组的高级操作

4.数组与指针的深度关联(重点)

5.字符串数组 


这一章节主要内容:

1.数组的本质

2.数组的边界与安全问题

3.数组的高级操作

4.数组与指针的深度关联(重点)

5.字符串数组 

1.数组的本质

数组名:(重中之重)

数组名在除了sizeof(数组名)和&(数组名)的情况下表示的是首元素的地址。

sizeof(数组)&(数组名)表示的是整个数组。

#include 
int main() {
    int arr[5] = {1,2,3,4,5};
    // 1. 数组名通常是首元素地址(arr 等价于 &arr[0])
    printf("arr = %p\n", arr);           // 首元素地址(如 0x7ffd...)
    printf("&arr[0] = %p\n", &arr[0]);   // 与 arr 完全相同
    // 2. sizeof(arr) 计算整个数组的字节数(5个int,每个4字节 → 20)
    printf("sizeof(arr) = %zu\n", sizeof(arr));  // 输出 20
    // 3. &arr 取整个数组的地址(类型为 int(*)[5],即数组指针)
    printf("&arr = %p\n", &arr);         // 地址值与 arr 相同,但类型不同
    // 验证类型差异:&arr + 1 偏移整个数组的大小(20字节)
    printf("&arr + 1 = %p\n", &arr + 1); // 比 &arr 大 20(十进制)
    return 0;
}
  • 数组名不是可修改的指针(arr = &arr[1]; 会编译错误)。
  • &arr 的类型是 “指向整个数组的指针”,因此 &arr + 1 会跳过整个数组,而 arr + 1 只跳过一个元素。

数组长度计算:

计算公式:sizeof(arr)/sizeof(arr[0]);

#include 
int main() {
    int arr1[5] = {1,2};  // 长度固定为5,未初始化元素默认0(全局/静态数组)
    int arr2[] = {1,2,3}; // 省略长度,编译器自动计算为3
    // 计算数组长度的通用公式
    int len1 = sizeof(arr1) / sizeof(arr1[0]); // 5
    int len2 = sizeof(arr2) / sizeof(arr2[0]); // 3
    printf("arr1长度:%d,arr2长度:%d\n", len1, len2);
    // 错误:数组长度不可修改,不能动态扩容
    // arr1[5] = 6;  // 越界访问(有效下标0~4)
    return 0;
}
  • 局部数组未初始化的元素是随机值,全局 / 静态数组默认初始化为 0。
  • 若数组作为函数参数,sizeof(arr) 会退化为指针大小(如 64 位系统为 8),因此需额外传长度参数。

2.数组的边界与安全问题

1.数组越界的危害及规避

#include 
int main() {
    int arr[3] = {10,20,30};
    int x = 100;
    // 越界访问:修改了数组外的内存(可能覆盖x的值)
    arr[5] = 200;  // 编译不报错,但运行时可能导致x变为200
    printf("x = %d\n", x);  // 输出可能为200(取决于内存布局)
    // 正确做法:用长度控制循环
    int len = sizeof(arr)/sizeof(arr[0]);
    for(int i=0; i

那么我们应该如何正确初始化呢,这个我们在上节课中已经讲过,在这不做赘述。

3.数组的高级操作

 数组作为函数参数(退化问题)

数组作为参数的时候,会退化成指针,丢失长度,那么什么是数组退化呢,我们后边有专门一章节来详细讲解,让你搞懂数组退化,现在只是简单阐述一下。

#include 
// 错误:函数内无法通过sizeof获取原数组长度(arr已退化为指针)
void printArr1(int arr[]) {
    int len = sizeof(arr)/sizeof(arr[0]); // 计算错误(8/4=2,64位系统)
    printf("错误长度:%d\n", len);
}
// 正确:额外传入长度参数
void printArr2(int arr[], int len) {
    for(int i=0; i

在上面的代码中,arr是int[5]类型,但在数组做函数参数的时候,会后退化成指针,退化成

int*类型。退化后只有4个字节(x86操作系统)。

  • void func(int arr[]) 等价于 void func(int *arr),函数无法知道数组实际长度。
  • 必须通过额外参数传递长度(如 printArr2),或使用结束标记(如字符串的 '\0')。

数组的拷贝与比较

#include 
#include  // 包含memcpy
int main() {
    int arr1[5] = {1,2,3,4,5};
    int arr2[5];
    // 错误:数组不能直接用=赋值
    // arr2 = arr1;  // 编译错误
    // 正确1:循环逐个拷贝
    for(int i=0; i<5; i++){
        arr2[i] = arr1[i];
    }
    // 正确2:用memcpy内存拷贝(效率更高)
    memcpy(arr2, arr1, sizeof(arr1)); // 拷贝整个arr1到arr2
    // 错误:数组不能直接用==比较(比较的是首地址)
    if(arr1 == arr2) printf("相等"); // 永远为假(地址不同)
    // 正确:循环逐个比较元素
    int isEqual = 1;
    for(int i=0; i<5; i++){
        if(arr1[i] != arr2[i]){
            isEqual = 0;
            break;
        }
    }
    printf("元素是否相等:%s\n", isEqual ? "是" : "否"); // 是
    return 0;
}
  • memcpy(dest, src, n) 按字节拷贝,需确保目标数组有足够空间。
  • 比较数组时必须遍历所有元素,任何一个不等则数组不等。

后面我们会详细讲解memcpy函数和相关的函数。

4.数组与指针的深度关联(重点)

 数组下标与指针的等价性

arr[i] 本质是 *(arr + i),指针偏移与下标访问完全等价。

#include 
int main() {
    int arr[5] = {10,20,30,40,50};
    // 下标访问与指针访问等价
    printf("arr[2] = %d\n", arr[2]);         // 30
    printf("*(arr + 2) = %d\n", *(arr + 2)); // 30(等价)
    // 反向等价(不推荐,可读性差)
    printf("2[arr] = %d\n", 2[arr]);         // 30(等价于*(2 + arr))
    // 指针变量操作数组
    int *p = arr; // p指向首元素
    printf("p[3] = %d\n", p[3]);             // 40
    printf("*(p + 3) = %d\n", *(p + 3));     // 40(等价)
    return 0;
}
  • 数组下标是指针偏移的 “语法糖”,编译器会自动转换为指针操作。
  • 指针变量 p 可像数组一样用 p[i] 访问,与 arr[i] 逻辑一致。

5.字符串数组 

字符串数组就是字符串这就话对吗?

字符数组不一定是字符串,字符串必须以 '\0' 结尾。

#include 
#include 
int main() {
    // 字符数组(无'\0',不是字符串)
    char arr1[3] = {'a','b','c'};
    printf("strlen(arr1) = %zu\n", strlen(arr1)); // 结果随机(未找到'\0')
    // 字符串(以'\0'结尾)
    char str1[4] = {'a','b','c','\0'}; // 显式加'\0'
    char str2[] = "abc"; // 隐式加'\0'(长度为4:'a','b','c','\0')
    printf("strlen(str2) = %zu\n", strlen(str2)); // 3(统计'\0'前的字符)
    printf("str2 = %s\n", str2); // 正确打印:abc(遇到'\0'停止)
    return 0;
}
  • strlen 等字符串函数依赖 '\0' 判断结束,无 '\0' 会越界。
  • 用 " " 初始化的字符数组会自动添加 '\0',推荐用这种方式定义字符串。
  • strlen的返回类型size_t。
posted on 2025-11-19 19:55  ljbguanli  阅读(0)  评论(0)    收藏  举报