TL;DR:
Почему нет беззнаковых типов wchar_t и подписанных wchar_t?
Потому что средства обработки расширенных символов C были определены таким образом, что они не нужны.
Более подробно,
Подпись char не стандартизирована.
Чтобы быть точным, реализация должна определять char так, чтобы он имел тот же диапазон, представление и поведение, что и знаковый char или беззнаковый char. (C2011, 6.2.5/15)
Следовательно, существуют типы signed char
и unsigned char
.
Следовательно, подразумевает причинно-следственную связь, которую было бы трудно четко аргументировать, но, безусловно, signed char
и unsigned char
более подходят, когда вы хотите обрабатывать числа, а не символы.
Поэтому функции, работающие с одним символом, должны использовать тип аргумента, который может содержать как знаковые символы, так и беззнаковые символы.
Нет, совсем нет. Стандартные библиотечные функции, работающие с отдельными символами, могут быть легко определены в терминах типа char
, независимо от того, является ли этот тип подписанным, поскольку реализация библиотеки действительно знает его подписанность. Если бы это было проблемой, то это в равной степени относилось бы и к строковым функциям - char
было бы бесполезно.
Ваш пример getchar()
неуместен. Он возвращает int
, а не тип символа, потому что он должен иметь возможность возвращать индикатор ошибки, который не соответствует ни одному символу. Более того, представленный вами код не соответствует сопроводительному предупреждающему сообщению: он содержит преобразование из int
в unsigned char
, но не преобразование из char
в unsigned char
.
Некоторые другие функции обработки символов принимают параметры int
или возвращают значения типа int
как для совместимости с getchar()
и другими функциями stdio, так и по историческим причинам. В прежние времена вы вообще не могли передать char
— оно всегда повышалось до int
, и это то, что функции будут (и должны) принимать. В дальнейшем нельзя изменить тип аргумента, несмотря на эволюцию языка.
Кроме того, стандарт ISO C90, где было введено wchar_t
, не говорит ничего конкретного о представлении wchar_t
.
C90 больше не актуален, но, без сомнения, он говорит что-то очень похожее на C2011 (7.19/2), который описывает wchar_t
как
целочисленный тип, диапазон значений которого может представлять различные коды для всех членов самого большого расширенного набора символов, указанного среди поддерживаемых языков [...].
Ваши цитаты из справочника glibc не являются авторитетными, за исключением, возможно, только для glibc. В любом случае они кажутся комментариями, а не уточнениями, и непонятно, почему вы их поднимаете. Хотя, конечно, по крайней мере первое верно. Ссылаясь на стандарт, если все члены самого большого расширенного набора символов, указанного среди локалей, поддерживаемых данной реализацией, могут поместиться в char
, тогда эта реализация может определить wchar_t
как char
. Раньше такие реализации были гораздо более распространены, чем сегодня.
Вы задаете несколько вопросов:
Частное сообщение показывает, что реализации разрешено поддерживать расширенные символы только со значением ›=0 (независимо от подписания wchar_t
). Кто-нибудь знает, что это значит?
Я думаю, это означает, что тот, кто сообщил вам об этом, не знает, о чем он говорит, или, возможно, то, о чем он говорит, отличается от требований, предъявляемых стандартом C. Вы обнаружите, что на практике наборы символов определяются только неотрицательными кодами символов, но это не является ограничением, налагаемым стандартом C.
Означает ли тонкий, что когда wchar_t
является 16-битным типом (например), мы можем использовать только 15 бит для хранения значения широкого символа?
Стандарт C не говорит и не подразумевает этого. Вы можете сохранить значение любого поддерживаемого символа в файле wchar_t
. В частности, если реализация поддерживает набор символов, содержащий коды символов, превышающие 32767, вы можете сохранить их в файле wchar_t
.
Другими словами, верно ли, что расширенный знак wchar_t является допустимым значением?
Стандарт C не говорит и не подразумевает этого. Здесь даже не сказано, является ли wchar_t
знаковым типом (если нет, то знаковое расширение для него бессмысленно). Если это знаковый тип, то нет гарантии, что расширение знака значения, представляющего символ в некотором поддерживаемом наборе символов (значение, в принципе, может быть отрицательным), приведет к созданию значения, которое также представляет символ этого символа. набор или любой другой поддерживаемый набор символов. То же самое относится и к добавлению 1 к значению wchar_t
.
Кроме того, частное сообщение показывает, что стандарт требует, чтобы любое допустимое значение wchar_t
было представлено wint_t
. Это правда?
Это зависит от того, что вы подразумеваете под действительным. Стандарт говорит, что wint_t
представляет собой целочисленный тип, неизменный по умолчанию, для продвижения аргументов, который может содержать любое значение, соответствующее членам расширенного набора символов, а также по крайней мере одно значение, не соответствующее ни одному члену расширенного набора символов.
(C2011, 7.29.1/2)
wchar_t
должен содержать любое значение, соответствующее элементу расширенного набора символов, в любом поддерживаемом языковом стандарте. wint_t
также должен иметь возможность хранить все эти значения. Однако может случиться так, что wchar_t
способен представлять значения, которые не соответствуют ни одному символу ни в одном из поддерживаемых наборов символов. Такие значения допустимы в том смысле, что тип может их представлять. wint_t
не требуется для представления таких значений.
Например, если самый большой расширенный набор символов любой поддерживаемой локали использует коды символов до 32767, но не более, то реализация может свободно реализовать wchar_t
как 16-битное целое число без знака, а wint_t
как 16-битное целое число со знаком. Значения, представляемые wchar_t
, которые не соответствуют расширенным символам, затем не могут быть представлены wint_t
(но wint_t
по-прежнему имеет много кандидатов на требуемое значение, которое не соответствует ни одному символу).
Что касается функций классификации символов и широких символов, единственный ответ состоит в том, что различия просто возникают из-за разных спецификаций. Функции классификации char
определены для работы с теми же значениями, что и функция getchar()
определена для возврата — либо -1, либо символьное значение, преобразованное, если необходимо, в unsigned char
. С другой стороны, функции классификации широких символов принимают аргументы типа wint_t
, которые могут представлять значения всех широких символов без изменений, поэтому преобразование не требуется.
Вы утверждаете в связи с этим, что
Здесь нужно использовать iswlower((unsigned wchar_t)wc)
, но нет типа unsigned wchar_t
.
Нет и может быть. Вам не нужно преобразовывать аргумент wchar_t
в iswlower()
в любой другой тип, и, в частности, вам не нужно преобразовывать его в явно беззнаковый тип. В этом отношении функции классификации широких символов не аналогичны функциям классификации обычных символов, поскольку они были разработаны задним числом. Что касается unsigned wchar_t
, C не требует существования такого типа, поэтому переносимый код не должен его использовать, но он может существовать в некоторых реализациях.
Относительно обновления, добавленного к вопросу:
Говорят ли стандарты, что приведение к unsigned int и к int в следующих двух программах гарантированно будет правильным? (Я просто заменил wint_t и wchar_t на их фактическое значение в glibc)
В стандарте ничего подобного не говорится о соответствующих реализациях в целом. Я предполагаю, однако, что вы хотите конкретно спросить о соответствующих реализациях, для которых wchar_t
равно int
, а wint_t
равно unsigned int
.
В такой реализации ваша первая программа ошибочна, поскольку не учитывает возможность того, что getwchar()
возвращает WEOF
. Преобразование WEOF
в тип wchar_t
, если это не приводит к возникновению сигнала, не гарантирует получения значения, соответствующего любому расширенному символу. Таким образом, передача результата такого преобразования в putwchar()
не демонстрирует определенного поведения. Более того, если WEOF
определено с тем же значением, что и UINT_MAX
(которое не может быть представлено int
), то преобразование этого значения в int
имеет поведение, определяемое реализацией, независимо от вызова putwchar()
.
С другой стороны, я думаю, что ключевой момент, с которым вы боретесь, заключается в том, что если значение, возвращаемое getwchar()
в первой программе, не равно WEOF
, то оно гарантированно не изменится при преобразовании в wchar_t
. Ваша первая программа будет выполняться так, как и предполагалось в этом случае, но приведение к int
(или wchar_t
) не нужно.
Точно так же вторая программа верна при условии, что широкосимвольный литерал соответствует символу в применимом расширенном наборе символов, но преобразование не требуется и ничего не меняет. Значение wchar_t
такого литерала гарантированно может быть представлено типом wint_t
, поэтому приведение меняет тип своего операнда, но не значение. (Но если литерал не соответствует символу в расширенном наборе символов, то поведение определяется реализацией.)
С третьей стороны, если ваша цель состоит в том, чтобы написать строго соответствующий код, то правильным решением и предполагаемым режимом использования этих конкретных широкосимвольных функций будет следующее:
#include <locale.h>
#include <wchar.h>
int main(void)
{
setlocale(LC_CTYPE, "en_US.UTF-8");
wint_t wc = getwchar();
if (wc != WEOF) {
// No cast is necessary or desirable
putwchar(wc);
}
}
и это:
#include <locale.h>
#include <wchar.h>
#include <wctype.h>
int main(void)
{
setlocale(LC_CTYPE, "en_US.UTF-8");
wchar_t wc = L'ÿ';
// No cast is necessary or desirable
if (iswlower(wc)) return 0;
return 1;
}
person
John Bollinger
schedule
23.11.2016