访问字符串:: c_str()

时间:2019-06-07 20:25:02

标签: c++ pointers valgrind stdstring

因此,当我遇到最奇怪的内存问题时,我正在编写一些网络代码,而我无法正确地整理出这里可能发生的情况。我想知道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}}变量来编译版本,则会收到警告,指出该变量应为常数。

这是怎么回事?

2 个答案:

答案 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}}之间。