为什么在从 WCHAR 转换为 char 时会得到一个额外的空终止字符?

Why do I get an extra null terminating character when converting from WCHAR to char?

我正在研究使用此 C 代码的字符串空终止。

#include <Windows.h>
#include <stdio.h>

int wmain(int argc, WCHAR *argv[])
{
    WCHAR *wstr = argv[1];
    int wlen, len, written;
    char *strA;
    DWORD nOut = 0;

    wlen = lstrlenW(wstr);

    printf("wlen: %d\n", wlen);

    if (wstr[wlen] == 0) printf("wstr[%d] == 0\n", wlen);
    if (wstr[wlen + 1] == 0) printf("wstr[%d] == 0\n", wlen + 1);

    len = WideCharToMultiByte(GetConsoleOutputCP(), 0, wstr, wlen, NULL, 0, NULL, NULL);
    printf("len: %d chars required\n", len);

    strA = HeapAlloc(GetProcessHeap(), 0, len + 1);
    if (!strA) return -1;

    written = WideCharToMultiByte(GetConsoleOutputCP(), 0, wstr, wlen, strA, len, NULL, NULL);
    printf("written: %d\n", written);
    strA[len] = 0; /* Null terminate the ASCII string */

    WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), strA, len, &nOut, NULL); printf("\n");

    if (strA[len] == 0) printf("strA[%d] == 0\n", len);
    if (strA[len + 1] == 0) printf("strA[%d] == 0\n", len + 1);

    HeapFree(GetProcessHeap(), 0, strA);

    return 0;
}

如果我提供一个由偶数个 WCHAR 组成的输入字符串,例如Hello!,我得到:

wlen: 6
wstr[6] == 0
wstr[7] == 0 /* Where does this come from? */
len: 6 chars required
written: 6
Hello!
strA[6] == 0
strA[7] == 0 /* Where does this come from? */

但是如果我提供一个包含奇数个 WCHAR 的字符串,例如Hello,我只得到:

wlen: 5
wstr[5] == 0
len: 5 chars required
written: 5
Hello
strA[5] == 0

为什么在 wstr[len+1]str[len+1] 处多了一个空终止符?据我所知,没有为 ASCII 字符串中的第二个 NUL 分配足够的内存。

strA = HeapAlloc(GetProcessHeap(), 0, len + 1);

strA[i] 的有效索引 i 包括 0len。这意味着读取 strA[len + 1] 会调用未定义的行为,因为您正在读取超出缓冲区末尾的内容。

当您调用未定义的行为时,允许发生任何事情。一种可能的结果是您未定义的行为导致您读取值为 0.

char

同样,在这之后

wlen = lstrlenW(wstr);

您知道 wstr[i] 的有效索引 i 包含 0wlen。所以再次阅读 wstr[wlen + 1].

是错误的

简单地说,停止读取超出缓冲区的末尾。

您将错误的长度值传递给 WideCharToMultiByte。应该是:

UINT cp = GetConsoleOutputCP();
len = WideCharToMultiByte(cp, 0, wstr, -1, NULL, 0, NULL, NULL);

written = WideCharToMultiByte(cp, 0, wstr, -1, strA, len+1, NULL, NULL);