[原创]开博啦,说说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。

abcdef的内存存储示意图

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。

三、结语

     就想起这些来,先写这些吧。其实数组和指针差别很大的,只不过是数组可以弱化为同类别的指针,于是他们的关系就变得相当的暧昧了。

     这是我的第一篇博文,希望能给你带来些许的收获,也欢迎来指正不足,谢谢!!

posted on 2009-03-07 17:47  凤凰羽翼  阅读(606)  评论(8编辑  收藏  举报

导航