数组、字符串和指针 | C指针基础(二)
- 数组名 arr、&arr 与 &arr[0] 打印结果为何相同?
- 字符串与指针
- 利用指针读取数组
数组名 arr、&arr 与 &arr[0] 打印结果为何相同?
先运行下面这段程序:
#include <stdio.h>
int main() {
int arr[10] = {0};
printf("%d\\n", arr);
printf("%d\\n", &arr);
printf("%d\\n", &arr[0]);
return 0;
}
运行后你会发现,三个 printf 打印出来的结果完全相同,并且输出的都是地址。 这是因为:在表达式中,数组名 arr 会退化为指向首元素的指针,其值等于 &arr[0];而 &arr 取的是整个数组的地址,虽然类型不同,但地址数值也与首元素地址相同,因此三者打印结果一致。
也就是说:在C语言中,数组名本质上是一个指针,存储的是该数组第一个元素的内存地址。数组名的取地址和取第一个元素的地址是同一个操作。
字符串与指针
我们知道,在 C 语言中,字符串本质上也是一种字符数组,因此可以仿照上一节的方式运行下面的程序:
#include <stdio.h>
int main() {
char name[10];
char name1[10];
char name2[10];
scanf("%s", &name);
scanf("%s", name1);
scanf("%s", &name2[0]);
printf("%s\\n", &name);
printf("%s\\n", name1);
printf("%s\\n", &name2[0]);
return 0;
}
运行后你会发现,分别向 name、name1、name2 输入内容后,三个 printf 都能正确打印出最开始输入的字符串,而不是像上一节那样打印地址。
这是因为两次示例使用的格式占位符不同:数组示例中使用 %d,打印的是指针本身保存的地址数值;而字符串示例中使用 %s,printf 会沿着传入指针所指向的首字符开始,逐个读取并输出字符,直到遇到 \\0 结束符,因此最终打印出来的是完整的字符串内容。
对比示例:
// 数组示例:%d 输出地址
printf("%d\\n", arr); // 输出类似:一串地址数值
// 字符串示例:%s 输出字符串
printf("%s\\n", name); // 输出类似:hello(你输入的内容)
也就是说:在 C 语言中,字符串是字符数组,字符串名同样可以看作指向首字符的指针;&name、name、&name[0] 在数值上都等于字符串首字符的地址。
利用指针读取数组
前面已经知道,数组名在表达式中会退化为指向首元素的指针,因此除了用下标访问,还可以利用指针来读取数组元素。先看下面的程序:
#include <stdio.h>
int main() {
int arr[10] = {10001, 20003, 0};
printf("%d\\n", *arr); // 等价于 arr[0]
printf("%d\\n", *(arr + 1)); // 等价于 arr[1]
return 0;
}
运行后会输出:
10001
20003
根据上一篇笔记可知,* 是解引用运算符,用来取出指针所指向的内容;而数组名 arr 本身就表示首元素地址,所以 *arr 就是取出第一个元素,等价于 arr[0]。
同理,arr + 1 并不是把地址简单地加 1 个字节,而是按照指针指向的类型自动步进。因为 arr 是 int 类型数组,arr + 1 会指向下一个 int 元素,也就是 arr[1] 的地址;再对它解引用,就得到第二个元素的值。
下面两组写法完全等价:
printf("%d\\n", arr[0]); // 下标方式读取第一个元素
printf("%d\\n", *arr); // 指针方式读取第一个元素
printf("%d\\n", arr[1]); // 下标方式读取第二个元素
printf("%d\\n", *(arr + 1)); // 指针方式读取第二个元素
推广到一般情况,arr[i] 与 *(arr + i) 等价,这就是 C 语言中指针与数组之间的联通关系。



