C++内存管理很奇怪

Strange thing with C++ memory management

本文关键字:管理 内存 C++      更新时间:2023-10-16

考虑以下代码的输出:

char A = 'a';
char B[] = "b";
cout<<&A;

它输出"ab"(AB的串联),我想知道为什么。请给我解释一下。

因为&A是一个char *。由char *表示的字符串需要具有''终止字节。

&A指向单个char,没有后续''。因此,尝试打印此文本字符串会导致未定义的行为。

您得到了意外的结果,因为char *类型的对象的operator <<被重载,以至于它输出由char *类型的指针指向的字符串。

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

'a' 'b' ''
|   |
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);

>cout期望char*参数指向以零结尾的字符串。但是&A不是零终止的,所以前提条件失败,这会导致未定义的行为。

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

我想知道为什么

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


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

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

所有其他指针隐式转换为void*并使用前一个重载,指针输出为内存地址。但后者优先于字符指针的重载,并且参数必须是以 null 结尾的字符串。

因此,由于字符指针被解释为以 null 结尾的字符串,因此打印字符地址的天真尝试不起作用。解决方案:在传递到流之前,将指针显式转换为void*

流以这种方式设计,以便方便地支持以 null 结尾的字符串,例如被视为(并且正在)比地址更典型的流式传输的字符串文本。例如,方便std::cout << "Hello World!";.打印"Hello World",而不是字符串文本所在的内存地址。

这是一个依赖于编译器的行为。通常,A 和 B 将在堆栈上一个接一个地分配。获取 A 的地址看起来像以零结尾的字符串"ab"。别这样!您依赖于编译器正在构造的堆栈帧布局。

因为你的字符不是以空结尾的。 此行为将未定义。

您的编译器在字符"a"之后立即放置了包含"b"和"\0"的字符串,因此一旦您将A字符打印为字符串,它就会找到"a",后跟"b",然后是"\0",因此您打印了"ab">