因此,当我遇到最奇怪的内存问题时,我正在编写一些网络代码,而我无法正确地整理出这里可能发生的情况。我想知道c_str()
中是否存在某种我没有正确观察到的暗示。
这是其中包含错误的代码。 (还有一个释放错误,但我只是将这个功能作为一个宠物项目而已。)
#include <netdb.h> // for AF_UNSPEC, AF_INET, AF_INET6
#include <stdint.h> // for uint16_t, etc.
#include <sys/types.h> // for AF_UNSPEC, AF_INET, AF_INET6
#include <sys/socket.h> // for AF_UNSPEC, AF_INET, AF_INET6
#include <string> // for std::strin
#include <stdio.h>
#include <string.h>
int main() {
uint16_t port = 2098;
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET6; // IPv6 (also handles IPv4 clients)
hints.ai_socktype = SOCK_STREAM; // stream
hints.ai_flags = AI_PASSIVE; // use wildcard "INADDR_ANY"
hints.ai_protocol = IPPROTO_TCP; // tcp protocol
hints.ai_canonname = nullptr;
hints.ai_addr = nullptr;
hints.ai_next = nullptr;
const char* port_num = (std::to_string(port)).c_str();
struct addrinfo *result;
int res = getaddrinfo(nullptr, port_num, &hints, &result);
printf("HI\n");
}
如果对生成的二进制文件进行valgrind,则会得到:
==45919== Invalid read of size 1
==45919== at 0x573BA5C: getaddrinfo (in /usr/lib64/libc-2.17.so)
==45919== by 0x400C95: main (in /homes/iws/kieruc/Coding/a.out)
==45919== Address 0x5a22058 is 24 bytes inside a block of size 29 free'd
==45919== at 0x4C2B16D: operator delete(void*) (vg_replace_malloc.c:576)
==45919== by 0x4EF3B62: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib64/libstdc++.so.6.0.19)
==45919== by 0x400C7C: main (in /homes/iws/kieruc/Coding/a.out)
==45919== Block was alloc'd at
==45919== at 0x4C2A1E3: operator new(unsigned long) (vg_replace_malloc.c:334)
==45919== by 0x4EF3A18: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib64/libstdc++.so.6.0.19)
==45919== by 0x400FFE: char* std::string::_S_construct<char*>(char*, char*, std::allocator<char> const&, std::forward_iterator_tag) (in /homes/iws/kieruc/Coding/a.out)
==45919== by 0x400F14: char* std::string::_S_construct_aux<char*>(char*, char*, std::allocator<char> const&, std::__false_type) (in /homes/iws/kieruc/Coding/a.out)
==45919== by 0x400EDD: char* std::string::_S_construct<char*>(char*, char*, std::allocator<char> const&) (in /homes/iws/kieruc/Coding/a.out)
==45919== by 0x400E8F: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string<char*>(char*, char*, std::allocator<char> const&) (in /homes/iws/kieruc/Coding/a.out)
==45919== by 0x400E20: std::string __gnu_cxx::__to_xstring<std::string, char>(int (*)(char*, unsigned long, char const*, __va_list_tag*), unsigned long, char const*, ...) (in /homes/iws/kieruc/Coding/a.out)
==45919== by 0x400CDE: std::to_string(int) (in /homes/iws/kieruc/Coding/a.out)
==45919== by 0x400C60: main (in /homes/iws/kieruc/Coding/a.out)
==45919==
==45919== Invalid read of size 1
==45919== at 0x573BACA: getaddrinfo (in /usr/lib64/libc-2.17.so)
==45919== by 0x400C95: main (in /homes/iws/kieruc/Coding/a.out)
==45919== Address 0x5a22058 is 24 bytes inside a block of size 29 free'd
==45919== at 0x4C2B16D: operator delete(void*) (vg_replace_malloc.c:576)
==45919== by 0x4EF3B62: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib64/libstdc++.so.6.0.19)
==45919== by 0x400C7C: main (in /homes/iws/kieruc/Coding/a.out)
==45919== Block was alloc'd at
==45919== at 0x4C2A1E3: operator new(unsigned long) (vg_replace_malloc.c:334)
==45919== by 0x4EF3A18: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib64/libstdc++.so.6.0.19)
==45919== by 0x400FFE: char* std::string::_S_construct<char*>(char*, char*, std::allocator<char> const&, std::forward_iterator_tag) (in /homes/iws/kieruc/Coding/a.out)
==45919== by 0x400F14: char* std::string::_S_construct_aux<char*>(char*, char*, std::allocator<char> const&, std::__false_type) (in /homes/iws/kieruc/Coding/a.out)
==45919== by 0x400EDD: char* std::string::_S_construct<char*>(char*, char*, std::allocator<char> const&) (in /homes/iws/kieruc/Coding/a.out)
==45919== by 0x400E8F: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string<char*>(char*, char*, std::allocator<char> const&) (in /homes/iws/kieruc/Coding/a.out)
==45919== by 0x400E20: std::string __gnu_cxx::__to_xstring<std::string, char>(int (*)(char*, unsigned long, char const*, __va_list_tag*), unsigned long, char const*, ...) (in /homes/iws/kieruc/Coding/a.out)
==45919== by 0x400CDE: std::to_string(int) (in /homes/iws/kieruc/Coding/a.out)
==45919== by 0x400C60: main (in /homes/iws/kieruc/Coding/a.out)
但是,如果我将代码更改为此
std::string portstr = std::to_string(port);
struct addrinfo *result;
int res = getaddrinfo(nullptr, portstr.c_str(), &hints, &result);
然后就没有内存错误。而且我不太明白为什么。
要注意的另一件事是,如果我尝试使用带有const char*
的{{1}}变量来编译版本,则会收到警告,指出该变量应为常数。
这是怎么回事?
答案 0 :(得分:4)
c_str
返回的缓冲区仅在关联的std::string
对象存在的情况下才有效。还有这里
const char* port_num = (std::to_string(port)).c_str();
创建一个临时字符串对象,获取其缓冲区地址,然后在完整表达式的末尾死亡。使用悬挂指针会出现内存错误。
如果您希望使用临时std::string
,则必须在使用缓冲区的完整表达式中创建它:
int res = getaddrinfo(nullptr, std::to_string(port).c_str(), &hints, &result)
答案 1 :(得分:4)
使用const char* port_num = (std::to_string(port)).c_str();
,您将创建一个std::string
类型的临时对象,该对象的生存时间与使用该表达式的时间一样长。因此,.c_str()
将指向您的语句之后立即释放的内存。
从其中减去两行:
auto portStr = std::to_string(port);
const char* port_num = portStr.c_str();
因此,portStr
对象将一直存在直到函数结束,并且您可以使用.c_str
调用的结果直到函数结束(除非您更改{{ 1}}之间。