0

一周前,我开始了我的 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的属性差异,即为什么其中一个是不可变的。

4

5 回答 5

5

注意:以下所有代码均指函数内的范围。

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

char string2[] = "abcdefgh";

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

char * string1 = "abcdefgh";

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

char * string1 = strdup("abcdefgh");
....
free(string1); // don't forget to free the buffer!
于 2012-06-08T12:52:27.773 回答
4

char * String1 = "abcdefgh";
在 C(和 C++)中是 const,允许编译器存储固定的 const 数据,但它喜欢,它可能有一个单独的 DATA 段,它可能有完全 const 程序存储(在哈佛架构中)

char String2[9]="abcdefgh"; 分配一个 9 元素的字符数组,并恰好用一些字符串对其进行初始化。你可以用数组做你想做的事。任何其他类型的数组都将以相同的方式存储。

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

于 2012-06-08T12:52:28.223 回答
2

您不能修改字符串文字。字符串文字的类型是 char const[],并且任何修改它的尝试都是未定义的行为。并给出如下声明:

char* s1 = "a litteral";

,编译器确实应该生成警告。此处不推荐使用隐式转换为非常量,并且仅将其引入语言中以避免破坏现有代码(从 C 没有的时代开始const)。

在这种情况下:

char s2[] = "init";

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

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

(写起来方便一点。)

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

char* f() { return "abcd"; }

/* ... */
f()[1] = 'x';

下次你打电话f时,它又回来了"axcd"。没有出现在源代码列表中的值的文字不是 可读代码的方式,C 标准委员会决定这是一个最好不要保留的特性。

于 2012-06-08T13:28:50.640 回答
1
char string[] = "foo"

这会分配一个 char 数组,并用值 {'f', 'o', 'o', '\0'} 对其进行初始化。您可以为字符获得“自己的”存储空间,并且可以修改数组。

char strptr* = "foo"

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

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

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

于 2012-06-08T13:06:54.780 回答
1

1) 正如 c++ 标准 (2003) ( http://www.iso.org/iso/catalogue_detail.htm?csnumber=38110 )

1 字符串文字是由
双引号括起来的字符序列,可以选择以字母 L 开头,如 "..." 或 L"..."。不以 L 开头的字符串文字是普通字符串文字,也称为窄字符串文字。一个普通的字符串字面量具有“n const
char 数组”类型和静态存储持续时间 ( basic.stc ),其中 n 是字符串的大小,定义如下,并使用给定的字符进行初始化。以 L 开头的字符串文字,例如 L"asdf",是一个宽字符串文字。宽字符串文字的类型为“array of n const wchar_t”并具有静态存储持续时间,其中 n 是字符串的大小,定义如下,

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

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

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

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

于 2012-06-08T13:10:14.607 回答