【问题标题】:In C, what would happen if I put 'successive wchar_t characters' into a wchar_t variable?在 C 语言中,如果我将“连续 wchar_t 字符”放入 wchar_t 变量会发生什么?
【发布时间】:2021-11-30 04:21:30
【问题描述】:
#include <stdio.h>

wchar_t wc = L' 459';
printf("%d", wc);           //result : 32

我知道 ASCII 码表中的“空格”是“十进制 32”。

我不明白的是,据我所知,如果变量没有足够的空间来存储值,则该值将是原始值的“最后一位”。

例如,如果我将二进制值“1100 1001 0011 0110”放入单字节变量中,它将是“0011 0110”,即原始二进制值的“最后一个字节”。

但上面的代码显示了原始值的“第一个字节”。

我想知道当我执行上面的代码时内存级别发生了什么。

【问题讨论】:

  • 根据 C 规范,"包含多个多字节字符或映射到扩展执行字符集的多个成员的单个多字节字符的宽字符常量的值,或包含未在扩展执行字符集中表示的多字节字符或转义序列是实现定义的。"
  • 例如,微软的 MSVC (cl) 和 GNU GCC (gcc) 将 wc 设置为不同的值。 MSVC 将其设置为L' ',GCC 将其设置为L'9'。如果您将警告级别设置得足够高,两个编译器都会发出有关截断的警告。
  • wchar_t 在 Linux 中为 4 个字节,在 Windows 中为 2 个字节。无论哪种方式wchar_t wc = L' 459' 都无效。描述您在使用什么系统以及您的目标是什么。
  • @Barmak Shemirani 它在 VS2019 上运行时没有任何警告。我的目标是“了解它在内存级别上的真正运作方式”。据我所知,我的代码没有问题,因为 'wchar_t' 在 VS 中是 'unsigened short' 的 typedef 并且字符字面前缀 'L' 表示值是 'wchar_t type' 所以我看不到任何我的代码有问题。

标签: c memory binary wchar-t


【解决方案1】:
_int64 x = 0x0041'0042'0043'0044ULL;
printf("%016llx\n", x);             //prints 0041004200430044

wchar_t wc;
wc = x;
printf("%04X\n", wc);               //prints 0044 as you expect

wc = L'\x0041\x0042\x0043\x0044';   //prints 0041, uses the first character
printf("%04X\n", wc);

如果您分配的整数值太大,编译器会采用适合 2 个字节的最大值 0x0044

如果您尝试将多个元素分配给一个元素,编译器会采用第一个适合的元素0x0041L'x' 是一个单一的宽字符。


VS2019 将对wchar_t wc = L' 459' 发出警告,除非警告级别设置为小于 3,但不建议这样做。使用警告级别 3 或更高级别。

wchar_t 是原始类型,而不是 unsigned shorttypedef,但它们在 Windows 中都是 2 个字节(在 linux 中是 4 个字节)

请注意,'abcd' 是 4 个字节。 L 前缀表示每个元素 2 个字节(在 Windows 中),因此 L'abcd' 是 8 个字节。

要查看wc 内部的内容,让我们看一下Unicode 字符L'X',它具有0x0058 的UTF-16 编码(类似于最高128 的ASCII 值)

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    wchar_t wc = L'X';
    wprintf(L"%c\n", wc);
    char buf[256];
    memcpy(buf, &wc, 2);
    for (int i = 0; i < 2; i++)
        printf("%02X ", buf[i] & 0xff);
    printf("\n");
    return 0;
}

输出将是58 00。不是00 58,因为Windows 运行在little-endian 系统上并且字节被翻转。

另一个奇怪的事情是 UTF16 使用 4 个字节来表示某些代码点。所以你会收到这条线的警告:

wchar_t wc = L'?';

相反,您想使用字符串:

wchar_t *wstr = L"?";
::MessageBoxW(0, wstr, 0, 0); //console may not display this correctly

这个字符串将是 6 个字节(2 个元素 + 空终止字符)

【讨论】:

  • 我无法完全理解这个答案的含义,我仍然无法弄清楚我的代码发生了什么,但无论如何感谢您的回答。如果您能详细说明 'wchar_t wc = L' 459' 执行时内存级别的确切数据过程,将不胜感激。
  • L' 459' 是 8 个字节,它不适合 wc 这是 2 个字节。编译器忽略 (459) 它只使用第一个字符 wc = L' '; 运行此代码:wprintf(L"[%c] %04X", wc, wc) 结果将是 "[ ] 0020"(空格及其 ASCII 值)。 -- 你在想const wchar_t *str = L"1234"吗?
  • 但是当我执行 "wchar_t wc = '459'" 时,"printf("%d", wc)" 的结果是 "13625" 这是原始 4byte 的 "last" 2bytes价值。我不明白的是,为什么 L“459”的 printf 显示“空格”部分,而“459”的 printf 显示“59”部分。完全相反。
  • wchar_t wc = '459' AND printf using %d = 显示 '13625'。 wchar_t wc = L' 459' AND printf using %d = 显示'32'。我想知道'459'和L'459'内存数据的区别。
  • 136250x3539,它对应于' 459' 中最后2 个字符的ASCII 值。将' 459' 分配给wchar_t 无效。编译器将 ' 459' 视为 4 字节整数,然后尝试将其与 wchar_t 匹配,如答案中所述。
猜你喜欢
  • 1970-01-01
  • 2020-01-24
  • 2020-01-08
  • 2016-11-19
  • 1970-01-01
  • 1970-01-01
  • 2010-12-09
  • 1970-01-01
  • 2012-01-26
相关资源
最近更新 更多