在学习C、C++的过程中,或多或少多接触过:字符串内容不能修改。一般告知的原因是字符串存放在字符常量区,字符常量区的内容不能修改。
细心的同学会发现,如果一个字符串数组中存放的数据也是放在字符常量区的,为什么数组的内容可以修改?
基于这个问题,写出了如下测试代码。
#include <iostream>
using namespace std;
int main()
{
char *p = "hello world";
char str[] = "doanld";
str[0] = 'D';
p[0] = 'H'; //运行出错,会提示 bus error
printf("p[0] = %c, *p = %s, str = %s\n", p[0], p, str);
}
有了上面的代码,编译得到的文件使用ida反编译,可以看到汇编代码,其中的
"hello world"保存在 aHelloWorld中,"doanld"保存在cs:aDoanld中,
定位后发现 aHelloWorld和cs:aDoanld在__cstring区块中,同时文中用到的print字段也在其中。
通过此就可以看出,所有的字符串都存放在__cstring section中。即,代码中定义的C字符串存放在__cstring节中。该字段的字符不能修改。
因为char *p = "hello world";
中 p
指向aHelloWorld
,所以不能修改,从汇编层面也能看出,对应的汇编代码为lea rax, aHelloWorld ;
中用的lea
。
为什么数组中可以修改字符串的值?实际,数组中是存放了一份字符串的拷贝(存放在栈上),修改的是栈上的拷贝,从汇编春光也能看出。
对应的汇编代码为:
mov [rbp+var_8], rax
mov edx, dword ptr cs:aDoanld ; "doanld"
mov [rbp+var_F], edx
mov si, word ptr cs:aDoanld+4 ; "ld"
其他
-
上面代码,在编译的时候,在
char *p = "hello world"
处会有“conversion from string literal to 'char *' is deprecated”
警告,该警告的解决办法是,加上const,即:const char *p = "hello world";
通过这种方式来理解p所指向的内容不能修改就更加容易了。 -
lea
指令
load effective address, 加载有效地址,可以将有效地址传送到指定的的寄存器。指令形式是从存储器读数据到寄存器, 效果是将存储器的有效地址写入到目的操作数, 简单说, 就是C语言中的”&”. -
mov
指令
在CPU内或CPU和存储器之间传送字或字节。- lea 和 mov使用区别
- lea对变量没有影响是取地址
- mov对变量来说没有影响是取值
- lea 和 mov使用区别