C++ 从std::string对象到char*的显式转换,无需使用任何std::string成员函数

C++ 从std::string对象到char*的显式转换,无需使用任何std::string成员函数,c++,string,C++,String,这个问题不是关于任何问题,而是一个深入理解std::string的内存布局的问题 我做了一些实验,意识到可以显式地将std::string转换为char*,并成功地检索存储在std::string对象中的“string”。问题是,当std::string对象的起始地址与std::string::c_str()方法返回的地址不同时,如何可能 最近,我遇到了从std::string对象到char*对象的显式转换。起初,我认为这种转换不适用于std::string,但我很惊讶地知道它可以工作 intm

这个问题不是关于任何问题,而是一个深入理解
std::string
的内存布局的问题

我做了一些实验,意识到可以显式地将
std::string
转换为
char*
,并成功地检索存储在
std::string
对象中的“string”。问题是,当
std::string
对象的起始地址与
std::string::c_str()
方法返回的地址不同时,如何可能

最近,我遇到了从
std::string
对象到
char*
对象的显式转换。起初,我认为这种转换不适用于
std::string
,但我很惊讶地知道它可以工作

intmain()
{
std::string TestString=“测试”;
void*pPointerToStringObject=(void*)&TestString;
char*pExplicitlyConvertedString=*((char**)pPointerToStringObject);
printf(“指向字符串对象的指针:%p\n”,pPointerToStringObject);
printf(“c_str()返回的指针:%p\n\n”,TestString.c_str());
printf(“通过字符串对象的显式转换检索到的字符串:\%s\”\n,pExplicitlyConvertedString);
}
输出:

Pointer to the string object : 0x7ffd84d3f4a0
Pointer returned by c_str()  : 0x7ffd84d3f4b0

"String" retrieved by explicit conversion of string object : "Testing"
这是不对的,当您取消引用
pExplicitlyConvertedString
时,您的程序具有未定义的行为

支持的标准方法是使用


这是一个执行的问题。假设您使用的是libstdc++。然后,
std::string
的第一个成员是。这就是你得到的

由于SSO应用于短字符串,因此此指针指向(
b0-a0
十六进制)

例如,如果我们看一下libstdc++实现:

_Alloc_hider _M_dataplus;  // offset 0 - an address of object - pointer to data
size_type    _M_string_length;

enum { _S_local_capacity = 15 / sizeof(_CharT) };

union
{
  _CharT     _M_local_buf[_S_local_capacity + 1];  // offset 16 - short string stored here
  size_type  _M_allocated_capacity;
};
指针存储在
\u Alloc\u hider
中的位置:

pointer _M_p; // The actual data.


请注意,您的代码可能无法以这种(未定义的)方式与所示实现以外的其他实现一起工作。例如,libc++使用另一种方法来应用SSO。一般来说,您的代码可能会导致未定义的行为,正如其他人在评论中指出的那样。

未指定
std::string
的内存布局。“我很惊讶它能工作”您被误导了。这是未定义的行为,因此有时可能会起作用。对于您来说,在您的平台上,在编译器和标准库实现中,指向内存缓冲区的指针与字符串的地址一致。编辑:我没有注意到指针是不同的。可能只是在实际字符日期之前有一堆不可打印的数据。即使在当前实现中,字符串作为std::string中的第一个元素,如果字符串的长度与“short string optimization”(短字符串优化)的大小一样长,也总是会失败。将测试字符串变长为SSO,通常为16字节。@FrançoisAndrieux:“指向内存缓冲区的指针与字符串的地址一致”——事实上并非如此。请参考有问题的输出。@Gavi请重新阅读评论,似乎您没有看到编辑内容(有标签)。我知道这根本不是好的做法。正如我在问题中提到的,我只是想了解一下std::string实现。@Gavi当您使用受未定义行为影响的代码进行实验时,了解程序行为的唯一方法是查看标准库的具体实现,很多时候,编译器生成的汇编代码。很难从平台无关的角度来解释程序的行为。@DanielLangr,如果按照您的想法实现了某些东西,那么就没有UB。大概若编译器能够捕获UB,那个么它可以做一些奇怪的事情。看一看(这个)[请注意,未定义的行为是一个不依赖于实现的属性。您实际观察到的行为就是它所做的。因此,即使特定的实现将提供一致和可靠的行为,此代码也具有未定义的行为。换句话说,一段代码具有或不具有未定义的行为,以及如何做到这一点未定义的行为在实践中可能因平台、实现和编译器的不同而有所不同。但即使一个组合给出了一致的结果,代码本身仍然是未定义的行为。第2部分:未定义的行为意味着“标准对结果行为没有任何说明”即使给定的标准库实现导致代码可靠运行,这也是正确的。@FrançoisAndrieux你确定吗?我相信UB确实依赖于代码。如果你将OP的代码与我展示的代码(libstdc++中的确切实现)合并,为什么会导致UB?该行为由标准定义(将指向标准布局类对象的指针作为指向其第一个成员变量的指针取消引用)。如果您在其中复制代码,则可以正常工作。但是,标准库是特殊的。除非您完全控制它,否则它位于您的代码外部。因此,不允许依赖它的结构。如果其他人获取代码并尝试编译它,它可能无法以与您相同的方式工作。这是未定义行为的属性。它可能适用于您u但不是下一个人。你不知道他们将如何编译它。如果你将字符串实现复制到你的项目中,重命名它并使用它而不是
std::string
,那么它就可以了(不太好,但合法)。它为每个人编译相同的代码。通常,您的代码可能会导致未定义的行为。这不是真的。代码将始终使用UB as强制转换为
char**
以打破严格的别名。
_Alloc_hider _M_dataplus;  // offset 0 - an address of object - pointer to data
size_type    _M_string_length;

enum { _S_local_capacity = 15 / sizeof(_CharT) };

union
{
  _CharT     _M_local_buf[_S_local_capacity + 1];  // offset 16 - short string stored here
  size_type  _M_allocated_capacity;
};
pointer _M_p; // The actual data.