C++内存管理的奇怪问题

5
考虑一下这段代码的输出结果:
char A = 'a';
char B[] = "b";
cout<<&A;

它输出了"ab"(即AB的连接),我想知道为什么。请解释一下。

4
在C语言中没有cout这个函数。请不要滥用标签。 - StoryTeller - Unslander Monica
4
你遇到了未定义行为。为什么会出现这种情况可以解释,但这并不必要。 - DeiDei
1
输出的方式并不重要。这仍然是一个C问题,而问题是关于C和C++的内存管理。 - qtug
@qtug - 不,这不再是一个C问题。C标准没有指定cout,你的构造也不是合法的C代码。它的工作方式由C++标准规定。如果你关心C,你应该发布有效的C代码。 - StoryTeller - Unslander Monica
你应该知道C和C++可以以不同的方式管理内存。不同的规范意味着不同的语言。 - StoryTeller - Unslander Monica
显示剩余2条评论
7个回答

15
因为&A是一个char *。字符串通常由char *表示,需要在字符串的末尾添加'\0'字符来标识字符串结束。 &A指向单个char,没有紧随其后的'\0'。因此,尝试打印该文本字符串将导致未定义的行为。

1
@SouravGhosh - 转换为 void*。这也是在 stdio 中使用 %p 的正确方式。 - StoryTeller - Unslander Monica

4

由于类型为char *的对象的运算符<<重载的方式输出指向char *类型指针所指向的字符串,因此您得到了意外的结果。

看起来您的编译器将数组B放在字符A之后,它们在内存中看起来像一个字符串。

'a' 'b' '\0'
|   |
A   B

(在C++标准中未指定 A 是否会在内存中先于B,或者B会在A之前,并且是否由于填充字节而在对象之间存在间隙。)
(如果您想输出对象 A 的地址,则应编写例如)
cout<< ( void * )&A;

或者

cout<< ( const void * )&A;

或者,就像。
cout<< static_cast<void *>( &A );

或者像这样。
cout<< static_cast<const void *>( &A );

无需指定非常量对象的指针是否为const限定符,因为它将在运算符中隐式转换为类型const void *

basic_ostream<charT,traits>& operator<<(const void* p);

我喜欢考虑代码可能用途的方式,以及如何实现这一点。 - Cheers and hth. - Alf

1

cout期望char*参数指向一个以零结尾的字符串。但是&A没有以零结尾,因此前提条件失败,导致未定义行为。

未定义行为无法真正解释,因为标准允许发生任何事情。显示更多字符是可能的结果。崩溃也是一种可能。或者其他的什么。


1

我想知道为什么

因为流插入运算符要求传递给它的任何字符指针都必须指向以空字符结尾的字符串。传递给 cout 的指针指向的是不以空字符结尾的字符串 A。由于操作的前提条件(要求)未满足,行为是未定义的。


插入运算符有两个相关的重载(我已经简化了模板细节,以及其中一个是成员重载,另一个不是):

ostream& operator<< (const void*);
ostream& operator<< (const char*);

所有其他指针都隐式转换为void*并使用前一个重载,指针被输出为内存地址。但是,后者更适合字符指针,并且要求参数为以null结尾的字符串。
因此,由于字符指针被解释为以null结尾的字符串,试图打印字符的地址是不起作用的。解决方案:在传递给流之前将指针显式转换为void*
流被设计为支持以null结尾的字符串(例如字符串字面量),这些字符串通常比地址更容易使用。例如,std::cout << "Hello World!";很方便地打印出“Hello World”,而不是字符串字面量所在的内存地址。

0

这是一个依赖于编译器的行为。通常情况下,A和B会在堆栈上相继分配。获取A的地址看起来像以零结尾的字符串“ab”。不要这样做!你依赖于编译器构建的堆栈帧布局。


0

因为你的字符没有以空字符结尾。 这将导致行为未定义。


0

你的编译器把包含 'b' 和 '\0' 的字符串立即放在字符 'a' 后面,因此一旦你将A字符作为字符串打印出来,它会找到 'a',然后是 'b' 然后是 '\0',因此你打印了 'ab'


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接