[原创]开博啦,说说C/C++的数组("[]")与指针("*")
最初学习C++的时候(先学的C++,后来又补的C),对于指针就很是茫然,包括指针到底是什么,指针有什么用,等等。当然现在这些困惑都慢慢的解开了,不过依照“温故而知新”的古训,把其中最好玩的一部分拿出来,希望对初学C/C++的朋友有些帮助,也希望各位老师能够对我提出批评建议,谢谢。
一、问题
题外话到此结束。先看几个题目。(呵呵,题目都是我自己出的,有不严谨的地方还望指出,谢谢。
1、下面代码输出什么?
#include <iostream> void f1( char** A ) { std::cout << sizeof( A ) << std::endl; } int main() { char A[2][7] = { "abcdef", "hijklm" }; f1( A ); return 0; }
2、下面代码有错误吗?如没有,会输出什么?如有,改正之,并思考输出是什么。
#include <iostream> void f1( char* A, char B[], char* &C ) { std::cout << sizeof(A) << " " << sizeof(B) << " "; std::cout << sizeof(C) << std::endl; } int main() { char A[7] = "abcdef"; char *B = "abcdef"; f1(A, A, A); return 0; }
3、下面代码有错误吗?如没有,会输出什么?如有,改正之,并思考输出是什么。
#include <iostream> void f1( char** A ) { std::cout << sizeof( A ) << std::endl; } int main() { char A[2][7] = { "abcdef", "hijklm" }; f1( A ); return 0; }
二、答案及讨论。
1、
第一题:输出“7 4”。(VC6和GCC)
解释:这里就要引出数组和指针的第一个区别:数组类型(array type)是由数组元素的类型以及数组的长度决定的。(ISO/IEC 9899:1999(E)(即C99标准)P35),而指针类型(pinter type)是由其所指向的元素类型(即被引用类型决定的)。(呵呵,有没有晕啊,结合第一题解释一下。)
第一题中的char A[7] = "abcdef", char *B = "abcdef". 显然A为数组,B为指针。所以A为“char类型的数组,并且长度为char的7倍”;而B为“指向char类型的指针”(顺便问一句,为什么A的长度要写7而不是6呢?)。所以对于sizeof操作符,sizeof(A)会返回A的长度,刚才提到A长度为char的7倍,在Win32上char为1个字节,所以sizeof(A)为7;而同样的sizeof(B)也返回B的长度,所不同的是B是一个指针,在Win32平台上指针为32位,即4个字节,所以返回4。
总算说完了,跟说绕口令似的,不知道你有没有明白啊?
再说清楚点,附上张图。下图是“abcdef”在内存中存储的示意图。B指向的是“abcdef”的起始地址,即字符a的地址。而A是代表着从a到\0的整个区域。所以A的长度为7,B的为4。
2、
第二题:错在f1(A, A, A)上。错在最后一个参数上,如没有最后一个参数,输出为“4 4”。(VC6和GCC)
解释:f1的最后一个参数为char* &C,即C首先是一个引用,该引用的类型是一个char*的指针。问题来了,因为引用就是别名,当形参带有引用时,要求引用和指针类型完全相同。A与C因为类型不合被编译器检测出来,报错。
如果去掉最后一个参数,输出为“4 4”,这是因为形参上的 char* 与char[] 是完全等价的,在将实参传递给形参的时候,不管遇到的是char*还是char[],数组都将弱化为char*(事实上,因为是传值调用,所以在传递参数过程中,类型发生了变化,主函数内的A依然是char[7],而f1内的A和B确实char*)。
3、
第三题:错在f1的声明上,或者说错在main调用f1上。如果修改f1,可以改成void f1( char (*A)[7] );如果修改main函数中调用f1那句可以改成f1( (char**)(*A) );(那个……那个……确实太难看了)。不管怎样改,结果都是“4”。(VC6和GCC)
解释:回顾一下第一题和第二题,第一题中提到:数组类型是由两者确定的:其一,数组元素;其二,数组个数。第二题中提到:在传参的时候数组可以弱化为数组元素的指针类型(即char类型的数组可以弱化成char类型的指针)。
那么回来看看这个题目,这里char[2][7]要传参给char**,首先要搞清楚char[2][7]是个什么东西,套用刚才的类型定义,应该这样说:其一,char[2][7]的元素为char[7];其二,个数为2。那么,根据第二题,在传参过程中,char[2][7]可以被弱化为char(*)[7],可是f1要接受的参数是char**,和char(*)[7]不同,也不能互相转化(一个是指向char数组的指针,一个是指向char*的指针)。类型不合,编译器报错。
解决方法就是让两者类型一样,或者f1改成void f1( char (*A)[7] ),或者将A强制转化为char**,当然不能直接转化(刚才已经说了,两者类型不同也不能互相转化),这里提供的方法是先取A的第一个元素,即*A,*A的类型为char[7],char[7]可以转化为char*,再进一步强制转化为char**。因为不管哪种方法,A都已经转化为了指针,所以,结果都是4。
4、
还有一个小点:数组是常量,指针是变量,还以char A[7] = "abcdef", char *B = "abcdef"为例,可以写++B,但是不能写++A。
三、结语
就想起这些来,先写这些吧。其实数组和指针差别很大的,只不过是数组可以弱化为同类别的指针,于是他们的关系就变得相当的暧昧了。
这是我的第一篇博文,希望能给你带来些许的收获,也欢迎来指正不足,谢谢!!