各种数据类型的变量如何存储在C++(本机)二进制文件中

How are variables of various data types stored in C++(native) binary?

本文关键字:C++ 本机 二进制文件 存储 变量 何存储 数据类型      更新时间:2023-10-16

一周前,我和C++一起开始了我的冒险。我读了很多关于C++。我正在尝试以下内容:

 char * String1 = "abcdefgh";

然后,我尝试通过以下方式修改其值:

 String1[2] = 'f';

这导致了未处理的异常。但以下结果会导致正确执行:

 char String2[9]="abcdefgh";
 String2[7]='s';

我尝试使用 DUMPBIN 提取有关使用上述代码生成的二进制文件的信息。DUMPBIN是一个Visual Studio工具。我使用/ALL 选项提取二进制文件中包含的所有信息。

我可以在 RAWDATA 部分看到两个"abcdefgh"实例。我明白为什么。

我的问题如下:

1) 虽然 String1 和 String2 本质上都是指向同一字符序列的两个不同实例的指针,但为什么 String1 操作不是合法的?

2)我知道编译器会生成一个符号表来映射变量名称及其值。有没有工具可以在Windows操作系统中可视化符号表?

3)如果我有一个整数数组而不是字符序列,可以在RAWDATA中找到它吗?

我还可以在 RAWDATA 中看到以下内容:

Unknown Runtime Check Error.........
 Stack memory around _alloca was corrupted.......
  ....A local variable was used before it was initialized.........
 ....Stack memory was corrupted..
  ........A cast to a smaller data type has caused a loss of data.
  If this was intentional, you should mask the source of the cast with the appropriate bitmask.

这些东西是如何进入二进制可执行文件的?将这些消息放在二进制文件中的目的是什么(显然是不可读的)?

编辑:我的问题 1) 有一个词 实例,用于表示以下内容:

字符序列"abcdefgh"派生自一组非大写的英文字母,即{a,b,...,y,z}。这个序列被实例化两次,并存储在两个内存位置,比如A和B.String1指向A(假设),String2指向B。这个问题中没有概念上的混淆。

我想理解的是内存位置 A 和 B 的属性差异,即为什么其中一个是不可变的。

注意:下面的所有代码都引用了函数中的一个作用域。

下面的代码初始化一个可写缓冲区string2数据。编译器生成初始化代码,以从只读编译器生成的字符串复制到此缓冲区。

char string2[] = "abcdefgh";

下面的代码在 string1存储指向编译器生成的只读字符串的指针。字符串的内容位于可执行映像的只读部分中。这就是为什么修改它会失败的原因。

char * string1 = "abcdefgh";

可以通过string1指向可写缓冲区来使其工作。这可以通过复制字符串来实现:

char * string1 = strdup("abcdefgh");
....
free(string1); // don't forget to free the buffer!

char * String1 = "abcdefgh";
在 C(和 C++)中是 const,编译器可以随心所欲地存储固定的 const 数据,它可能有一个单独的 DATA 段,它可能具有完全的 const 程序存储(在哈佛架构中)

char String2[9]="abcdefgh"; 分配一个 9 个元素的字符数组,恰好用一些字符串初始化它。您可以对阵列执行所需的操作。任何其他类型的数组都将以相同的方式存储。

某些运行时错误的错误消息存储在程序数据段中(与原始 char* 字符串相同)。其中一些像"这个程序需要Windows"显然必须在那里而不是在操作系统中,因为DOS不知道一个程序需要更高版本的Windows。 但我不确定为什么这些特定的运行时错误不是由 OS

char* s1 = "a litteral";

,编译器确实应该生成警告。 隐含的此处不推荐转换为非 CONST,并且仅引入到避免破坏现有代码的语言(从某个时代开始C 没有const )。

在这种情况下:

char s2[] = "init";

,实际上没有字符串文字。 "字符串文字"实际上是一个初始化规范与字符串文本不同,不会出现内存中的任何位置;编译器使用它来确定如何s2应该初始化,并且完全等效于:

char s2[] = { 'i', 'n', 'i', 't', '' };

(写起来更方便一些。

--简短的历史侧面:早期的C没有const . 的类型字符串文字是char[]的,修改它是合法的。 这个线索到一些非常可怕的代码:

char* f() { return "abcd"; }
/* ... */
f()[1] = 'x';

下次你打电话给f,它返回"axcd"。 一窝没有源列表中显示的值是不是可读代码的方式,C 标准委员会决定这是一个最好不要保留的功能。

char string[] = "foo"

这将分配一个 char 数组,并使用值 {'f', 'o', 'o', '\0'} 初始化它。您可以获得字符的"自己的"存储,并且可以修改数组。

char strptr* = "foo"

这将分配一个指针,并将该指针的值设置为包含 {'f', 'o', 'o', '\0'} 的 char 数组的地址。指针是你随心所欲的,但 char 数组不是。事实上,数组的类型不是char[],而是const char[]strptr确实应该声明为 const char*,这样你就不会错误地尝试修改 const 数组。

在第一种情况下,"foo" 是数组初始值设定项。在第二个中,"foo" 是字符串文本。

关于每种情况下的内存确切位置的更具体细节往往未由标准指定。但是,一般来说,char string[] = "foo"在堆栈上分配一个char数组,char strptr* = "foo"堆栈上分配一个char指针,并(静态地)在可执行文件的数据部分中分配一个const char数组。

1)

如 c++ 标准 (2003) (http://www.iso.org/iso/catalogue_detail.htm?csnumber=38110) 中所指出的

1 字符串文本是用
双引号,可选以字母 L 开头,如"..." 或 L"..."。 不以 L 开头的字符串文本是一个 普通字符串文本,也称为窄字符串 字面。 普通字符串文本的类型为"n 个常量
数组" char"和静态存储持续时间 (basic.stc),其中 n 是大小 的字符串如下定义,并使用给定的 字符。 以 L 开头的字符串文本(如 L"asdf")是 宽字符串文本。 宽字符串文本的类型为"n 的数组" const wchar_t",并具有静态存储持续时间,其中 n 的大小为 下面定义的字符串,并使用给定的字符初始化- 之三。

2 是否所有字符串文本都是不同的(即存储 在非重叠对象中)是实现定义的。 这 尝试修改字符串文本的效果未定义。

如上所述,这不是非法的,是未定义的行为,因此,使用 VS,您会在 Windows 上出现异常,使用 g++,您将在 Linux 中出现分段错误(基本上它们看起来很相似)

2)您可以使用反汇编程序并检查exe文件的数据部分(查看此wiki以获取有关几个exe文件结构x86反汇编/Windows可执行文件的更多信息)

3)是的,它应该在exe文件的.data部分