LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
楼主: nait

二维字符数组和一维字符指针数组有很大的不同吗?

[复制链接]
发表于 2005-3-24 08:25:19 | 显示全部楼层
主要是类型就搞错了!!
从编译器的角度看,类型就单位类型所占的字节数。
单位char *a[100] 占四个字节。
而单位char b[100][100] 占100个字节  。(注意我们使用的内存是线性的所以没有2维,都是一维的)

如果有function(char *a[])
你调用function(b),首先进行类型转换 使的a=(char ×)b
那么 a[0]就是 以b开头的4个字节的内容!

比如
void f(int *a)
{}
int main(void)
{
        short int n1 = 7, n2 = 8;
        short int a[][1] = { 7, 8 };
        f(a);

        return 0;
}

这次就发生了明显的类型转换.
a[0]是8,7的2进制合并来的一个数字1000 0000 0000 0111
而×a[0]则是访问这个地址!

关键在于你对类型的。。。。。
回复 支持 反对

使用道具 举报

发表于 2005-3-24 20:11:46 | 显示全部楼层
char * a[4][2] = {'a','b'.......};

(int)a = (int)*a = 申请的内存块起址(即内存块中第一个字符a的地址)

char **pa = a; //Error ,因为 如果把'a'的内存地址强制赋给 **pa,则 *pa 原则上表示一个字符的地址,这里却得到'a' --- 97,那么 **pa,则试图取内存位置97上的值,所以段错误。
回复 支持 反对

使用道具 举报

发表于 2005-3-24 20:19:29 | 显示全部楼层
Post by herberteuler
这里的段错误不是由于下标越界,而是由于楼主使用了错误的实参。

呵呵,你们厉害,我还没有看出来呢!
回复 支持 反对

使用道具 举报

发表于 2005-3-24 20:20:00 | 显示全部楼层


  1. #include <stdio.h>

  2. int main()
  3. {
  4.         /*
  5.          * 1.s[4][2]的含义
  6.          * 2. ** p = a[M][N]是不能完成自动转转的
  7.          * 3.类型的含义
  8.          * */
  9.         int s[4][2] = {{1,2},{13,4},{5,6},{7,8}};
  10.         printf("s1=%d\n",(s+1)[1][1]);
  11.         int ** p = s;
  12.         /*printf("p+1=%d\n",(p+1)[1][1]);*/
  13.         printf("p+1=%d\n",(p+1)[1]);
  14.         printf("s=%x\n",s);
  15.         printf("p=%x\n",p);
  16.         printf("s+1=%x\n",s + 1);
  17.         return 0;

  18. }
复制代码
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-3-25 10:14:23 | 显示全部楼层
哈哈
终于看明白了
再谢各位大哥:):)
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-6-4 16:15:09 | 显示全部楼层
Post by 弥敦路九号
char * a[4][2] = {'a','b'.......};

(int)a = (int)*a = 申请的内存块起址(即内存块中第一个字符a的地址)

char **pa = a; //Error ,因为 如果把'a'的内存地址强制赋给 **pa,则 *pa 原则上表示一个字符的地址,这里却得到'a' --- 97,那么 **pa,则试图取内存位置97上的值,所以段错误。

又把这个帖看了一遍,真是温故而知新啊,确实又有了不同的感受

细细品位了一下,我觉得弥敦路九号兄的回帖似乎有点问题
按照nuclearweapon兄所提到的类型角度来说,*pa应该是char *类型的,一般32位机占4个字节,所以*pa应该不会只是97吧?

我做了个实验作为佐证,代码如下

  1. #include <stdio.h>

  2. int
  3. main(int argc, char **argv)
  4. {
  5.         char a[4][2] = {'a', 'b', 'c', 'd', 'e'};

  6.         char **pa = a;
  7.         printf("%p\n", *pa);

  8.         return 0;
  9. }
复制代码

