В C, что произойдет, если я помещу «последовательные символы wchar_t» в переменную wchar_t?

#c #memory #binary #wchar-t

Вопрос:

 #include <stdio.h>  wchar_t wc = L' 459'; printf("%d", wc); //result : 32  

Я знаю, что «пробел» — это «десятичное число 32» в таблице кодов ASCII.

Чего я не понимаю, так это того, насколько я знаю, что если для переменной недостаточно места для хранения значения, значение будет «последними цифрами» исходного значения.

Например, если я помещу двоичное значение «1100 1001 0011 0110» в однобайтовую переменную, это будет «0011 0110», который является «последним байтом» исходного двоичного значения.

Но приведенный выше код показывает «первый байт» исходного значения.

Я хотел бы знать, что происходит на уровне памяти, когда я выполняю приведенный выше код.

Комментарии:

1. Согласно спецификации C, » Значение широкой символьной константы, содержащей более одного многобайтового символа или одного многобайтового символа, который сопоставляется нескольким элементам расширенного набора символов выполнения или содержит многобайтовый символ или escape-последовательность, не представленные в расширенном наборе символов выполнения, определяется реализацией».

2. Например, Microsoft MSVC (cl) и GNU GCC (gcc) wc имеют разные значения. MSVC устанавливает его в L' ' значение, а GCC устанавливает его в L'9' значение . Оба компилятора выдадут предупреждение об усечении, если вы установите уровень предупреждения достаточно высоким.

3. wchar_t составляет 4 байта в Linux и 2 байта в Windows. В любом случае wchar_t wc = L' 459' это недопустимо. Опишите, над какой системой вы работаете и какова ваша цель.

4. @Barmak Shemirani Он запускается без какого-либо предупреждения на VS2019. Моя цель — «узнать, как это действительно работает на уровне памяти». Насколько я узнал, в моем коде нет проблем, потому что «wchar_t» имеет тип «без подписи короткий» в VS, а буквенный префикс символов » L «представляет значение » тип wchar_t», поэтому я не вижу никаких проблем в своем коде.

Ответ №1:

 _int64 x = 0x0041'0042'0043'0044ULL; printf("6llxn", x); //prints 0041004200430044  wchar_t wc; wc = x; printf("Xn", wc); //prints 0044 as you expect  wc = L'x0041x0042x0043x0044'; //prints 0041, uses the first character printf("Xn", wc);  

Если вы присваиваете слишком большое целочисленное значение, компилятор принимает максимальное значение 0x0044 , которое помещается в 2 байта.

Если вы попытаетесь назначить несколько элементов одному элементу, компилятор выберет первый элемент 0x0041 , который подходит. L'x' это означает быть одним широким персонажем.


VS2019 выдаст предупреждение для wchar_t wc = L' 459' , если уровень предупреждения не установлен ниже 3, но это не рекомендуется. Используйте предупреждение уровня 3 или выше.

wchar_t является примитивным типом, а не typedef для unsigned short , но оба они составляют 2 байта в Windows (4 байта в Linux).

Обратите внимание, что 'abcd' это 4 байта. L Префикс указывает 2 байта на элемент (в Windows), то L'abcd' есть 8 байт.

Чтобы увидеть , что находится внутри wc , давайте посмотрим на символ Юникода L'X' , который имеет кодировку UTF-16 0x0058 (аналогично значениям ASCII до 128).

 #include <stdlib.h> #include <stdio.h> #include <string.h>  int main(void) {  wchar_t wc = L'X';  wprintf(L"%cn", wc);  char buf[256];  memcpy(buf, amp;wc, 2);  for (int i = 0; i < 2; i  )  printf("X ", buf[i] amp; 0xff);  printf("n");  return 0; }  

Результат будет таким 58 00 . Это не 00 58 потому, что Windows работает в системах с малым числом пользователей, и байты переворачиваются.

Еще одна странная вещь заключается в том, что UTF16 использует 4 байта для некоторых кодовых точек. Таким образом, вы получите предупреждение для этой строки:

 wchar_t wc = L'😀';  

Вместо этого вы хотите использовать строку:

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

Эта строка будет состоять из 6 байтов (2 элемента нулевой завершающий символ).

Комментарии:

1. Я не могу полностью понять, что означает этот ответ, и я все еще не могу понять, что происходит с моим кодом, но в любом случае спасибо за ответ. Если бы вы могли подробно описать точный процесс обработки данных на уровне памяти при выполнении ‘wchar_t wc = L’ 459′, это было бы очень полезно.

2. L' 459' это 8 байт, это не вписывается в wc то, что составляет 2 байта. Компилятор игнорирует (459), он просто использует первый символ wc = L' '; , запускающий этот код: wprintf(L"[%c] X", wc, wc) результатом будет "[ ] 0020" (пустое пространство и его значение ASCII). — Ты о чем думаешь const wchar_t *str = L"1234" ?

3. Но когда я делаю «wchar_t wc = ‘459’», результатом «printf(«%d», wc)» является «13625», что является «последними» 2 байтами исходного значения 4 байта. Чего я не понимаю, так это почему printf L» 459″ показывает часть «пробел», в то время как printf » 459 «показывает часть «59». Они совершенно противоположны друг другу.

4. wchar_t wc = ‘459’ И printf с использованием %d = показывает ‘13625’. wchar_t wc = L’ 459′ И printf с использованием %d = показывает «32». Я хотел бы знать разницу в данных памяти между » 459 » и «459».

5. 13625 то есть 0x3539 , это соответствует значениям ASCII для последних 2 символов в ' 459' . Присвоение ' 459' wchar_t недопустимо. Компилятор обрабатывает ' 459' как 4-байтовое целое число, затем пытается сопоставить его с wchar_t тем, как описано в ответе.