Wchar_t vs wint_t
Это вопрос ANSI C. У меня есть следующий код.
#include <stdio.h>
#include <locale.h>
#include <wchar.h>
int main()
{
if (!setlocale(LC_CTYPE, "")) {
printf( "Can't set the specified locale! "
"Check LANG, LC_CTYPE, LC_ALL.\n");
return -1;
}
wint_t c;
while((c=getwc(stdin))!=WEOF)
{
printf("%lc",c);
}
return 0;
}
Мне нужна полная поддержка UTF-8, но даже на этом простейшем уровне я могу как-то улучшить это? Почему wint_t
используется, а не wchar
, с соответствующими изменениями?
Ответы
Ответ 1
UTF-8
является одним из возможных кодировок для Unicode. Он определяет 1, 2, 3 или 4 байта на символ. Когда вы читаете его через getwc()
, он будет извлекать от одного до четырех байтов и составлять из них один код символа Unicode, который будет вписываться в wchar
(который может быть 16 или даже 32 бит в ширину, в зависимости от платформы).
Но поскольку значения Unicode соответствуют всем значениям от 0x0000
до 0xFFFF
, нет никаких значений для возврата условий или кодов ошибок. (Некоторые из них указали, что Unicode больше 16 бит, что true, в тех случаях, когда используются суррогатные пары. Но дело здесь в том, что Unicode использует все доступные значения, не оставляя ни одного для EOF.)
Различные коды ошибок включают EOF (WEOF
), который сопоставляется с -1. Если бы вы положили возвращаемое значение getwc()
в wchar
, не было бы способа отличить его от символа Unicode 0xFFFF
(который, кстати, зарезервирован в любом случае, но я отвлекся).
Таким образом, ответ заключается в использовании более широкого типа, wint_t
(или int
), который содержит не менее 32 бит. Это дает более низкие 16 бит для реального значения, и все, что бит, установленный за пределами этого диапазона, означает что-то, кроме произошедшего символа.
Почему бы нам не использовать wchar
вместо wint
? Большинство связанных с строкой функций используют wchar
, потому что на большинстве платформ он равен размеру wint
, поэтому строки имеют меньший размер памяти.
Ответ 2
wint_t
может хранить любое допустимое значение wchar_t
. wint_t
также может принимать результат оценки макроса WEOF
(обратите внимание, что wchar_t
может быть слишком узким, чтобы содержать результат).
Ответ 3
Как @musiphil так хорошо вложил в свой комментарий, который я попытаюсь расширить здесь, есть концептуальная разница между wint_t
и wchar_t
.
Их разные размеры - это технический аспект, который вытекает из того, что у каждого есть очень четкая семантика:
-
wchar_t
достаточно велика для хранения символов или кодовых точек, если вы предпочитаете. Таким образом, они неподписанны. Они аналогичны char
, которые были практически на всех платформах ограничены 8-битными 256 значениями. Так что переменные строк с широкими char являются, естественно, массивами или указателями этого типа.
-
Теперь введите строковые функции, некоторые из которых должны иметь возможность возвращать любые wchar_t
плюс дополнительные статусы. Поэтому их тип возврата должен быть больше, чем wchar_t
. Таким образом, используется wint_t
, который может выражать любые широкие char, а также WEOF
. Будучи статусом, он также может быть отрицательным (и обычно есть), поэтому wint_t
, скорее всего, подписан. Я говорю "возможно", потому что стандарт C не предусматривает его. Но независимо от знака значения статуса должны находиться вне диапазона wchar_t
. Они полезны только как возвратные значки и никогда не предназначены для хранения таких символов.
Аналогия с "классическим" char
и int
велика, чтобы устранить любую путаницу: строки не относятся к типу int []
, они char var[]
(или char *var
). И не потому, что char
является "половиной размера int
", а потому, что это строка.
Ваш код выглядит правильно: c
используется для проверки результата getwch()
, поэтому он wint_t
. И если его значение не WEOF
, как ваши теги if
, тогда можно безопасно назначить его символу wchar_t
(или строковый массив, указатель и т.д.)