当代码编译为 64 位时,此简单代码会断言(请参阅代码中的注释)。当编译为 32 位时,它不会断言。
实际上,当使用
CStringA::ReverseFind
查找最高有效位为 1 的字符时,就会出现问题。
下面的代码说明了问题:搜索
|
有效,但搜索 ¦
失败。
奇怪的是只有
CStringA::ReverseFind
有这个问题,CStringA::Find
工作正常。
其他信息:
#include <afxwin.h>
void TEST()
{
CStringW st0w(L"AB¦CD");
CStringA st0a("AB¦CD");
CStringW st1w(L"AB|CD");
CStringA st1a("AB|CD");
ASSERT(st1w.Find(L'|') == 2);
ASSERT(st1a.Find('|') == 2);
ASSERT(st0w.Find(L'¦') == 2);
ASSERT(st0a.Find('¦') == 2);
ASSERT(st1w.ReverseFind(L'|') == 2);
ASSERT(st1a.ReverseFind('|') == 2);
ASSERT(st0w.ReverseFind(L'¦') == 2);
ASSERT(st0a.ReverseFind('¦') == 2); // code asserts here
}
int main()
{
TEST();
}
这是 MFC 中的错误吗?
有人遇到过这种情况吗?
这是一个错误,虽然不是在 MFC 中,而是在底层标准库实现中,特别是
strrchr
函数。
这是我的非 MFC 重现,仍然断言 x64 的评论:
#include <string.h>
#include <assert.h>
struct alignas(32) {
char pad[8];
wchar_t val[8] = L"AB¦CD";
} st0w;
struct alignas(32) {
char pad[8];
char val[8] = "AB¦CD";
} st0a;
struct alignas(32) {
char pad[8];
wchar_t val[8] = L"AB|CD";
} st1w;
struct alignas(32) {
char pad[8];
char val[8] = "AB|CD";
} st1a;
void test1()
{
assert(wcschr(st1w.val, L'|') == st1w.val + 2);
assert(strchr(st1a.val, (unsigned char)'|') == st1a.val + 2);
assert(wcschr(st0w.val, L'¦') == st0w.val + 2);
assert(strchr(st0a.val, (unsigned char)'¦') == st0a.val + 2);
assert(wcsrchr(st1w.val, L'|') == st1w.val + 2);
assert(strrchr(st1a.val, (unsigned char)'|') == st1a.val + 2);
assert(wcsrchr(st0w.val, L'¦') == st0w.val + 2);
assert(strrchr(st0a.val, (unsigned char)'¦') == st0a.val + 2); // code asserts here
}
int main()
{
test1();
}
我观察到在调试器中可以看到
strrchr
函数的矢量化实现。它对未对齐的部分进行未矢量化,然后进行一些奇特的矢量化。
x64 和 x86 之间的区别在于它们不共享实现。 x86 版本是用汇编器实现的,而不是用 C 或 C++ 实现的。
alignas(32)
和 char pad[8]
确保字符串不与矢量化自然边界对齐。
我创建了一个错误报告:https://developercommunity.visualstudio.com/t/strrchr-doesnt-work-as-expected-for-x64/10610842
我还观察到,我不确定在进行矢量化实现之前执行未对齐的步骤对于现代硬件来说是否是一个好主意。现代硬件应该很好地处理未对齐的 SSE 系列和 AVX/AVX2,如果使用 AVX512,它有足够的屏蔽操作来处理所有事情。