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 (или строковый массив, указатель и т.д.)