今天在学习C语言的时候学到了一些关于数组和指针的比较重要的知识点
数组名本质上是数组首元素的地址
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%p\n", arr);
printf("%p\n", &arr[0]);
return 0;
}
由代码可以看出,当打印arr的地址和打印arr[ 0 ]的地址的时候,输出的结果是相同的。
给函数传递数组,数组参数应该由指针变量或数组名+[]来接收
#include<stdio.h>
void print(int* m)
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
print(arr);
return 0;
}
也可以使用数组名+[ ]来接收([ ] 中有没有数字都可以),这样写可以让别人很同意就看出来该函数接收了一个数组的传递。二者本质上是一样的。
#include<stdio.h>
void print(int m[10])
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
print(arr);
return 0;
}
给函数传递数组的时候,只传递了首元素的地址,并没有将整个数组传递到函数中
#include<stdio.h>
void print(int *m)
{
int sz = sizeof(m) / sizeof(m[0]);
printf("%d\n", sz);
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", sz);
print(arr);
return 0;
}
在主函数中,求数组的长度sz可得到sz的值为10,在print函数中sz的值为2,因为在print函数中,m为数组首元素的地址,sizeof(m)得到的值为8(我用的是64位的电脑,所以地址长度为8,如果使用32位的电脑,sizeof(m)得到的值为4,最后结果为1,这里不必纠结),而m[ 0 ]是具体的int类型的值,长度为4,所以sz的值为2。也就说明在给函数传递数组的时候,并没有将整个数组传递给函数,只是传递了该数组的首元素的地址。
在函数中是无法求得数组的长度的,只能通过传递的方式获取数组长度。
#include<stdio.h>
void print(int *m,int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", m[i]);
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
print(arr, sz);
return 0;
}
运行结果为
指针和数组
数组元素在内存中是连续存放的。我们可以通过数组的第一个元素的地址求到该数组的其他元素。
先看代码
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &arr[0];
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%p %p\n", p + i, &arr[i]);
}
return 0;
}
运行结果
从运行结果我们发现,当把arr的起始地址赋值给p的时候, p+i 等价于 &arr[ i ]。 p+i 就是下标为 i 的元素的地址。
以下代码是比较常见的打印数组中各个元素的写法
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
当我们知道 p+i 就是下标为 i 的元素的地址 就可以
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &arr[0];
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
两种方式的运行结果是一样的
有人可能会有疑惑:对于 int 类型的数组,如果p是arr的起始地址,那为什么 p+1 是arr的第二个元素的地址而不是 p+4是arr的第二个地址呢,相邻的两个元素之间的地址不应该相差4个字节吗?
其实是这样的,int 型的指针+1 意思是跳过一个整形,得到的是下一个元素,而不是地址+1。
如果是char 类型的指针,+1跳过的是一个字符,得到下一个元素。
我们刚刚讲到:数组名是数组首元素的地址,也就是arr 等价于 &arr[ 0 ](该文章的第一个代码),既然我们可以写 int*p=&arr[ 0 ],arr 等价于 &arr[ 0 ],那我们同样可以写成 int* p=arr,二者是等价的。出现arr的地方 我们可以换成p 同理,出现p的地方 我们可以换成arr。既然这样,上面的代码可以写成如下
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(arr + i));
}
return 0;
}
同样也可以是这样的
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", p[i]);
}
return 0;
}
运行结果是一样的。
我们知道 printf("%d ", arr[ i ]) 可以打印下标为 i 的元素的内容 , printf("%d " , *(arr+i)) 同样可以打印下标为 i 的元素的内容,事实上arr[ i ] 本质上是通过 arr+i 然后解引用得到数组内容,也即是说arr[ i ] 本质上是 *(arr+i) 。
既然 arr[ i ] 本质上是*(arr+i) ,而根据加法交换律 *(arr+i) 可以写成 *( i+arr ),既然可以写成 *( i+arr ) 那么是不是也可以写成 i[ arr ] 呢? 我们试一试
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", i[arr]);
}
return 0;
}
得到运行结果
我们发现这样也是可以的。下面来解释一下
我们知道 ’ [ ] ‘ 是下标引用操作符,arr [ i ] 本质上是 *( arr+i ) ,我们可以理解成 arr 和 i 是 ‘ [ ] ’ 操作符的两个操作数 ,这样理解就可以明白为什么可以写成 i[ arr ] 了。
在此声明一下,本人刚刚所写的内容,只是为了让大家可以更加深刻地理解数组和指针,内容中部分代码的写法虽然正确,但是并不提倡这样写。比如 arr [ i ] 可以写成 i[ arr ] ,虽然可以这样写,这样写没有错误,但是并不提倡这样写,因为这样写太不直观了。
以上全部内容可能对于部分朋友来说可能排版有点乱,如果有不懂的地方希望大家可以自己动手实践一下,只有自己动手实践,才能对知识点有更深的理解。