编译时会有警告
x.c: In function `main':
x.c:8: warning: initialization from incompatible pointer type
也在预料之中
执行结果:
  1. 0x64636261
复制代码

和我的猜想吻合,把dcba的抓过来了   
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-6-4 16:46:12 | 显示全部楼层
Post by herberteuler
呵呵,又是指针和数组的区别。先看一段程序:
  1. int f(int *s[])
  2. {
  3.         return *s[0];
  4. }

  5. int main(void)
  6. {
  7.         int n1 = 7, n2 = 8;
  8.         int a[][1] = { 7, 8, };
  9.         int *b[] = { &n1, &n2, };

  10.         f(a);
  11.         f(b);

  12.         return 0;
  13. }

复制代码

再看它的汇编代码(为了适合阅读已经排版):
  1.         .file        "x.c"
  2.         .text
  3. .globl f
  4.         .type        f, @function
  5. f:
  6.         pushl        %ebp
  7.         movl        %esp, %ebp

  8.         movl        8(%ebp), %eax
  9.         movl        (%eax), %eax
  10.         movl        (%eax), %eax

  11.         popl        %ebp
  12.         ret
  13.         .size        f, .-f
  14. .globl main
  15.         .type        main, @function
  16. main:
  17.         pushl        %ebp
  18.         movl        %esp, %ebp
  19.         subl        $40, %esp
  20.         andl        $-16, %esp
  21.         movl        $0, %eax
  22.         subl        %eax, %esp

  23.         ; n1 = 7
  24.         movl        $7, -4(%ebp)

  25.         ; n2 = 8
  26.         movl        $8, -8(%ebp)

  27.         ; a[][1] = { 7, 8, }
  28.         movl        $7, -16(%ebp)
  29.         movl        $8, -12(%ebp)

  30.         ; b[] = { &n1, &n2, }
  31.         leal        -4(%ebp), %eax
  32.         movl        %eax, -24(%ebp)
  33.         leal        -8(%ebp), %eax
  34.         movl        %eax, -20(%ebp)

  35.         ; f(a)
  36.         leal        -16(%ebp), %eax
  37.         movl        %eax, (%esp)
  38.         call        f

  39.         ; f(b)
  40.         leal        -24(%ebp), %eax
  41.         movl        %eax, (%esp)
  42.         call        f

  43.         movl        $0, %eax
  44.         leave
  45.         ret
  46.         .size        main, .-main
  47.         .section        .note.GNU-stack,"",@progbits
  48.         .ident        "GCC: (GNU) 3.3.5  (Gentoo Linux 3.3.5-r1, ssp-3.3.2-3, pie-8.7.7.1)"
复制代码

可以看到,当用 a 做参数时,程序已经尝试访问(虚拟)地址为 7 的内存了。这当然会有段错误。

此外,全局的指针和数组不同,或者说,下面的声明和定义不是等价的:
  1. /* 第 1 个文件中 */
  2. int array[10];

  3. /* 另一个文件中 */
  4. extern int *array;
复制代码

原因也是类似的:请看一下编译器产生的汇编代码。


又仔细看了一下这段汇编,虽然不像以前一窍不通,但水平有限,仍有一些不明之处
我尝试做了一些注释,请大侠们看一下我的理解是否正确,顺便我也把我的问题在注释中提出来了
请大侠们不吝赐教,thx   
  1. .file        "x.c"
  2.         .text
  3. .globl f
  4.         .type        f, @function
  5. f:
  6.         pushl        %ebp
  7.         movl        %esp, %ebp

  8.         movl        8(%ebp), %eax          [color=Blue] #8(%ebp)是第一个参数在栈上的位置,把它的地址赋给%eax[/color]
  9.         movl        (%eax), %eax         [color=Blue]#第一次访问内存[/color]
  10.         movl        (%eax), %eax         [color=Blue]#第二次访存[/color]

  11.         popl        %ebp
  12.         ret
  13.         .size        f, .-f
  14. .globl main
  15.         .type        main, @function
  16. main:
  17.         pushl        %ebp
  18.         movl        %esp, %ebp
  19.         subl        $40, %esp            [color=Blue]#为main定义的auto变量预留40B的空间[/color]
  20.         andl        $-16, %esp        [color=Red]#这句就不怎么懂了,esp和-16相与,字面上是把esp的低4位清零,但这样做的目的是什么呢?[/color]
  21.         movl        $0, %eax           [color=Red]#接下来的这两步是什么意思?[/color]
  22.         subl        %eax, %esp

  23.         ; n1 = 7
  24.         movl        $7, -4(%ebp)   [color=Blue]#紧跟ebp的是第一个auto变量[/color]

  25.         ; n2 = 8
  26.         movl        $8, -8(%ebp)   [color=Blue]#第二个auto var[/color]

  27.         ; a[][1] = { 7, 8, }
  28.         movl        $7, -16(%ebp)    [color=Red]#想不通为什么要把7放在后面?[/color]
  29.         movl        $8, -12(%ebp)

  30.         ; b[] = { &n1, &n2, }
  31.         leal        -4(%ebp), %eax         [color=Blue]#%ebp - 4这个地址上是7,就是把7的地址赋给eax[/color]
  32.         movl        %eax, -24(%ebp)      [color=Red]#仍想不通为什么7的地址在后而8的在前[/color]
  33.         leal        -8(%ebp), %eax
  34.         movl        %eax, -20(%ebp)

  35.         ; f(a)
  36.         leal        -16(%ebp), %eax    [color=Blue]#这两步把7压栈了,所以在函数f中第一次访存movl        (%eax), %eax得到的是7,这样第二次访存就会出错[/color]
  37.         movl        %eax, (%esp)
  38.         call        f

  39.         ; f(b)
  40.         leal        -24(%ebp), %eax       [color=Blue]#同样的压栈操作,但压的是一个地址,故函数f中第一次访存movl        (%eax), %eax得到的是地址,不会出错[/color]
  41.         movl        %eax, (%esp)
  42.         call        f

  43.         movl        $0, %eax
  44.         leave
  45.         ret
  46.         .size        main, .-main
  47.         .section        .note.GNU-stack,"",@progbits
  48.         .ident        "GCC: (GNU) 3.3.5  (Gentoo Linux 3.3.5-r1, ssp-3.3.2-3, pie-8.7.7.1)"
复制代码
回复 支持 反对

使用道具 举报

发表于 2005-6-4 17:18:01 | 显示全部楼层
二维数组和一维指针数组完全是两码事,不兼容。这个问题问得太弱。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-6-4 17:38:55 | 显示全部楼层
现在当然知道,但当时不知道啊,呵呵

Tetris能否替我解答一下我提出的那段汇编代码的问题?
thx   
回复 支持 反对

使用道具 举报

发表于 2005-6-4 17:50:26 | 显示全部楼层

  1.         andl        $-16, %esp        #这句就不怎么懂了,esp和-16相与,字面上是把 sp的低4位清零,但这样做的目的是什么呢?
  2.         movl        $0, %eax           #接下来的这两步是什么意思?
  3.         subl        %eax, %esp
复制代码

应该是内存对齐吧。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表