C语言数组名到底代表什么?

99ANYc3cd6
预计阅读时长 16 分钟
位置: 首页 C语言 正文

在C语言中,数组名绝大多数情况下表示的是数组首元素的内存地址,也就是说,数组名是一个指向数组第一个元素的常量指针

c语言中数组名表示什么
(图片来源网络,侵删)

这个结论是理解C语言数组的关键,下面我们通过不同场景来深入探讨这个概念。


数组名作为“指针” (最常见的情况)

当数组名出现在表达式中时,它会“衰变”(decay)为一个指向其首元素的指针。

示例代码:

#include <stdio.h>
int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    // 1. 打印数组名 arr
    // 这里 arr 衰变为指向 arr[0] 的指针
    // %p 是打印指针地址的格式说明符
    printf("数组名 arr 的值 (地址): %p\n", (void*)arr); 
    printf("arr[0] 的地址: %p\n", (void*)&arr[0]);
    // 2. 数组名可以用于指针运算
    // arr + 1 等价于 &arr[0] + 1,即指向 arr[1] 的地址
    printf("arr + 1 的值 (地址): %p\n", (void*)(arr + 1));
    printf("arr[1] 的地址: %p\n", (void*)&arr[1]);
    // 3. 使用解引用操作符 * 获取值
    // *arr 等价于 *(&arr[0]),即 arr[0] 的值
    printf("*arr 的值: %d\n", *arr); // 输出 10
    printf("arr[0] 的值: %d\n", arr[0]); // 输出 10
    // 4. 数组名可以作为函数参数
    // 在函数声明中,void print_array(int a[]) 和 void print_array(int *a) 是完全等价的
    // 这再次证明了数组名会退化为指针
    print_array(arr);
    return 0;
}
void print_array(int *a) {
    printf("函数接收到的指针指向的值: %d\n", *a);
}

输出结果分析:

c语言中数组名表示什么
(图片来源网络,侵删)
数组名 arr 的值 (地址): 0x7ffc... (一个具体的内存地址)
arr[0] 的地址: 0x7ffc... (与上面完全相同)
arr + 1 的值 (地址): 0x7ffc... (比上面大4,因为int通常占4字节)
arr[1] 的地址: 0x7ffc... (与上面完全相同)
*arr 的值: 10
arr[0] 的值: 10
函数接收到的指针指向的值: 10

从上面的例子可以看出,arr&arr[0] 在值上是完全相等的,都指向数组的第一个元素。


数组名的“例外”情况 (不衰变的情况)

有三种关键情况下,数组名不会衰变为指针,它仍然代表整个数组。

a) sizeof 操作符

当数组名作为 sizeof 操作符的操作数时,sizeof 返回的是整个数组所占用的总字节数,而不是指针的大小。

示例代码:

c语言中数组名表示什么
(图片来源网络,侵删)
#include <stdio.h>
int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int *ptr = arr;
    printf("sizeof(arr) = %zu\n", sizeof(arr)); // 输出 20 (假设int占4字节, 5 * 4 = 20)
    printf("sizeof(ptr) = %zu\n", sizeof(ptr)); // 输出 8 (在64位系统上,指针大小通常是8字节)
    return 0;
}

分析:

  • sizeof(arr) 计算的是整个 int[5] 数组的大小。
  • sizeof(ptr) 计算的是 int* 指针变量本身的大小。

b) & (取地址) 操作符

当对数组名使用 & 操作符时,得到的是整个数组的地址,而不是首元素的地址,虽然这个地址的值和首元素的地址值相同,但它们的类型和含义不同。

示例代码:

#include <stdio.h>
int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    // arr 的类型是 int* (指向int的指针)
    // &arr 的类型是 int (*)[5] (指向包含5个int的数组的指针)
    printf("arr 的地址: %p\n", (void*)arr);
    printf("&arr[0] 的地址: %p\n", (void*)&arr[0]);
    printf("&arr 的地址: %p\n", (void*)&arr);
    // 尝试指针运算
    printf("arr + 1 的地址: %p\n", (void*)(arr + 1));      // 移动 sizeof(int) 字节
    printf("&arr + 1 的地址: %p\n", (void*)(&arr + 1));  // 移动 sizeof(arr) 字节
    return 0;
}

输出结果分析:

arr 的地址: 0x7ffc...
&arr[0] 的地址: 0x7ffc...
&arr 的地址: 0x7ffc... (值相同)
arr + 1 的地址: 0x7ffc... (比上面大4)
&arr + 1 的地址: 0x7ffc... (比上面大20, 即整个数组的大小)

分析:

  • arr&arr[0 的值相同,但类型不同,都是 int*
  • &arr 的值虽然和它们相同,但类型是 int (*)[5]
  • arr + 1 将地址增加一个 int 的大小。
  • &arr + 1 将地址增加整个 int[5] 数组的大小。

c) 字符串字面量初始化数组

当用字符串字面量初始化字符数组时,数组名代表整个数组。

#include <stdio.h>
int main() {
    // str1 是一个字符数组,它有足够的空间存放 'H', 'e', 'l', 'l', 'o', '\0'
    char str1[] = "Hello";
    // str2 是一个指向字符串字面量的指针,它自己只是一个指针变量
    char *str2 = "Hello";
    printf("sizeof(str1) = %zu\n", sizeof(str1)); // 输出 6 (包括'\0')
    printf("sizeof(str2) = %zu\n", sizeof(str2)); // 输出 8 (指针大小)
    // str1 可以被修改,因为它是一个数组
    str1[0] = 'h';
    printf("str1: %s\n", str1); // 输出 "hello"
    // str2 指向一个只读内存区域,修改它会导致未定义行为 (通常是段错误)
    // str2[0] = 'h'; // 这行代码很危险!
    return 0;
}

总结与对比

场景 数组名 arr 的含义 类型 示例
作为表达式 (赋值、运算、函数参数) 衰变为首元素地址 int* (对于 int arr[N]) int *p = arr;
sizeof 操作符 代表整个数组,计算总字节数 int[N] (数组类型) sizeof(arr)
& 取地址操作符 代表整个数组的地址 int (*)[N] (指向数组的指针) &arr
作为字符串字面量初始化 代表整个字符数组 char[] char str[] = "test";

重要提醒

  1. 数组名不是变量:它是一个常量指针,你不能对它进行赋值操作。
    int arr[5];
    arr = some_other_pointer; // 错误!不能给数组名赋值
  2. 理解指针与数组的关系:C语言的设计使得数组和指针的语法非常相似。arr[i] 总是等价于 *(arr + i),也等价于 *(i + arr),也等价于 i[arr](虽然最后一种写法不推荐,但它能证明数组访问的本质是指针运算),这种等价性是C语言强大的特性,也是初学者容易混淆的地方。

希望这个详细的解释能帮助你彻底理解C语言中数组名的含义!

-- 展开阅读全文 --
头像
织梦如何开通支付宝支付?
« 上一篇 04-27
dede如何制作双语网站?
下一篇 » 04-27

相关文章

取消
微信二维码
支付宝二维码

目录[+]