为什么std :: string使wsagetlasterror返回零

Why std::string makes WSAGetLastError returning zero?

本文关键字:返回 wsagetlasterror string std 为什么      更新时间:2023-10-16

有一天。我写了插座包装班,例如C 的python内置插座库(Visual Studio 2015(

所有功能的运作良好。但是,我发现我所做的一些功能有问题。

使用std::string的方法之后,例如.size().clear()WSAGetLastError()总是返回零。

std::string str = "Hello, world";
size_t recvsz = ::send(socket, str.c_str(), str.size(), 0);
// Why always return zero after using std::string's methods?
int err = ::WSAGetLastError();

因此,我不知道插座状态,插座已经死了,或者插座仍在等待接收数据。

所以,我使用了一种方法,在std::string的方法之前存储WSAGetLastError()返回值,然后使用WSASetLastError()恢复它。

std::string str = "Hello, world";
// Before using std::string's methods
int err = ::WSAGetLastError();
size_t recvsz = ::send(socket, str.c_str(), str.size(), 0);
// Restore error value after std::string's methods
::WSASetLastError(err);

我使用的正确方法是正确的吗?或为什么std::string犯下最后一次错误?

编辑:哇...我错误.. :(。

SOCKET socket;
char buf[100] = {0x00,};
std::string data, temp_buf;
ssize_t recvsz = ::recv(socket, buf, 99, 0);
// When err has proper error code before std::string methods
// It just for a test
// int err = ::WSAGetLastError();
temp_buf = buf;
data += buf;
int err = WSAGetLastError(); // Always return zero!

,因为您通过写入不拥有的只读记忆来犯下了总体而言。简而言之,您将计算机的内存打电话给现在,现在您的程序将无法预测。

使用合规的C 11编译器,您可以做:

ssize_t recvsz = ::recv(socket, &str[0], str.size(), 0);

那是因为&str[0]是指向实际的字符串缓冲区的指针(在非const上下文中(,并且是因为从C 11中保证了数据可以存储连续。从技术上讲,在此之前,不是很多(尽管实际上它基本上总是是(。

否则,您将不得不分配自己的char[]缓冲区,以接收数据。除非您使用具有紧密资源限制的嵌入式系统,否则您可以将其自动存储持续时间几乎没有成本(但无论如何您都不会使用string(。

顺便说一下,请小心您的返回类型:size_t未签名。recv返回a ssize_t ,该签署,可能是否定的,以指示错误条件。

const size_t BUF_SIZE = 256;
char buf[BUF_SIZE];
ssize_t recvsz = ::recv(socket, &buf[0], BUF_SIZE, 0);
if (recvsz < 0) {
   // ... handling
}
// Now, optionally:
std::string str(buf, recvsz);

现在,有了所有固定的,答案是即使成功的操作也可以更改"最后一个错误"。因此,是的,如果要保留它,则必须保存,然后重新设置先前的"最后一个错误"值。这确实具有可疑的价值&mdash;为什么您不处理生成的错误?在造成错误后继续进行业务似乎是一种不好的方法。

您不应以这种方式写入std::string的内部缓冲区。它由对象管理,您必须使用append()assign()之类的功能进行更新,因此字符串可以跟踪其当前内容和长度。

此外,c_str()返回const char*指针,您不能写入此类内存。

如果您确定,您将始终编写正确的字节(因此书面数据和内部存储的实际数据长度将匹配(,您可以使用operator[]来获取对该特定字符的引用字符串(大概是您的情况下的第一个(,并将其地址用作目标缓冲区。


@andyg在他的评论中注意到,C 17引入了std::basic_string::data(),它也将指针返回到实际的缓冲区。

您的代码应采用以下表格之一:

size_t recvsz = ::recv(socket, &str[0], str.size(), 0);
size_t recvsz = ::recv(socket, str.data(), str.size(), 0); // C++ 17, str must not be const