gpt4 book ai didi

c++ - 为什么 std::string 构造函数重置 GetLastError

转载 作者:太空狗 更新时间:2023-10-29 19:51:37 27 4
gpt4 key购买 nike

我从 C++ 代码调用 Windows API,我有一个辅助方法来执行 FormatMessage 内容并抛出异常以进行错误处理。函数的签名是

void throw_system_error(const std::string& msg_prefix, DWORD messageid)

我注意到了一些奇怪的事情。此代码无法正常工作:

handle = ...;
if (handle == NULL) {
throw_system_error("something bad happened", GetLastError());
}

传递给 throw_system_error 的错误代码始终为零。

因为这工作得很好:

handle = ...;
if (handle == NULL) {
DWORD error = GetLastError();
throw_system_error("something bad happened", error);
}

更多的调查表明这个版本有同样的问题:

handle = ...;
if (handle == NULL) {
std::string msg("something bad happened");
DWORD error = GetLastError();
throw_system_error(msg, error);
}

它寻找整个世界,就好像 std::string 的构造函数正在重置错误代码。

我的猜测是 std::string 正在内部分配内存,这会导致一些系统调用,然后将最后一个错误设置回零。

有人知道这里到底发生了什么吗?

Visual C++ 2015,64 位。

最佳答案

让我们看看 GetLastError文档:

Most functions that set the thread's last-error code set it when they fail. However, some functions also set the last-error code when they succeed.

You should call the GetLastError function immediately when a function's return value indicates that such a call will return useful data. That is because some functions call SetLastError with a zero when they succeed, wiping out the error code set by the most recently failed function.

所以有一个调用 SetLastError 的函数,很可能是一个分配内存的函数:当你构造一个字符串时,new 被调用来分配内存。

现在,让我们看看new在vc++中的实现。 Stack Overflow 中对这个问题有一个很好的答案:https://softwareengineering.stackexchange.com/a/293209

It depends if you are in debug or release mode. In release mode, there is HeapAlloc/HeapFree which are kernel functions,

while in debug mode (with visual studio) there is a hand written version of free and malloc (to which new/delete are re-directed) with thread locks and more exceptions detection, so that you can detect more easily when you did some mistakes with you heap pointers when running your code in debug mode.

所以在 Release模式下,调用的函数是HeapAlloc ,它调用 SetLastError。来自文档:

If the function fails, it does not call SetLastError

所以代码应该在 Release模式下正常工作。然而,在调试实现中,函数 FlsGetValue 被调用,并且该函数在成功时调用 SetLastError

检查这个很容易,

#include <iostream>
#include <Windows.h>
int main() {

DWORD t = FlsAlloc(nullptr);
SetLastError(23); //Set error to 23
DWORD error1 = GetLastError(); //store error

FlsGetValue(t); //If success, it is going to set error to 0

DWORD error2 = GetLastError(); //store second error code

std::cout << error1 << std::endl;
std::cout << error2 << std::endl;
system("PAUSE");
return 0;
}

输出如下:

23
0

所以 FlsGetValue 调用了 SetLastError()。为了证明它仅在调试时被调用,我们可以进行以下测试:

#include <iostream>
#include <Windows.h>
int main() {

DWORD t = FlsAlloc(nullptr);
SetLastError(23); //Set error to 23
DWORD error1 = GetLastError(); //store error

int* test = new int; //allocate int

DWORD error2 = GetLastError(); //store second error code

std::cout << error1 << std::endl; //output errors
std::cout << error2 << std::endl;

delete test; //free allocated memory
system("PAUSE");
return 0;
}

如果你在 Debug模式下运行它,它会给你,因为它调用了 FlsGetValue:

23
0

但是,如果你在 Release模式下运行它,它会产生,因为它调用了 HeapAlloc:

23
23

关于c++ - 为什么 std::string 构造函数重置 GetLastError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39168416/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com