指针和数组有什么关系呢?
有些人认为指针就是数组,数组就是指针,这种说法是错误的,指针就只是指针,数组也只是数组,它们之间是没有任何关系的
比较1:求数组大小和指针字节的大小
先明确一点即使我用sizeof(p)这种方式也不能说成sizeof是一个函数,sizeof只是一个关键字,不要被表象所迷惑。
char *p = "abcdefgh";
char array[] = "abcdefgh";
printf("p:%d\n", sizeof(p));
printf("array:%d\n", sizeof(array));
如果你把指针当成数组,那么指针大小岂不是8个字节,而我们知道在32为平台下,指针的大小永远是4个字节,那如果你把数组当成指针那岂不是只有4个字节。因此单从这一点来看你就不能弄错。
比较2、以指针的方式和以下标的方式去访问指针
我们都知道在这里p是一个指针变量,在栈上占4个字节,而在p存储的是字符串常量的首地址,字符串常量是在静态全局区的,因此这是静态全局去的地址。我们并不知道这块地址的名字,因此这种是完全匿名的方式这个字符串常量的字节大小为8.如果我们要访问里面的元素我们可以通过指针的形式去访问
比如如果我们要访问字符串常量中的d
1)以指针的形式去访问:*(p+3);
p保存的是这个字符串常量的首地址,给指针+1实际上是加上类型的字节的大小,因此加3相当于加上sizeof(char)*3因此通过这种方式可以访问到d
2)以下标的形式去访问字符串常量中的d:p[3]
先得到p值再加上3个元素的偏移量得到新地址,然后再对新地址解引用拿到值,这种方式的访问其实和以指针的形式去访问没有差别。
比较3、以指针的方式和以下标的方式去访问数组、
我们都知道数组array是保存在栈中的,如果我们要拿到数组的首地址必须通过这个数组名得到,如果要去访问数组中的元素则可以通过对这个数组名加上所需要的偏移量。因此这是一种具名+匿名的方式
1)以指针的方式去访问数组:*(array+3)
先通过array的到数组的首地址,再加上3个字符的偏移量(这个偏移量指的是元素的偏移量)的到字符d的地址再解引用得到字符d
2)通过下标的方式去访问数组:array[3]
直接通过array拿到数组的首地址再加上3个元素的偏移量,再取出其中的值
可以看出指针和数组没有关系,只是它们访问的方式相似,指针是完全匿名访问,数组则是具名+匿名的访问方式
此外还要注意一点我们是不能去修改字符串常量的值的因为它保存在静态全局区,只具有读属性。
比较4:声明为数组,定义为指针,和声明为指针,定义为数组
首先我们要分清楚声明和定义的区别:
声明:没有为这个变量分配内存,因此可以声明多次
定义:为变量实际分配了内存,只能定义一次,这个变量的地址以后是不变的。因此不能多次定义一个变量。
extern 的注意事项:如果在一个.c文件里使用extern 关键字来表示使用在另一.c文件里定义的变量或者函数,那么这个变量或者函数必须是全局的。并且extern int a的时候是不能对它进行初始化的,也就是你不能写成extern int a=10的这种情况。
声明为数组,定义为指针
在a.c文件里定义一个指针变量p保存字符串常量的首元素的首地址
char *p = “abcdefgh”;
在b.c文件里使用extern 来声明成数组
extern char p[4]
也即是在b.c的文件里会认为变量p是一个数组,存放了9个char类型的元素,但注意这是声明不是定义,并没有实际为这个变量p分配内存空间,它的内存空间是在a.c中定义的时候分配的,但是在a.c文件中我们定义的是指针类型,也就是p只占了4个字节大小。但是我们的b.c文件并不知道,依旧把它当成数组去使用。
但是我们p在a.c文件里保存的是字符串常量的地址
如果们在b.c的文件里把p当成数组使用是什么情况?
如果你在b.c文件中把p当成数组使用那么
很明显这并不是我们所需要的某块内存的地址并且如果我们对这里的p[i]进行赋值也会导致我们指针变量p原来保存的地址被改变,无法再找到原来指向的真正的地址。这就很可怕了。
声明为指针,定义为数组:
在a.c文件里定义一个数组
char p[] = “abcdefgh”;
在b.c文件里使用extern 来声明一个指针
extern char *p;
如果我们在b.c文件中使用这个指针,我们都知道在a.c文件中数组p大小是9个字节里面保存的是字符,但是p作为指针只能看到前4个字节
#include<stdio.h>
#include<stdlib.h>
extern char *p;
int main()
{
printf("%p", p);
system("pause");
return ;
}
由于我的计算机是小端模式因此会是64 63 62 61
指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址
注意这里的64 63 62 61都是16进制对应10进制为 d c b a的ASCII码,因此从这我们看出这个指针变量p的地址其实是数组的前4个元素的ACSII值
而如果我们再b.c文件中对这个p里面保存的地址可能就不是一个有效地址。
于是访问这个地址里的内容程序崩溃了:
由此可见指针是一把双刃剑,你既可以利用它去访问你需要的内容,但也可能因为它导致非法访问内存导致程序崩溃。
联想到之前的数组指针,指针数组,就顺便一起再整理整理
指针数组:指针数组是数组
int *p1[10]
[ ]的优先级高于*,因此p先和[]因此就先构成了数组的定义,数组名为p1,数组里存放的是10个指向int类型的指针
数组指针:数组指针是指针
int (*p2)[10]
()的优先级最高,因此p2先和*结合形成了指针的定义,指针变量名为p2,p2指向的是一个具有10个int类型的数组