user-0 提问: char**和char[][]的问题

回复:
user: user-1
content: 引用 22 楼 rongxiaojun 的回复:[quote=引用 17 楼 dcw0402 的回复:] 这里,二维数组其实是一个一维数组的加长版.... 改成这样也可以 void output(char *buffer){ 	printf("%s\n",buffer); } 我也不知道为啥,感觉7楼说有的些道理  这是因为char buffer[100][1024]中，buffer的值即char[0][0]的地址，（真不可思议，二维数组地址即第一个数组中第一个元素地址，而非第一个数组的地址），然后调用函数output(buffer)，此时buffer的值传进参数中，参数类型char*，则参数为char* 类型且指向了首字符'i',然后printf打印字符串直到'\0'出现，一切都解释通了。[/quote] 你的理解还是有问题，buffer是一个数组名，当你将buffer作为指针使用时，其被转化为一个指向数组首元素的指针。这里数组首元素是什么？是一个包含1024个char的数组（因为本质上所有数组都是一维数组），buffer[0]可以当做它的数组名！

user: user-2
content: void output（char **buffer）{    printf（“%s\n”,buffer[0]）;        // output::buffer == ((char**) (main::buffer);        // output::buffer[0] == ((char**) (&main::buffer ))[0];        //         // 这是把 main::buffer 当作储存指针的数组使用，        // output::buffer[0]是该指针数组的第一个元素        // 该指针其实是把         // main::buffer[0][0],main::buffer[0][1],main::buffer[0][2],main::buffer[0][3]。。。。        // 这4（指针长度）个字符,组成的一个整型数据，当作指针使用        // 这样 output::buffer[0] 其实是个野指针。        // 出错是正常，不错是巧合。        // 这里借用一下，C++的作用域限定符:: 表示作用域   }                                 void main(){    char buffer[100][1024] = {0};    int len = sprintf(buffer[0],"i am a bastard...");    buffer[0][len] = '\0';    output(buffer);//传递buffer 即 &buffer[0]，这里类型不对，应该报错，或者警告。  }  多级指针，需要指针数组做中转，或者动态分配数据 才能和多维数组匹配 //维持这种函数定义，需要传如指针地址，指针数组，和二级指针 void output（char **buffer）{    printf（“%s\n”,buffer[0]）;     }                                 void main(){    char buffer[100][1024] = {0};    int len = sprintf(buffer[0],"i am a bastard...");    buffer[0][len] = '\0';       /************* 1 ***************/    { char *p[100];      int i;      for(i=0;i<100;i++)p[i] = buffer[i];      output(p);    }     /************* 1 ***************/    { char *p[100];      char **pp = &p;      int i;      for(i=0;i<100;i++)p[i] = buffer[i];      output( pp );//    }    /*********** 3**********/    {      char **pp = malloc(sizeof(char *)*100);//c++ 替换为 char ** =new char*[100];      int i;      for(i=0;i<100;i++)pp[i] = buffer[i];      output( pp );//      free(pp);   //c++ 替换为 delete[]pp;    } }  多级指针，多维数组 数组维度，和指针的级别是不同的概念，虽然有对应关系。 指针级别，就是间接访问的次数， 数组维度，指的是数组层次，是一层套一层的概念，这个就是多维数组，递归定义的含义。 数组访问是逐层查找， 就像中央---省 ---县----镇----村--- 家-----个人 这样的查找的，不过是按照每一层次的大小（改维度的大小）数数而已。 数组每个维度，只是大块和小块的关系，整个数组是在一起的。  变量（所谓对象），和数据是直接对应的，所以是直接访问的。 指针，访问所指地址中的数据，是间接访问的。  多级指针，就是不断找到下级地址访问数据。每一级指针，只能找到下级指针。  指针分级，并不需要放在一起存储，除了一级一级找到地址，无法一次到位访问。 如果，某级指针未定义（初始化），就不能向下级访问了。 比如  int *p;// int **pp =& p;//  可以访问 *pp ;不可以访问 *p; 因为 p ；没有初始化----定义时候，没有初始化，也没有赋值，让他指向对象(变量)的地址。 数组，已经定义，各个维度都定义好了。 即  int a[M][N];//二维数组 a 已经定义。 a[0].........a[M-1] 都是定义好的一维子数组。 a[0][1].........a[M-1][N-1]; //都是定义好的数组元素（变量）。

user: user-2
content: void output（char **buffer）{    printf（“%s\n”,buffer[0]）;        // output::buffer == ((char**) (main::buffer);        // output::buffer[0] == ((char**) (&main::buffer ))[0];        //         // 这是把 main::buffer 当作储存指针的数组使用，        // output::buffer[0]是该指针数组的第一个元素        // 该指针其实是把         // main::buffer[0][0],main::buffer[0][1],main::buffer[0][2],main::buffer[0][3]。。。。        // 这4（指针长度）个字符,组成的一个整型数据，当作指针使用        // 这样 output::buffer[0] 其实是个野指针。        // 出错是正常，不错是巧合。        // 这里借用一下，C++的作用域限定符:: 表示作用域   }                                 void main(){    char buffer[100][1024] = {0};    int len = sprintf(buffer[0],"i am a bastard...");    buffer[0][len] = '\0';    output(buffer);//传递buffer 即 &buffer[0]，这里类型不对，应该报错，或者警告。  }  多级指针，需要指针数组做中转，或者动态分配数据 才能和多维数组匹配 //维持这种函数定义，需要传如指针地址，指针数组，和二级指针 void output（char **buffer）{    printf（“%s\n”,buffer[0]）;     }                                 void main(){    char buffer[100][1024] = {0};    int len = sprintf(buffer[0],"i am a bastard...");    buffer[0][len] = '\0';       /************* 1 ***************/    { char *p[100];      int i;      for(i=0;i<100;i++)p[i] = buffer[i];      output(p);    }     /************* 1 ***************/    { char *p[100];      char **pp = &p;      int i;      for(i=0;i<100;i++)p[i] = buffer[i];      output( pp );//    }    /*********** 3**********/    {      char **pp = malloc(sizeof(char *)*100);//c++ 替换为 char ** =new char*[100];      int i;      for(i=0;i<100;i++)pp[i] = buffer[i];      output( pp );//      free(pp);   //c++ 替换为 delete[]pp;    } }

user: user-3
content: 参数类型的原因，你可以参考下我的这篇博文，希望能让你找到答案 http://blog.csdn.net/xuchao1229/article/details/8223518

user: user-4
content: 无论一维，二维，三维，四维，五维...数组，本质上，它的首地址，都只是寻一次地址，大概可以说等价于一维指针。  但是一维，二维，三维...指针就不一样了。n维指针，要寻找n次地址。

user: user-5
content: 引用 27 楼 rongxiaojun 的回复:[quote=引用 26 楼 lwouyang 的回复:] 说了，技术上相等，因为算出来的都是同一个地址，不让它们相等能怎么办？ 怎么算出来的？ &地址符不就是取变量地址吗？都取了地址了buffer又怎么能和&buffer相等呢？buffer[0]是值，&buffer[0]是它的地址，值和地址是一回事吗？ int a = 14; 你怎么不让a和&a相等？[/quote] 因为是同一个地址，只不过类型不一样，

user: user-6
content: 这个只能弄成数组指针

user: user-7

user: user-8
content: //在堆中开辟一个4×5的二维int数组 #include <stdio.h> #include <malloc.h> int **p; int i,j; void main() {     p=(int **)malloc(4*sizeof(int *));     if (NULL==p) return;     for (i=0;i<4;i++) {         p[i]=(int *)malloc(5*sizeof(int));         if (NULL==p[i]) return;     }     for (i=0;i<4;i++) {         for (j=0;j<5;j++) {             p[i][j]=i*5+j;         }     }     for (i=0;i<4;i++) {         for (j=0;j<5;j++) {             printf(" %2d",p[i][j]);         }         printf("\n");     }     for (i=0;i<4;i++) {         free(p[i]);     }     free(p); } //  0  1  2  3  4 //  5  6  7  8  9 // 10 11 12 13 14 // 15 16 17 18 19  //在堆中开辟一个3×4×5的3维int数组 #include <stdio.h> #include <malloc.h> int ***p; int i,j,k; void main() {     p=(int ***)malloc(3*sizeof(int **));     if (NULL==p) return;     for (i=0;i<3;i++) {         p[i]=(int **)malloc(4*sizeof(int *));         if (NULL==p[i]) return;         for (j=0;j<4;j++) {             p[i][j]=(int *)malloc(5*sizeof(int));             if (NULL==p[i][j]) return;         }     }     for (i=0;i<3;i++) {         for (j=0;j<4;j++) {             for (k=0;k<5;k++) {                 p[i][j][k]=i*20+j*5+k;             }         }     }     for (i=0;i<3;i++) {         for (j=0;j<4;j++) {             for (k=0;k<5;k++) {                 printf(" %2d",p[i][j][k]);             }             printf("\n");         }         printf("---------------\n");     }     for (i=0;i<3;i++) {         for (j=0;j<4;j++) {             free(p[i][j]);         }         free(p[i]);     }     free(p); } //  0  1  2  3  4 //  5  6  7  8  9 // 10 11 12 13 14 // 15 16 17 18 19 //--------------- // 20 21 22 23 24 // 25 26 27 28 29 // 30 31 32 33 34 // 35 36 37 38 39 //--------------- // 40 41 42 43 44 // 45 46 47 48 49 // 50 51 52 53 54 // 55 56 57 58 59 //---------------

user: user-9
content: 楼主来这里看看吧http://blog.csdn.net/code_crash/article/details/4855010 如果想了解的更清晰点，直接问supermegaboy本人

user: user-0
content: 引用 26 楼 lwouyang 的回复:说了，技术上相等，因为算出来的都是同一个地址，不让它们相等能怎么办？ 怎么算出来的？ &地址符不就是取变量地址吗？都取了地址了buffer又怎么能和&buffer相等呢？buffer[0]是值，&buffer[0]是它的地址，值和地址是一回事吗？ int a = 14; 你怎么不让a和&a相等？

user: user-10
content: 说了，技术上相等，因为算出来的都是同一个地址，不让它们相等能怎么办？

user: user-0
content: 引用 23 楼 lwouyang 的回复:[quote=引用 22 楼 rongxiaojun 的回复:] [quote=引用 17 楼 dcw0402 的回复:] 这里,二维数组其实是一个一维数组的加长版.... 改成这样也可以 void output(char *buffer){ 	printf("%s\n",buffer); } 我也不知道为啥,感觉7楼说有的些道理  这是因为char buffer[100][1024]中，buffer的值即char[0][0]的地址，（真不可思议，二维数组地址即第一个数组中第一个元素地址，而非第一个数组的地址），然后调用函数output(buffer)，此时buffer的值传进参数中，参数类型char*，则参数为char* 类型且指向了首字符'i',然后printf打印字符串直到'\0'出现，一切都解释通了。[/quote] 错！准确的讲，二维数组名的值是指向第一个数组的指针值，buffer == &buffer[0]。 二维数组在概念上是数组的数组，第一个数组 buffer[0] 的值才是指向 buffer[0][0] 的指针值。这些概念是实在的，因此 buffer+1 和 buffer[0]+1 都有各自确切的意义。技术上，这几个地址值是相等的，buffer == &buffer[0] == buffer[0] == &buffer[0][0]。但是 buffer 和 buffer[0] 类型是不同的。二维数组名 buffer 和 char* buffer 之间必有一个强制类型转换，而 buffer[0] 与 char* buffer 则是兼容类型。[/quote] 真不可思议： buffer==&buffer==&buffer[0] == buffer[0] == &buffer[0][0]真的相等，为什么？buffer==&buffer 和 buffer[0]==&buffer[0]就明显不一样吧，明显取了地址的，为什么？

user: user-11

user: user-10
content: 引用 22 楼 rongxiaojun 的回复:[quote=引用 17 楼 dcw0402 的回复:] 这里,二维数组其实是一个一维数组的加长版.... 改成这样也可以 void output(char *buffer){ 	printf("%s\n",buffer); } 我也不知道为啥,感觉7楼说有的些道理  这是因为char buffer[100][1024]中，buffer的值即char[0][0]的地址，（真不可思议，二维数组地址即第一个数组中第一个元素地址，而非第一个数组的地址），然后调用函数output(buffer)，此时buffer的值传进参数中，参数类型char*，则参数为char* 类型且指向了首字符'i',然后printf打印字符串直到'\0'出现，一切都解释通了。[/quote] 错！准确的讲，二维数组名的值是指向第一个数组的指针值，buffer == &buffer[0]。 二维数组在概念上是数组的数组，第一个数组 buffer[0] 的值才是指向 buffer[0][0] 的指针值。这些概念是实在的，因此 buffer+1 和 buffer[0]+1 都有各自确切的意义。技术上，这几个地址值是相等的，buffer == &buffer[0] == buffer[0] == &buffer[0][0]。但是 buffer 和 buffer[0] 类型是不同的。二维数组名 buffer 和 char* buffer 之间必有一个强制类型转换，而 buffer[0] 与 char* buffer 则是兼容类型。

user: user-0
content: 引用 17 楼 dcw0402 的回复:这里,二维数组其实是一个一维数组的加长版.... 改成这样也可以 void output(char *buffer){ 	printf("%s\n",buffer); } 我也不知道为啥,感觉7楼说有的些道理  这是因为char buffer[100][1024]中，buffer的值即char[0][0]的地址，（真不可思议，二维数组地址即第一个数组中第一个元素地址，而非第一个数组的地址），然后调用函数output(buffer)，此时buffer的值传进参数中，参数类型char*，则参数为char* 类型且指向了首字符'i',然后printf打印字符串直到'\0'出现，一切都解释通了。

user: user-0
content: 引用 11 楼 skyliuxu 的回复:[quote=引用 楼主 rongxiaojun 的回复:] 长话短说： void output（char **buffer）{    printf（“%s\n”,buffer[0]）; }  void main(){    char buffer[100][1024] = {0};    int len = sprintf(buffer[0],"i am a bastard...");    buffer[0][len] = '\0';    output(buffer); }  程序出现“段错误” 求解答！ 是linux下gcc编译系统吗？在vs下错误信息就是write access violation at address 0xblablabla(大概是这么个错误提示吧，记不清了)。 为什么会出现这样的错误呢？这就值得好好探讨了。 首先，指针和数组是根本不同的两个东西！尽管它们有着千丝万缕的联系！ 第二，要理解为什么出现上面这样的错误，需要理解两个最基本的东西： （1）数组名称出现在表达式中时（除用于sizeof外），会被看成一个指向该数组第一个元素的指针。注意，本质上讲，C中所有数组都是一维数组，所以第一个元素是什么是非常明确的。比如，int a[10]的第一个元素是一个整型数，而int a[10][10]的第一个元素是一个包含10个整型数的数组。 （2）指针可以与整型数进行加减法，结果是新的指针，而加减的单位是指针所指向元素的大小。比如，int *p, p = a;(注意体会（1）在这儿的作用)，则p + 1指向的是a[1],p+4指向的是a[4]. 下面可以看一下为什么出现上面的错误了。 char buffer[100][1024]表示buffer是一个二维数组，output(buffer)中的buffer会被看做指向该二维数组第一个元素的指针，即被看作指向一个包含1024个char的数组的指针，char (*)[1024]是这个指针的类型。而在output的定义中，其期望的参数类型是char **，所以编译结果会有warning。当然，你依然可以运行，因为存在强制类型转换。这也是为什么4楼做法没用的原因，你显式的进行类型转换除了消除了warning外（机器是很好糊弄的），基本没有什么意义。接着分析出现错误的原因，output函数的参数声明是char **，即二级指针（pointer to pointer）。注意，强制类型转换不会改变指针的值。这样，buffer的值与main中定义的buffer的值是一样，不过output里的buffer是二级指针。那么buffer[0]是什么，buffer是二级指针，buffer[0]当然就是一级指针！buffer[0]等于多少？buffer[0]实际上是*（buffer+0），前面说过，指针是可以与整数进行加减法的（这是array index的基础），而加减的单位是该指针指向的元素的大小，buffer指向的是什么元素？char*!所以*(buffer+0)的值是0x6d612069（i am所占的四个字节的值对应的地址，注意little endian），因此，出现write access violation就不足为奇了。 大概就是这么个意思，有一些地方的表述是不够准确的，欢迎进一步探讨。[/quote] 说的太好了，几乎完全消化了  我总结一下（我系统是64位）： GDB显示的结果如下： buffer=0x7ffffffe5180 buffer[0][0]=0x7ffffffe5180 output (buffer=0x7ffffffe5480) at b.c:22 如上三条说明了两个问题： 1.buffer其实是buffer[100][1024]中首元素buffer[0][0]的地址，即‘i’的地址 2.强制类型转换确实不会改变指针的值，尽管函数外buffer是char(*)[1024]类型、函数参数char**类型，外buffer还是将buffer[0][0]地址传给了参数char**。 此时问题来了，函数内buffer为二级指针，buffer[0]类型char*，为指针，64位系统指针8字节，则buffer[0]指针的值为buffer所指内存单元向下共8个内存单元内容组成的值。是不是很绕，这8个内存单元内容是什么呢，此时函数内buffer值就是函数外buffer[0][0]即字符'i'的地址，则从‘i’开始依次取八个字节得到的是'i am a b'，将其转化为数值即为0x6d612069，故buffer[0]的值为0x6d612069，printf（“%s\n”,buffer[0]）,故发生地址越界。

user: user-0
content: 引用 19 楼 lwouyang 的回复:编译时编译器没抱怨吗？如果是，你可以把编译器扔了。 如果有报怨的话，你没看就轻易放过了？ 得饶人处且饶人，我看不是大错只是警告，想放它条生路

user: user-10
content: 编译时编译器没抱怨吗？如果是，你可以把编译器扔了。 如果有报怨的话，你没看就轻易放过了？

user: user-12
content: char**p是一个指向char*的指针，*p或p[0]必须是一个地址。你的实参不满足这要求。