以下是引用uswood在2015-9-13 20:35:29的发言:
明白了,最终即便指向那个数组,但实际只保存第一个的地址,而本身转换成了int型所以sizeof会是4。
但如果是这样的话要用什么才能表示那整个动态空间呢?
1.不是转换。p是指针,*p是解引用,此时对象为指针所指向的变量,而sizeof()是求对象尺寸的,由于你声明的指针是int* p,即p所指向的对象被声明为int,则sizeof()所求者亦为int的尺寸。注意:sizeof()其实是不管对象具体是什么的,它祗按对象所归属的数据类型来返回,故sizeof(x)和sizeof(int)并无区别。这种情况不存在转换(实际上没有什么机制是转换的),编译器就是根据声明int* p来返回,int* p和int *p等价,sizeof(*p)就等价于sizeof(int)。若是sizeof(p),则是指针变量自身的尺寸,在32位机器和编译环境中,它的值就是4,不管指针指向什么数据类型,这个值都是4。事实上,sizeof()不是函数,而是编译器在编译期间根据环境设置立即得到的常数,不用在运行时再运算,它与宏的性质相似。
2.变量,是一个可以用名字来访问的数据存储空间,比如int x,是在栈上分配的变量,通过变量名x就可以访问到这个数据,它实际上是一个地址的名称,x可以被赋予不同的值,但地址始终是这个地址。指针也是一个变量,但它所储存的值有特殊意义,当一个变量被视为指针的时候,程序员假定这个变量的值是某个地址,而要取出这个被指向的地址的数据,需要使用解引用操作符约定。通常,在栈上分配的变量或空间(数组是一个连续储存若干同类变量的空间),都有变量名,这种变量和空间,其实是不需要使用指针访问的。但在堆上分配的数据,却是没有变量名的,这种数据,就一定要使用指针来访问。malloc()的动作,就是在堆上申请并分配得一块存储空间,由于没有变量名,所以把这块空间的地址返回来,好让程序员访问这块空间的数据,因此,返回的指针就是那整个动态空间的入口点,也是唯一的访问手段。
返回你原先的问题。recalloc()是重新分配堆空间,它的函数用法是void* recalloc(void* old_ptr, int size),第一个参数是原先已分配空间的指针,表示要对这块空间重新分配,要求最终的空间大小是第二个参数size所表示的字节,然后,把最终空间的地址返回来。这个动作有不同的操作:如果size比原先的小,那很好办,把尺寸边界缩小就可以了,此时,返回的指针就是原来空间的指针;但如果要求的新空间比原来的大,那么麻烦就来了,因为此时不可能简单地扩张空间边界(因为极可能已被分配给别的申请了),那么,内存管理系统就要另外找一块连续的满足尺寸条件的空间来替代原来的空间,并且要把原来空间中的数据搬动到新空间中,此时,函数返回的空间地址,就不再是原来的地址,故函数不是直接使用第一个参数的指针返回新空间,而是另外返回一个指针。这是很严重的问题,很多人并不知道recalloc()既然已经有指针参数,为何还要再返回一个指针,以为那是多余的,更会误以为重新分配后的空间仍可以用原来的指针入口。你原先的崩溃现象,就出在这里,新申请的空间比原来的大,位置已经挪动了,但你却用原来的指针去访问数据,那就是野指针,故只有你使用函数返回的指针作为访问指针时,结果才正确。不明白这个道理的人,会乱用recalloc(),其危险根据上面解释可知。
另外一个隐蔽的问题,也属于指针问题,就是假定你原先动态分配的指针是p1,然后又使用另外的p2、p3之类的指针指向同一个空间(通过p2 = p1、p3 = p1的赋值操作即可,尤其是在函数参数传递时,实际上已经复制了一份指针),那么若偶然recalloc()之后,p1的指向已经改变(或被free()掉),而此时p2、p3等是不知道的,你的代码极可能还会使用p2、p3去访问数据,这种正是高级程序员也难以防范的错误,而且几乎无法排查。这也是许多比C高级的程序如C++、C#、Java等戒用指针的原因,那些课程的书上都有详细讲解为什么会戒用指针。
指针的隐患是在这些地方!
如果按你说的1楼的代码是教学视频上看来的,那么可知做这个教学的人自己也是乱来,看这种东西学习,后果可想而知。不过,很多人以为偶然的成功,而当作必胜,学到错误的方法和知识而不知道,一直那样做下去,还自觉学会了,那才最可怕。出了错误还好,就怕不出错误。