<技术篇-技巧一>数组和指针并不相同

来源:互联网 发布:云南菜 知乎 编辑:程序博客网 时间:2024/04/29 04:03

抛出一个问题:一个文件有代码:char p[100];另一个有代码:extern char *p.这个是正确的还是错误的。

要解决这个问题,首先我们要了解什么是声明,什么又是定义。通俗来讲,定义就是(编译器)创建一个对象,为这个对象分配一块内存并给它取上一个名字,这个名字就是我们经常所说的变量名或对象名,比如int i,就是个定义,它被分配了一块内存,取了个名字标明这块内存,就是i,这个名字i一旦和这块内存匹配起来,它们就同生共死,终生不离不弃。并且这块内存的位置也不能被改变。一个变量或对象在一定的区域内(比如函数内,全局等)只能被定义一次,如果定义多次,编译器会提示你重复定义同一个变量或对象。一般来说,定义的内存都有个初始值,比如int i,初始值一般为0,假如i=10,就是说i表示的这块内存存放的值变作10,记住,‘=’是表示赋值的意思,而不是改变i标示的这块内存的地址。改变的只是i这块内存保存的值,这点要搞清楚。(顺便提一下,当变量在‘=’的右边时就表示该变量存放的值)

然后,什么是声明,它告诉编译器,这个名字已经匹配到一块内存上了,下面的代码用到变量或对象是在别的地方定义的。它所描述的并非自身,而是其他地方创建的对象。声明和定义最大的区别就在于分配了内存与对象名匹配没。比如extern int i表示在其他地方定义了一个int型的i,要用它的值得话在那个地方去拿。就不难理解extern int a[] 和extern int a[100]是等价的了。

ok,先消化一下声明和定义。

然后我们来说指针,比如定义了一个指针 char *p=“abcdefg”,实际上,编译器分配一块内存,大小是int型(指针的内存大小都是int),即四个字节,(指向的内存是char型的)标示为p(也可叫命名),假如这块内存地址为6666,然后这块叫p的内存保存的值其实是字符串“abcdefg”的首地址,假如是7777(有可能是连续的,即6670,没验证,读者可以试下验证)。而调用*p(取值)时,其实是先找到p这块内存即6666,然后提取它的内容即7777,通过7777找到字符串“abcdefg”,取得它的首地址7777代表的值‘a’。即指针是保存数据的地址,访问时是间接访问数据。数组不一样,数组是直接保存数据,访问也是直接访问数据,比如char a[]=”abcdefg“,编译器给数组a分配一块内存,大小已经是固定了的,即:char型大小*(7+1),8个字节。a代表这个首地址地址,假如是8888,假如要访问a[2]时,直接访问8888+2*1(char型大小)。这里顺便提一下,为啥不能给数组名a赋值,通俗说,就是因为编译器把a直接替换成8888了,即每当出现a其实是数组的首地址8888,当然8888不能被赋值。int i的i可以被赋值和char *p可以被赋值道理都是一样的,就是把他们内存地址表示的内容进行赋值,即替换他们内存保存的值。而不是直接改变内存地址。为啥a不能像i和p那样直接赋值给a内存存放的值呢?因为这样会造成歧义,可以参考下指针p,*p表示p指向的那块内存存放的值,p=&i表示改变p的指向,让他指向i那块内存,即改变了p存放的值。a和p都是指针类型,一个直接一个间接,*a和*p以及a[1]、p[1]都是取得最终指向的内存的值,没有像p那样的间接缓冲,a无法赋值。这也是编译器设计时考虑的。

说了这么多,我们来看看最初提出的问题。

我们定义的是char p[100];在另一个文件声明了extern char *p后,调用p[i]时,会发生什么呢?先去寻找p这块内存,提得到p[i]保存的内容,是个char的值,然后,由于声明的p是指针,会把这个值当成是一个地址来使用,于是p[i]的值就变成了char型值变成的那个地址所保存的内容了。这显然是无法接受的。

好了,不知道我说得明白没,有点绕。把思路理清了,其实指针和数组并不是那么不好区别。