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

时间:2015-02-27 09:37:14

标签: c winapi

我正在调查使用此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分配足够的内存。

1 个答案:

答案 0 :(得分:4)

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

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

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

同样,在此之后

wlen = lstrlenW(wstr);

您知道i的有效索引wstr[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);