为什么我会对字符串常量这么感兴趣,源于下面这个代码:
//blue.c
#include<stdio.h>
void main()
{
void copy_string(char from[],char to[]);
char *a="I am a teacher.";
char *b="You are a student.";
printf("string a=%s
string b=%s
",a,b);
printf("copy string a to string b:
");
copy_string(a,b);
printf("
string a=%s
string b=%s
",a,b);
}
void copy_string(char from[],char to[])
{
int i=0;
while(from[i]!=' ')
{
to[i]=from[i];
i++;
to[i]=' ';
}
}
这段代码能够编译通过,但在运行时却出现错误。再回过头来认真揣摩下这个代码,也没有发现逻辑上的错误。当你百思不得其解的时候,我们不妨把问题的焦点集中在这两行代码: char *a="I am a teacher.";char *b="You are a student.";这两行代码都是把字符串常量的首地址赋值给指针变量,而在copy_string函数中试图引用指针变量改写字符串常量的值,问题就出在a和b指针所指向的内容是只读不能写的,你可以print,但你不能input.当把程序加载进内存时,所有的字符串常量(用两个""括号括起来的内容)都放进.rodata(read only data)段,.rodata段和代码段都是只读不能写的内存段,所以上面的blue.c程序才会在运行时出现错误.
当然了,蓝月我会在这里提出改正的方法,呵呵。很简单,把char *a="I am a teacher.";char *b="You are a student.";改为:char a[]="I am a teacher.";char b[]="You are a student.";再重新编译连接,就可以正常运行了。
这时候你或者会提出另一个问题,char a[]="I am a teacher.";这行代码不是也把字符串的首地址赋值给数组变量a吗,这和指针变量有什么不同。为了方便说明问题,现在引出下面两个简单的程序:
//hello1.c
#include<stdio.h>
int main()
{
char *a="hello world!";
printf("%s
",a);
return 0;
}
//hello2.c
#include<stdio.h>
int main()
{
char a[]="hello world!";
printf("%s
",a);
return 0;
}
上面两个程序都是正确的,不同的地方只有一处。hello1.c用了指针变量,hello2.c用了数组变量。但当把它们分别反汇编,查看其生成的汇编代码,就会发现相对于hello1.c程序hello2.c程序生成的汇编代码会长一些。原因是hello1.c程序是直接把存放在.rodata段的字符串常量的首地址赋值给指针变量,而hello2.c程序是先把.rodata段的字符串常量全部复制到堆栈(堆栈是当程序加载进内存时预留的内存空间),再把堆栈中的字符串常量的首地址赋值给数组变量。
现在应该一切都明朗了。再回到上面的blue.c程序,copy_string函数之所以能通过数组变量a和b而改写字符串常量是因为它改写的是预先复制进堆栈的字符串常量而不是去改写存放在.rodata段的内存常量
由于本人能力有限,以上所说如有差错请留言指正
下面给出hello1.c和hello2.c在linux下的反汇编代码:
hello1.c的如下:
.file "hello1.c"
.section .rodata
.LC0:
.string "hello world!"
.LC1:
.string "%s
"
.text
.globl main
.tybe main,@function
main:
pushl %ebp
movl %esp,%ebp
subl $8,%esp
andl $-16,%esp
movl $0,%eax
subl %eax,%esp
movl $.LC0,-4(%ebp)
subl $8,%esp
pushl -4(%ebp)
pushl $.LC1
call printf
addl $16,%esp
movl $0,%eax
leave
ret
hello2.c的如下:
.file "hello2.c"
.section .rodata
.LC0:
.string "hello world!"
.LC1:
.string "%s
"
.text
.globl main
.tybe main,@function
main:
pushl %ebp
movl %esp,%ebp
pushl %edi
pushl %esi
subl $16,%esp
andl $-16,%esp
movl $0,%eax
subl %eax,%esp
leal -24(%ebp),%edi
movl $LC0,%esi
cld
movl $13,%ecx
rep
movsb
subl $8,%esp
leal -24(%ebp),%eax
pushl %eax
pushl $.LC1
call printf
addl $16,%esp
movl $0,%eax
leal -8(%ebp),%esp
popl %esi
popl %edi
leave
ret