首先我在做,
#include <iostream>
#include <string>
void foo (std::string s) {
std::cout << s << std::endl;
}
int main () {
char st[] = "0";
st[1] = '[';
std::string s (st);
std::cout << s << std::endl;
return 0;
}
当我使用流插入运算符发送到流对象cout
时,它显示垃圾(猜测这是正常行为)。
然后我按值传递给函数foo
进行检查。
我现在正在做这样的事情。
foo (s);
// after calling foo () value printed by the
// std::cout is changed. it takes care of NULL character ('\0')
std::cout << s << std::endl;
它不会在0[
之后显示垃圾。为什么呢?
我的问题是,
我按值foo ()
传递了字符串。在foo ()
的主体运行之前运行一个复制构造函数。但仍然没有意义。这个copyctor
更改参数如何通过值从main ()
传递?
提前致谢。
答案 0 :(得分:10)
char st[] = "0";
流行测验:st
有多大?
答案:两个chars
一个用于"0
“,一个用于空终止符。
然后你去覆盖空终止符:
st[1] = '[';
...并尝试根据以下内容构建string
:
std::string s (st);
这里调用了string
const char*
构造函数,它的工作方式是查找null终止符。自从你把它吹走之后,它将会超过st
的末尾。跑过st
的末尾会唤起未定义的行为,这就是你看到垃圾的原因。
重要的是要注意,未定义的行为意味着任何都可能发生。这里的“任何东西”包括“我想要发生的事情”。它不一定会得到垃圾输出,或者你的程序会崩溃。试图弄清楚为什么UB以这种方式表现出来或者这是一种无理的努力。
答案 1 :(得分:3)
人们告诉你未定义的行为。这很棒,但你已经知道了。
在实践中,关键是你的第一个&#34;垃圾&#34;字符本身可能是'\0'
字符,在这种情况下,您不会再看到任何字节。
您不能假设您将至少看到20个任意字符,因为您的代码的一个完全合理的结果是您看到0个任意字符。
简而言之,你试图使不合理的人合理化;停下来!
答案 2 :(得分:1)
这两行之后:
char st[] = "0";
st[1] = '[';
st
不是以空字符结尾的字符串。 std::string(char const*)
期望一个以空字符结尾的字符串。由于std:string
的构造函数的输入不符合该标准,因此您输入的是未定义的行为区域。之后可能发生任何事情。
答案 3 :(得分:1)
这一行:
char st[] = "0";
使编译器分配一个2字节数组的固定大小数组,并用&#34; 0 \ 0&#34;填充它。 (ASCII 0后跟空字符)。这一行
st[1] = '[';
然后分配&#39; [&#39;到之前被NULL占用的位置,破坏了字符串的空终止。然后这一行
std::string s (st);
将从st
的开头开始,并继续将字符复制到s
,直到它在内存中的某处找到空值。这就是你看到垃圾的原因。