本文已参与「新人创作礼」活动,一起开启掘金创作之路。
@[toc]
请在Debug模式下进行文章涉及的操作,以避免Release行为优化了部分细节。
一、产生
针对如下代码,先提出一个问题,g_str 和 g_str1有何异同?LINE 1 和 LINE 2执行情况如何?
// 代码`1-1`
#include <iostream>
char g_str[] = "hello";
char *g_str1 = (char*)"world";
int main()
{
g_str[0] = '1'; // LINE 1
g_str1[0] = '1'; // LINE 2
}
二、分析
先回答一下提出的第一个问题:
首先
g_str和g_str1都是全局变量,并且都存储在静态存储区。但是两者在本质上是有区别的。
- 在声明上,
g_str是一个长度为6char类型的数组,g_str1是一个char类型的指针。
首先,如果仅仅是执行
read操作的话,两者是都可以正常执行的,比如下边这两行代码// 代码`2-1` printf("%s",g_str); printf("%s",g_str1);但是如果你尝试对两者进行修改操作时,结果会怎样?也就是代码1-1中的操作。依次执行,修改
g_str[0]正常执行,修改g_str1[0]则会报错写入访问权限冲突,报错情况如下所示:这个问题解释起来很简单,由于汇编过程中将不同的代码放入了不同的地方,而这些地方的权限也不尽相同,以上操作导致了一个不可被
write的数据发生了write行为,所以编译器报错了。这也就引出了第二个不同点。 但是,上述问题在Release模式下就不会产生。有待研究
- 访问权限不同,一个是==常量全局变量==,一个是==非常量全局变量==。
可以看到在声明
g_str1的时候进行了强制转换的操作,不强制转换的话,编译器会报错,告诉你不能把一个const char *的用来==初始化==一个char*的实体。这里往往最容易被忽略的一点就是
char *g_Str1 = (char*)"world";这行代码中的"world",其实是一个const char *。也就是你在声明并定义g_Str1的时候,其实是把一个const区域的指针强转给了g_str1。因此在修改g_str[0]的时候会出现访问权限拒绝,因为g_str1本身就只指向了一个const的内存段。
三、结合编译文件查看一下上述问题产生的原因
使用Dumpbin工具查看一下代码2-1的.obj文件。
从这里开始所提到的段落都是.obj文件中的
SECTION HEADER,如SECTION HEADER #5即指段落5。
- 首先看一下符号表中的相关项:
看到这个,我纳闷了~
明明两种声明编译后的结果一样啊,并且都还在同一个数据段(SECT4),对外类型也是默认的
External,本质上都是char *。
符号表展示出来的结果确实如此,那接着看,看数据段上是怎么说明的。
数据段是包含一个数据头和段落数据,一个obj文件中有多个数据段,每一个数据段都有自己的特性,包括权限、物理地址、数据大小、重定为,行号等,如下为
SECTION HEADER #4的段落头:基本上,从段落头我们就能看到当前数据段的一些大致信息,比如当前段落是.data段(通常包含已初始化的数据)。由于我们对
g_str和g_str1均进行了初始化,因此,在该段中,可以看到这两个变量。如下图所示,是段落#4的数据段和冲定向段。在符号表中我们已经看到了,
g_str和g_str1都是保存在段落4中的。在上图中得以验证,可以看到在==RAW DATA==中保存了g_str的初始值,在==RELOCATIONS==中保存了g_str1的初始值,并且加了其他的描述符在其前后,同时,还是以string类型进行编译的。
- RAW DATA :段落数据。每个段落真正数据保存的位置
- RELOCATIONS: 重定向段。描述文件中符号的重定向信息。
结合以上所看到的段落信息,说明一下:
由此,我们就可以看到,
g_str的数据是保存在.data段的数据段(==RAW DATA==)的,而.data段的权限是Read Write,这也就说明,g_str是可以被.text(通常包含可执行代码)段操作的,准确说是当前文件的.text段。 而g_str1是以重定向的方式被保存在数据段4中。保存的名字是??_C@_05MCBCHHEJ@world@,那我们就在obj文件中查找一个这个值真正保存的位置在哪。 从符号表查找??_C@_05MCBCHHEJ@world@,显示如下:可以看到其保存在段落5中。回到.obj文件中查看段落5,截图如下:
看到段落5之后是不是有一种,守得云开见月明的感觉。数据段保存的是
g_str1的源数据world,而数据段的访问权限是Read only。
因此,在调用g_strq[0] = '1'的时候,发生了写入访问权限冲突的异常。