请在Debug模式下进行文章涉及的操作,以避免Release行为优化了部分细节。
一、产生
针对如下代码,先提出一个问题,g_str 和 g_str1 有何异同?LINE 1 和 LINE 2 执行情况如何?
#include <iostream>
char g_str[] = "hello";
char *g_str1 = (char*)"world";
int main()
{
g_str[0] = '1';
g_str1[0] = '1';
}
二、分析
先回答一下提出的第一个问题:
首先g_str 和g_str1 都是全局变量,并且都存储在静态存储区。并且可以确定的是,两者都有自己的地址段。
但是两者在本质上是有区别的。
- 在声明上,g_str是一个长度为6char类型的数组,g_str1是一个char类型的指针。
首先,如果仅仅是执行read 操作的话,两者是都可以正常执行的,比如下边这两行代码
pritnf("%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 本身就只指向了一个cosnt 的内存段。
三、结合编译文件查看一下上述问题产生的原因
使用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' 的时候,发生了写入访问权限冲突的异常。
|