发布版本中,调试断言(Debug Assert) 会执行什么操作?

4
我的应用程序在处理中文字符(宽字符)时,在发布版本中表现奇怪。我有以下代码行,在调试模式下会抛出 debug assert:
str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end());

(其中str为std::wstring)

在调试模式下,此行会抛出断言。我知道这是因为 isspace 无法处理宽字符。所以我必须使用 iswspace 代替 isspace

str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end());

            if (!str.empty())
            { // add str to GUI }

如果我在调试断言中按下“忽略”,则 str 会被正确地添加到GUI中。但在发布模式下,str 不会被添加到GUI中。
但是如果我使用 iswspacestr 就会被正确地添加到 GUI 中,而不需要对我的添加到 GUI 逻辑进行任何更改。
更奇怪的是,有些中文字符在发布模式下也能够正确地添加到 GUI 中。例如,当 str 为 L"左" 时,它会被添加到 GUI 中。但当 str 为 L"右" 时就不会添加到 GUI 中了。
有人遇到过这个问题吗?
我理解的是,在发布模式下,调试断言不会被考虑,并且会像 "忽略" 一样工作。
编辑:
我进一步调试了它(在发布模式下)。看起来在L"右" 的情况下它并没有进入 if(!str.empty())。但是Visual Studio调试器在 if 条件处命中断点时仍然显示 L"右" 在 str 中。
编辑 2: 我在 str.erase 行上面添加了 std::locale::global(std::locale(""));。 现在它在调试和发布情况下的工作完全相同,文本被添加到 GUI 中。
这是一个例子:
#include <string>
#include <iostream>
#include <algorithm>

int main(int argc, char* argv[])
{
    std::wstring str1 = L"左";
    std::wstring str2 = L"右";
    str1.erase(std::remove_if(str1.begin(), str1.end(), isspace), str1.end());
    if (!str1.empty())
    {
        std::wcout << L"str1 not empty\n";
    }
    str2.erase(std::remove_if(str2.begin(), str2.end(), isspace), str2.end());
    if (!str2.empty())
    {
        std::wcout << L"str2 not empty\n";
    }
    getchar();
    return 0;
}

这将仅打印“str1不为空”。


忽略触发的断言可能会导致在发布模式下出现未定义的行为。在编译发布版本之前,请确保在调试模式下一切正常运作。 - DeiDei
1
你可能误解了问题的关键。assert 可能不是唯一的差异;Visual C++ 库往往会在简单的 assert 之外进行更广泛的调试检查。 - MSalters
3
能够看到声明文本和 str 类型的想法会很好,因为在 std::basic_string<char> 上使用 iswspace 并不起作用。通常来说,如果你在处理 Unicode,你应该使用 ICU,因为标准库对于 Unicode 的处理能力非常有限,即使是更新版本也仍然存在一些问题。你可以提供一个自包含示例吗? - DevSolar
@Jake:L"..."是宽字符串字面值。根据(实现定义的)编码方式,这样的字面值中的宽字符可能包含空字节。如果您将其传递给std::basic_string<char>,那么该空字节可能会被解释为字符串结尾。我认为现在是提供一个[mcve]的时候了,因为现在我们都只是在猜测。 - DevSolar
1
@DevSolar 我已经添加了一个例子。 - Jake
显示剩余4条评论
3个回答

5

在发布模式下,宏assert什么也不做,会被忽略。

如果在源代码中包含<assert.h>时,定义了宏名称NDEBUG,则assert不执行任何操作1,2

然而,仅仅忽略断言并不能解决问题。请确保您的代码在调试模式下正常工作。


2
我的理解是,在释放模式下,调试断言将不被考虑,并且类似于“忽略”。
是的,但您正在忽略未定义的行为,因为您正将超出范围的值传递给 isspace
阅读您使用的功能和函数的 文档
行为在 ch 的值不能表示为 unsigned char 并且不等于 EOF 时是未定义的。
实际上,您不应该使用 isspace 来处理这个。使用支持您使用的编码(UTF-8?UTF-16?)的库。

1

对于宽字符串,请使用 iswspace(以及其宽字符版本):

#include <string>
#include <algorithm>
#include <iostream>

int main(int argc, char* argv[])
{
    std::wstring str1 = L"左";
    std::wstring str2 = L"右";
    str1.erase(std::remove_if(str1.begin(), str1.end(), iswspace), str1.end());
    if (!str1.empty())
    {
        std::wcout << L"str1 not empty\n";
    }
    str2.erase(std::remove_if(str2.begin(), str2.end(), iswspace), str2.end());
    if (!str2.empty())
    {
        std::wcout << L"str2 not empty\n";
    }
    getchar();
    return 0;
}

输出:

str1 not empty
str2 not empty

无论在发布模式还是调试模式下,isspace版本是否“正常工作”都是一个红色警告,因为您一直在调用未定义的行为

CppReference.com on isspace, 强调我的:

如果ch的值不能表示为unsigned char并且不等于EOF,则行为是未定义的


如果你要在Unicode字符串上进行“真正”的文本处理,我强烈建议使用ICU,而不是标准库提供的任何东西,因为标准库无法正确处理Unicode。无论是字符串比较、规范化、分词还是其他任何操作——只要你真正关心这些字符串中包含的内容,你就应该使用一个真正了解Unicode并能够处理其复杂性的库。我不是说ICU很容易或舒适,但它是有能力的 - DevSolar
同意你的观点。正如我在问题中最初提到的那样,当我使用iswspace时,我没有任何问题。但是我的主要关注点是str怎么会变为空呢?!而且一旦我添加了std::locale::global(std::locale("")),它在发布版本中也可以正常工作。 - Jake
@Jake,“未定义行为”意味着未定义。计算机可能会清空该字符串,浇水植物或关闭您的冰箱。不要费心去尝试找出答案,因为任何答案都将特定于您当前的实现,并且根本不会告诉您任何有用的信息。在我看来,这是一种浪费精力的做法。 - DevSolar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接