该系列为本人的学习笔记,主要由本人整理书写而成。部分内容来自教材、视频课程等,不能保证完全原创性。
萌新的学习笔记,写错了恳请斧正。
# 数组名的理解
数组名其实就是数组首元素的地址,只有两个例外 (马上讲)
我们不妨写一个程序验证一下:
#include <stdio.h> | |
int main() | |
{ | |
int arr[10] = { 0 }; | |
printf("%p\n", &arr[0]); | |
printf("%p\n", arr); | |
return 0; | |
} |
运行后发现两者结果相同,都是相同的地址。
但是在下面这一串代码中,好像情况有所不同:
#include <stdio.h> | |
int main() | |
{ | |
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; | |
printf("%d\n", sizeof(arr)); | |
return 0; | |
} |
这里输出的结果为数组的长度:40 字节
但是 arr 难道不是数组首元素的地址吗,地址的长度怎么会是 40 个字节呢?
其实,这就是两个例外中的一个。
# 例外情况
- sizeof (数组名):sizeof 中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小
- & 数组名:这里的数组名表示整个数组,取出的是整个数组的地址 (整个数组的地址和数组首元素的地址值一样但是类型不同,解引用得到的不是首元素而是整个数组)
为了更深入的理解取地址数组名与数组首元素地址的区别,我们看看这段代码:
#include <stdio.h> | |
int main() | |
{ | |
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; | |
printf("&arr[0] = %p\n", &arr[0]); | |
printf("&arr[0]+1 = %p\n", &arr[0]+1); | |
printf("arr = %p\n", arr); | |
printf("arr+1 = %p\n", arr+1); | |
printf("&arr = %p\n", &arr); | |
printf("&arr+1 = %p\n", &arr+1); | |
return 0; | |
} |
其运行结果如下:

这里我们发现 & arr [0] 和 & arr [0]+1 相差 4 个字节,arr 和 arr+1 相差 4 个字节,是因为 & arr [0] 和 arr 都是
首元素的地址,+1 就是跳过一个元素;但是 & arr 和 &arr+1 相差 40 个字节,这就是因为 & arr 是数组的地址,+1 操作是跳过整个数组的。
# 使用指针访问数组
知道数组名的含义后,我们其实就可以通过指针来访问数组了
#include <stdio.h> | |
int main() | |
{ | |
int arr[10] = {0}; | |
int i = 0; | |
int len = sizeof(arr) / sizeof(arr[0]); | |
int* p = arr; | |
// 输入 | |
for(i = 0; i < len; i++) | |
scanf("%d", p + i); //@@1 | |
// 输出 | |
for(i = 0; i < len; i++) | |
printf("%d ", *(p + i)); //@@2 | |
return 0; | |
} |
注:上方 @@1 和 @@2 两处的 p 均可替换为 arr
所以说 arr 与 p 在这里是等价的
那我们能通过 arr [i] 来访问数组的元素,那是不是也可以通过 p [i] 来访问呢?
答案是肯定的,如下:
#include <stdio.h> | |
int main() | |
{ | |
int arr[10] = {0}; | |
int i = 0; | |
int len = sizeof(arr) / sizeof(arr[0]); | |
int* p = arr; | |
// 输入 | |
for(i = 0; i < len; i++) | |
scanf("%d", p + i); | |
// 输出 | |
for(i = 0; i < len; i++) | |
printf("%d ", p[i]); //@@@ | |
return 0; | |
} |
所以 p [i] 实际上就等价于 *(p+i)
那么 arr [i] 是不是也就等价于 *(arr+i) 呢?
是这样的。在编译器处理时,arr [i] 实际上就是被转换成首元素指针加偏移量,然后解引用来访问的,而知道这些后,我们甚至可以玩一点新花样。
# 数组访问的特殊写法
我们知道加法操作符左右两边是可换的 (两表达式不互相影响时)
所以 *(arr+1) 其实可以写成 *(i+arr)
而 *(arr+i) 等价于 arr [i], 那么 *(i+arr) 是不是也就可以写成 i [arr] 呢?
答案是,可以!!!
所以 arr [i] 也可以写成 i [arr]!!!
不过我们一般不这么写
# 一维数组传参的本质
如果我们把一维数组传递给一个函数,本质上传递的是首元素的地址
这也就是为什么数组传参后函数操作会实际的影响到数组本身
因为传递的是地址,解引用后实际操作的是原参数 (实参) 的内存空间
而只知道首元素地址是不能确定一个数组有多长的,所以我们一般把长度一起传过去
所以接受的类型应该是指针类型:
void Print(int* arr, int len) | |
{ | |
for (int i = 0; i < len; i++) | |
printf("%d", arr[i]); | |
} |
但我们之前讲过数组传参可以写成这样:
void Print(int arr[], int len) | |
{ | |
for (int i = 0; i < len; i++) | |
printf("%d", arr[i]); | |
} |
实际上这两种写法等价,本质上还是指针 (其实是 C 语言专门设计为可以写成这样便于理解)
这也就解释了为什么参数中 arr [] 的方括号内不需要数字,而有数字也会被忽略