C语言字符串数组与字符串指针详解

来源:互联网 发布:预产期的算法 编辑:程序博客网 时间:2024/06/11 04:40

字符串数组与字符串指针在使用上有很多相似的地方,导致对两者的理解容易混淆.下面我们将从汇编的角度来详细审视一下两者的区别.

先看下面一段代码:

/*file name: test.c*/#include<stdio.h>char *globle_pointer="aaaaaaaaa";char globle_buf[]="bbbbbbbb";void main(){    char *local_pointer="cccccccc";    char local_buf[]="dddddddddd";    *(globle_pointer+2)='A';    globle_buf[2]='B';    *(local_pointer+2)='C';    local_buf[2]='D';    printf("globle_pointer:%s\n",globle_pointer);    printf("globle_buf:%s\n",globle_buf);    printf("local_pointer:%s\n",local_pointer);    printf("local_buf:%s\n",local_buf);}

这个程序可以通过编译,但是一旦运行就会报出”段错误 (核心已转储)”.原因在于第8和第10行.紧跟在字符串指针后的字符串是只读数据,不允许修改.所以第8行和第10行企图修改只读数据段的操作当然受到禁止了.我们可以在汇编代码(片段)中清楚看到这一点:

        .file   "test.c"        .globl  globle_pointer        .section        .rodata.LC0:        .string "aaaaaaaaa"        .data        .align 4        .type   globle_pointer, @object        .size   globle_pointer, 4globle_pointer:        .long   .LC0        .globl  globle_buf        .align 4        .type   globle_buf, @object        .size   globle_buf, 9globle_buf:        .string "bbbbbbbb"        .section        .rodata.LC1:        .string "cccccccc".LC2:        .string "globle_pointer:%s\n".LC3:        .string "globle_buf:%s\n".LC4:        .string "local_pointer:%s\n".LC5:        .string "local_buf:%s\n"        .text        .globl  main        .type   main, @functionmain:

由于.section .rodata的修饰,.string “aaaaaaaaa”.string “cccccccc”变成只读数据段,而.string “bbbbbbbb”globle_pointer 受到.data修饰,恢复为可读写属性.所以globle_pointer作为全局变量可以修改它的值.这解释了字符串”aaaaaa”,”bbbbbbb”和”ccccccc”的读写性质,等等,”dddddd”在哪儿呢?通过读后续代码,我们可以看到”ddddd”是被代码写入了main函数的栈空间中,而栈是可读写的,所以这也解释了local_buf的读写属性:

        movl    %esp, %ebp        pushl   %ecx        .cfi_escape 0xf,0x3,0x75,0x7c,0x6        subl    $36, %esp        movl    %gs:20, %eax        movl    %eax, -12(%ebp)        xorl    %eax, %eax        movl    $.LC1, -28(%ebp)        movl    $1684300900, -23(%ebp)        movl    $1684300900, -19(%ebp)        movw    $25700, -15(%ebp)

一共10个’d’,正好在最后三行汇编代码中得到体现.说了这么多,可以看到所谓读写属性本质是由编译器的.section .rodata控制的.为了证明这一点,我们可以尝试突破这个限制:

我们在终端依次输入下面的命令:

$gcc -m32 -c test.c$objcopy --rename-section .rodata=.data test.o out.o$gcc -m32 out.o -o out$./out

我们逐行解释:第一行将test.c编译成test.o,不进行链接.第二行将test.o的只读数据段修改成读写数据段,输出为out.o,第三行链接out.o为可执行程序out,第四行执行这个文件.这时程序可以正常运行:

综上,程序示例中的字符串指针后的字符串是只读的,而字符串数组后的字符串是可读写的.

0 0
原创粉丝点击