设为首页 加入收藏

TOP

C语言变长数组之剖析(二)
2014-11-24 03:14:26 来源: 作者: 【 】 浏览:3
Tags:语言 剖析
) next



7 char arr[n+1];


(gdb) next



8 bzero(arr, (n+1) * sizeof(char));


(gdb) print/x arr



$2 = {0xb0, 0xe5}


(gdb) ptype arr



type = char [2]


(gdb) print &arr



$3 = (char (*)[2]) 0xbfffe1c8


这里,当程序执行流通过了为变长数组分配空间的第7行之后,用print/x命令打印出arr的值,结果居然是两个字节;而如果尝试用ptype打印出arr的类型,得到的结果居然是arr是一个长度为2的字符数组。很明显,在本例中,因为提供给main()函数的参数argv[1]是6,因此按常理可知arr应该是一个长度为7的字符数组,但很遗憾,gdb给出的却并不是这样的结果。用print &arr打印出arr的地址为0xbfffe1c8。继续上面的调试过程:


(gdb) x/4x &arr



0xbfffe5c8: 0xbfffe5b0 0xbfffe5c0 0x00000006 0x40015360


(gdb) x/8x $esp



0xbfffe5b0: 0xbffffad8 0x42130a14 0xbfffe5c8 0x0804828d


0xbfffe5c0: 0x42130a14 0x4000c660 0xbfffe5b0 0xbfffe5c0


可以看到,在&arr(即地址0xbfffe5c8)处的第一个32位值是0xbfffe5b0,而通过x/8x $esp可以发现,栈顶指针esp恰好就指向的是0xbfffe5b0这个位置。于是,可以猜想,如果arr是一个指针的话,那么它指向的就恰好是当前栈顶的指针。继续上面的调试:


(gdb) next



9 for (i = 0; i < n; i++) {


(gdb) next



10 arr[i] = (char)('A' + i);


(gdb) next



9 for (i = 0; i < n; i++) {


(gdb) until



12 arr[n] = '';


(gdb) next



13 printf("%sn", arr);


(gdb) x/8x $esp



0xbfffe5b0: 0x44434241 0x42004645 0xbfffe5c8 0x0804828d


0xbfffe5c0: 0x42130a14 0x4000c660 0xbfffe5b0 0xbfffe5c0


注意上面表示为蓝色的部分,由于Intel平台采用的是小端字节序,因此蓝色的部分实际上就是’ABCDEF’的十六进制表示。而红色的32位字则暗示着arr就是指向栈顶的指针。为了确认我们的这一想法,下面通过修改arr的值来观察程序的执行情况(需要注意的是:每一次运行时堆栈的地址是变化的):


(gdb) run



The program being debugged has been started already.


Start it from the beginning (y or n) y


Starting program: /root/source/test/dynarray 6




Breakpoint 1, main (argc=2, argv=0xbfffde24) at dynarray.c:6


6 n = atoi(argv[1]);


(gdb) next



7 char arr[n+1];


(gdb) next



8 bzero(arr, (n+1) * sizeof(char));


(gdb) print/x &arr



$3 = 0xbfffddc8


(gdb) x/8x $esp



0xbfffddb0: 0xbffffad8 0x42130a14 0xbfffddc8 0x0804828d


0xbfffddc0: 0x42130a14 0x4000c660 0xbfffddb0 0xbfffddc0


(gdb) set *(unsigned int*)&arr=0xbfffddc0



(gdb) x/8x $esp



0xbfffddb0: 0xbffffad8 0x42130a14 0xbfffddc8 0x0804828d


0xbfffddc0: 0x42130a14 0x4000c660 0xbfffddc0 0xbfffddc0


(gdb) next



9 for (i = 0; i < n; i++) {


(gdb) next



10 arr[i] = (char)('A' + i);


(gdb) next



9 for (i = 0; i < n; i++) {


(gdb) until



12 arr[n] = '';


(gdb) next



13 printf("%sn", arr);


(gdb) x/8x $esp



0xbfffddb0: 0xbffffad8 0x42130a14 0xbfffddc8 0x0804828d


0xbfffddc0: 0x44434241 0x40004645 0xbfffddc0 0xbfffddc0


地址0xbfffddc8(也就是arr的地址)处的值本来为0xbfffddb0,我们把它改成了0xbfffddc0,于是,当程序运行到向变长数组输入数据完成之后,我们发现这次修改的地址的确是从0xbfffddc0开始的。这就表明arr的确像我们通常所理解的一样,数组名即指针。只不过这个指针指向的位置在它的下方(堆栈向下生长),而不是像大多数时候一样指向上方的某个位置。


4、分析
上面的测试结果表明:变长数组的确是在栈空间中分配的;变长数组的数组名实际上就是一个地址指针,指向数组所在的栈顶位置;而GDB无法判断出变长数组的数组名实际上是一个地址指针。


GDB为什么无法准确判断出变长数组的类型的原因尚不清楚,但是作者猜测这和变长数组的动态特性有关,由于变长数组是在程序动态执行的过程生成的,GDB无法向对待常规数组一样从目标文件包含的.stabs节中获得长度信息,于是给出了错误的类型信息。


另外,作者对变长数组的作用域进行了测试,测试代码根据上例修改得到,如下所示:


1 int n;


2 char arr[n+1];


3


4 int


5 main(int argc, char *argv[])


6 {


7 int i;


8


9 n = atoi(argv[1]);


10 bzero(arr, (n+1) * sizeof(char));


11 for (i = 0; i < n; i++) {


12 arr[i] = (char)('A' + i);


13 }


14 arr[n] = '';


15 printf("%sn", arr);


16


17 return (0);


18 }


当如下编译的时候,gcc会提示出错:


[root@cyc test]# gcc -g dynarray.c



dynarray.c:2: variable-size type declared outside of any function


可见gcc不允许在文件域定义变长数组。


对于gcc中的变长数组能否用static修饰则使用如下代码进行测试:


1 int


2 main(int argc, char *argv[])


3 {


4 int i, n;


5


6 n = atoi(argv[1]);


7 static char arr[n+1];


8 bzero(arr, (n+1) * sizeof(char));


9 for (i = 0; i < n; i++) {


10 arr[i] = (char)('A' + i);


11 }


12 arr[n] = '';


13 printf("%sn", arr);


14


15 return (0);


16 }


当编译此源文件的时候,gcc给出如下错误提示:


[root@cyc test]# gcc -g dynarray.c



dynarray.c: In function `main':


dynarray.c:7: storage size of `arr' isn't consta

首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇GNU C中的数组类型 下一篇Linux内核基础--事件通知链(notif..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

·哈希表 - 菜鸟教程 (2025-12-24 20:18:55)
·MySQL存储引擎InnoDB (2025-12-24 20:18:53)
·索引堆及其优化 - 菜 (2025-12-24 20:18:50)
·Shell 中各种括号的 (2025-12-24 19:50:39)
·Shell 变量 - 菜鸟教 (2025-12-24 19:50:37)