一、首先,数组名不是指针!!!
我做了如下程序供大家学习和参考:
#include <stdio.h>
int main()
{
int *p;
int *q;
int a[15];
q = a;
printf("指针 p 的大小是:%d\n", sizeof(p));
printf("指针 q 的大小是:%d\n", sizeof(q));
printf("数组 a 的大小是:%d\n", sizeof(a));
printf("指针 p 中存放的地址是:%p\n", p);
printf("指针 p 的地址是:%p\n", &p);
printf("指针 q 中存放的地址是:%p\n", q);
printf("指针 q 的地址是:%p\n", &q);
printf("数组 a 首元素的地址是:%p\n", a);
printf("数组 a 的地址是:%p\n", &a);
return 0;
}
结果如下:
现在我们来分析一下这几个输出到底是什么意思:
- p、q的指针大小很好理解,我是六十四位操作系统所以指针大小是八位,但是现在假设 a 也是指针,那他的大小为什么是60呢?对吧,所以数组名不是指针!
- 指针的结构是一个类似于这样的结构:
我们先开辟一个0x4000的空间,用来存放地址,由于各个操作系统的不同(例如六十四位和三十二位的操作系统),所以指针的大小也会有所不同,当开辟了一个空间之后,这个空间里面原本存放的数据实际上对我们来说是没有用的,所以在我们给指针赋值前指针里面存的数据就是不确定的,我们不能拿来直接使用,就像 p 中存放的地址是0x0010,这个地址不是我们能够使用的,我们没有权限,也不知道它指向的是那一片区域,所以指针在使用前必须赋值,大家可以看到 q 指针在赋值之后里面存放的地址就是数组 a 的地址,我们通过这个地址能够找到一片我们可以访问的区域就是0x61FDD0; - 从 a 的地址可以看出,指针是在开辟的这片空间里放了一个地址,然后通过地址去找到指向的空间,但是实际上空间的大小他是不知道的,而数组就是在这个指针的地方分配空间,成为如下的一种数据结构:
所以这就是为什么数组 a 的地址和 &a 的地址是一样的缘故,因为首元素的地址就是数组地址,而你不能说指针指向的地址就是指针地址
二、数组名和指针是不是存在某种联系呢?
当然在实际操作中,我们经常会将数组地址交给一个指针,而同时,我们对指针的操作也同时可以对数组进行(除了修改),大家可以参考如下代码:
#include<stdio.h>
void swap(int *a)
{
int t;
t = a[0];
a[0] = a[1];
a[1] = t;
}
int main()
{
int a[5] = {1, 2, 3, 4, 5};
swap(a);
printf("%d\n", *a);
printf("%d\n", *(a+1));
printf("%d\n", a[1]);
return 0;
}
结果如下:
可以看到,我们对指针的操作同样是可以对数组进行,首先,我们是吧数组名当作指针来传到函数里面,用了swap(a);这一句,我们通过传递数组名来进行数组前两个元素交换的操作,我们可以看到函数形参里面是声明了一个 int 类型的指针,然后我们还像指针一样,寻找数组里面的元素,这么一看,数组名确实是和指针又有一定的联系。
这里要提一点,因为你将数组赋值给一个指针形参然后传到函数里了,所以这个数组到了函数里实际上又变成了一个指针,前面我们在主函数里查看数组 a 的大小发现大小是60,但是到函数里查看大小就会发现大小变成了8,这是因为,你将数组赋值给函数形参之后他就失去了原本的特性,变成了一个真真正正的指针了,虽然你还是可以通过这个指针来找到数组的数据,但是要知道他们的性质发生了改变。
三、const指针
实际上,如果你非要吧数组名当作一个指针来看,他是一个类似于 int * const p 类型的指针,看到这里大家可能有些疑惑,这个 int * const p 是个什么
其实一个指针无非就是这样子,而 const 的作用就是限定一个变量不许改变,你看,在上面那张图里,我们一共可以限制两个地方不改变,一个就是让这个 5 不变,另一个就是让 0x4000不变,这就引出了三种写法:
- int const *p;
- const int *p;
- int * const p;
前两种写法表达的意思是一样的,一旦这个 5 确定下来了,你就不能做 *p = 6;这个操作,当然并不是说这个 5 就不变了,而是你不能通过操作指针来,修改这个这个值,那现在要想修改怎么办,首先,指针的指向是不是可以改变,我们是不是可以让0x5000里面存别的地址来修改指向的数据,比方说现在有个 i 变量等于6,放在0x6000处,那我们重新让 *p = i;就行了,当然 *p = 6;不让,那我们可以让指针指向的变量变化,比方说变量 j 的地址就是0x4000,我们可以让 j = 6;这样我们再通过0x5000找到这个值的时候,结果也被改变了;
添加代码:
int const *b = (a+1);
a[1] = 10; //修改变量的值
printf("%d\n", *b);
b = (a+2); //修改 b 的指向
printf("%d\n", *b);
结果如下:
说到这里,这个不让指针修改变量到底有什么用呢?
看如下代码:
void assignment(const int *a)
{
*a = 10;
}
我们创建了一个叫 assignment 的赋值函数,我们原本希望在函数里修改 a[0] 的值,但是产生了如下问题:
这说明什么?说明我们不能在函数里修改数组元素了,这对我们保护数据是不是起到了一定的作用,当有人在这个函数里非法修改数据时,是不是就可以阻止这件事情的发生;
再来说说 int * const p;他和前面两个的区别是,他不让修改的是指针的指向,所以这个时候,你就可以用它来修改指针指向地址的值,如下:
现在想想数组名,是不是你拿到一个数组名,你可以通过它来修改数组元素,但是不能修改数组指向的地址,是不是发现他们有些相似;
所以,当我们想要保护好一个指针的时候,我们可以在声明形参的时候声明一个 const int *const p;这样当指针传入函数的时候,它即不能修改指针的指向,也不能修改指针指向的元素,当然,上面提到的三种形式该怎么记忆理解呢?(int const *p; const int *p; int * const p;)
这个我们就要看看 星号和指针名在不在一起了,如果在一起,那么就说明 *p指向的东西不能改变,没有在一起说明 p 不能改变。
说了这么多,我有想过可不可以自己分配空间给一个指针然后我们自己造一个数组呢?我想了很久,以我现学的知识应该是造不出来了,本来想的是用malloc,但是实际上是不行的,留个小疑问,供大家思考,哈哈!
四、总结
这篇文章是我看了恺哥的视频学习加上查资料学习得来,学完发表一下心得,感谢恺哥!感谢百度!