溢出字符数组每次都覆盖完全相同的字符串-为什么?

Overflowing char array overwrites the exact same string every time - why?

本文关键字:字符串 为什么 数组 字符 覆盖 溢出      更新时间:2023-10-16

下面的代码显示了在字符串上使用字符数组的危险:

int main(){
    char password[] = "SECRET";
    char msg[10], ch;
    int i = 0;
    cout << "Please enter your name:";
    while((ch = getchar()) != 'n'){
            msg[i++] = ch;
    }
    msg[i] = '';
    cout << "nnHello " << msg << endl;
    cout << "The password is " << password;
}

当我输入的名称(存储在char msg[10]中)大于16个字符时,这16个字符之后的内容将替换存储在char password[] ("SECRET")中的值。

  1. 为什么会这样?(一般的好奇心)
  2. 为什么16个字符而不是10 -数组的大小?
  3. 为什么总是password被覆盖,而不是其他变量或内存的其他部分,我不会立即注意到?
  4. 使用char[]比字符串有什么好处呢?

编辑:更新了后续问题:
5。针对passwordmsg相邻声明的论点,我将声明块洗牌如下:

char password[] = "SECRET";
char ch;
int i = 0;
char msg[10];

然而,没有变化。
6。作为对导致msgpassword之间的差距为6(字节?)长的争论的回应,我已经多次重新编译了代码,包括上面的重组。但还是没有变化。

有什么建议吗?

前三个问题的答案是相同的:因为这是编译器选择在堆栈上布局这些变量的方式。标准中没有任何内容保证——事实上,你所做的是未定义的行为——任何事情都可能发生。

改变编译器,甚至是编译器设置,还有其他可能发生的事情。与否。说不准。

对于4,除了与C代码的互操作性,或者其他需要C风格字符串的api,基本上没有。

1。在您的情况下,内存是这样存储的:

 msg                |   |i      |password
| | | | | | | | | | |1|2|3|4|5|6|S|E|C|R|E|T|

然后在msg上依次写:

 msg                |           |password
|A|Z|E|R|T|Y|U|I|O|P|1|2|3|4|5|6|S|E|C|R|E|T|

但是如果你继续:

 msg                |           |password
|A|Z|E|R|T|Y|U|I|O|P|1|2|3|4|5|6|Q|W|E|R|T|Y|

因为char数组不检查长度。(搜索溢出).

你在内存上写东西,你擦除其中的所有东西,可能是i或不属于你的程序的东西。

3,所以在覆盖密码之前需要6个字符。可以是0char,也可以是millions。

4 .除非你存储一个定义好的字节数组…什么也没有,这是代码所证明的。

更新:

  1. 改变代码的位置不会改变填充,添加变量,数组,或者更好:使用不同的编译器,这样即使优化后,二进制文件也会改变。

  2. 重新编译不会改变生成的二进制文件,因为编译器会做完全相同的事情。

您的两个数组msgpassword是静态的,因此它们被放置在堆栈上,这意味着它们彼此靠近。

细节依赖于实现,并且可能在编译器和优化级别之间发生变化。有可能编译器在分配内存时填充了堆栈,msg[0]password[0]之间有16个字节的间隙。

password每次都会被覆盖,因为它恰好在堆栈中的msg之上。如果您使用不同的编译器,或者在代码中交换了它们的位置,则可能不会。在执行之间,堆栈上的东西分配方式不会改变;它是在编译时确定的(它是静态的),而不是运行时。

注意,原则上,编译器可以自由地做任何它想做的事情!对于典型的编译器行为,我们只能做出有根据的猜测。如果您真的想知道发生了什么,您必须查看输出程序集。

std::string(对于c++)通常比char[]更可取,因为它实现了绑定检查并管理自己的内存,因此要安全得多。

1)在数组外写入将访问其他内容。
2)可能对齐。
3)机会。一切皆有可能。
4)任何东西!