我有以下代码,显示使用char数组对字符串的危险:
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] = '\0';
cout << "\n\nHello " << msg << endl;
cout << "The password is " << password;
}
当我输入超过16个字符的名称(存储在char msg[10]
)时,这16个字符后面的所有内容都会替换char password[]
中存储的值(“SECRET”) )。
password
被覆盖而不是其他变量或内存的其他部分我不会立即注意到?编辑:更新后续问题:
5. 为了回应password
和msg
彼此相邻声明的论点,我按照以下方式对声明块进行了改组:
char password[] = "SECRET";
char ch;
int i = 0;
char msg[10];
但是,没有变化
6。为了回应可能导致msg
和password
之间的差距为6(字节?)长的可能性,我已多次重新编译代码,包括上面的改组。仍然没有变化。
对于为什么有任何建议?
答案 0 :(得分:4)
前三个问题的答案是相同的:因为那是你的编译器如何选择在堆栈上布置这些变量。标准中没有任何内容可以保证 - 实际上,您所做的是未定义的行为 - 任何事情都可能发生。
更改编译器,甚至编译器设置,以及其他可能发生的事情。或不。没有说明。
至于4,除了与C代码的互操作性,或其他需要C风格字符串的API,基本上没有。
答案 1 :(得分:2)
您的两个数组msg
和password
是静态的,因此已被放置在堆栈中,这意味着它们彼此靠近。
具体细节取决于实现,可能会在编译器和优化级别之间发生变化。在分配内存时,编译器可能会对堆栈进行一些填充,msg[0]
和password[0]
之间存在16个字节的差距。
password
每次都会被覆盖,因为它恰好位于您的堆栈上msg
之上。如果您使用了不同的编译器,或者在代码中交换了它们的位置,则可能不是。如何在堆栈上分配内容不会在执行之间发生变化;它是在编译时确定的(它是静态的),而不是运行时。
请注意,原则上,编译器可以随意执行任何操作!我们只能根据典型的编译器行为做出有根据的猜测。 如果你真的想知道发生了什么,你必须看看输出组件。
std::string
(对于C ++)通常比char[]
更可取 - 它更安全,因为它实现了绑定检查并管理自己的内存。
答案 2 :(得分:2)
1。在您的情况下,内存存储如下:
msg | |i |password
| | | | | | | | | | |1|2|3|4|5|6|S|E|C|R|E|T|\0
然后你逐步写下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|\0
但如果你继续:
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数组不检查长度。 (搜索溢出)。
2.你在记忆中写道,你会删除两者之间的一切,也许是我或者不属于你的程序的东西。
3.在覆盖密码之前需要6个字符。它可能是0char和数百万。
4.除非您存储已定义的字节数组......否则,这就是代码证明的要点。
更新:
更改代码的位置不会改变填充,添加变量,数组或更好:使用不同的编译器,这样即使在优化之后,二进制也会发生变化。
重新编译不会改变生成的二进制文件,因为编译器会做同样的事情。
答案 3 :(得分:1)
1)在数组外写字会访问其他内容
2)可能对齐。
3)机会。任何事情都可能发生
4)什么都没有!