我正在调查使用此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分配足够的内存。
答案 0 :(得分:4)
strA = HeapAlloc(GetProcessHeap(), 0, len + 1);
i
的有效索引strA[i]
为0
至len
。这意味着读取strA[len + 1]
会调用未定义的行为,因为您正在读取缓冲区的末尾。
当您调用未定义的行为时,允许发生任何事情。一种可能的结果是,您的未定义行为会导致您阅读char
,其值为0
。
同样,在此之后
wlen = lstrlenW(wstr);
您知道i
的有效索引wstr[i]
为0
到wlen
。所以再次阅读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);