Три типа char в Си
2025-06-05 21:48Копаясь в семантике компилятора, чего только не узнаешь. Выясняется, в языке Си не два типа "signed char" и "unsigned char", а три. Тип просто "char" отличается от первых двух, хоть и ведет себя как один из них. Обнаружить этот факт можно, попытавшись повторно определить typedef:
С другими типами такого выпендрёжа нет. Компилируется без ошибок:$ cat foo.c
typedef char foo;
typedef signed char foo;
$ cc -c foo.c
typedef-char.c:2:21: error: typedef redefinition with different types ('signed char' vs 'char')
2 | typedef signed char foo;
| ^
typedef-char.c:1:14: note: previous definition is here
1 | typedef char foo;
| ^
1 error generated.
$ cat bar.c
typedef int bar;
typedef signed int bar;
$ cc -c bar.c

no subject
Date: 2025-06-06 05:29 (UTC)Если битовое поле определено через просто int, то оно может быть signed int или unsigned int.
no subject
Date: 2025-06-06 06:26 (UTC)no subject
Date: 2025-06-06 06:43 (UTC)foo(unsigned char)
А в учебниках Си++ я встречал упоминание о трёх типах char в главах о перегрузке функций для разных типов.
no subject
Date: 2025-06-06 07:34 (UTC)Много чего можно не знать, если не интересоваться.
no subject
Date: 2025-06-06 07:41 (UTC)Си вольно кастит типы
Для вызывающей стороны одинаковы все 3 варианта:
foo(unsigned char)
foo(char)
foo(signed char)
А так как можно определить только одну функцию с данным именем, пишут как проще, т.е. `char`,
no subject
Date: 2025-06-06 07:52 (UTC)$ cat putbyte.c void putbyte(char x); int main() { putbyte(255); } $ cc putbyte.c cc -c putbyte.c putbyte.c:5:13: warning: implicit conversion from 'int' to 'char' changes value from 255 to -1 [-Wconstant-conversion] 5 | putbyte(255); | ~~~~~~~ ^~~ 1 warning generated.no subject
Date: 2025-06-06 08:13 (UTC)Проверил в GCC и VC c опцией -Wall, код скомпилился без Warning
На практике же, если работать с байтами, используют `uint8_t`, и каким typedef он определен - вопрос реализации.
Если работают с текстом, `signed char`, `unsigned char` только ухудшают читаемость
А `putbyte(255);` - это пример как не надо делать, смешивать типы.
no subject
Date: 2025-06-06 08:38 (UTC)вот тут сравнение: https://gcc.godbolt.org/z/zrcvrdxTh
То есть чары (все 3 варианта) ведут себя чуть более злобно в некоторых ситуациях, и компилятор исходит из предположения, что в приведенном примере data и result это может быть одна и та же область памяти. Из за этого приходится делать дополнительный финт ушами. Сам пример с++ный, но по идее в си компилятор должен себя вести так же.
Это предположение можно отключить написав "char * __restrict data", но сишно ли слово __restrict, и только сиплюсплюсно - я не знаю.
В с++20 завели менее злобный char8_t, который означает, что указатели совершенно точно указывают на разные места. История про char8_t уже не про си.
no subject
Date: 2025-06-06 10:53 (UTC)no subject
Date: 2025-06-06 15:04 (UTC)Лениво проверять.
То есть это так в спецификации определено а не просто bug/feature компилятора?
no subject
Date: 2025-06-06 15:15 (UTC)"Я вам больше скажу": 30 лет жду нормальных классов для чисел в С++, скорее всего так и умру не дождавшись.
no subject
Date: 2025-06-06 19:45 (UTC)typedef unsigned char uint8_t;
typedef signed char int8_t;
И можно догадаться, зачем тип char от них отличается. Чтобы не путали.
no subject
Date: 2025-06-06 19:49 (UTC)no subject
Date: 2025-06-06 19:52 (UTC)$ cat foo.c typedef char *foo; typedef signed char *foo; $ cc -c foo.c foo.c:2:22: error: typedef redefinition with different types ('signed char *' vs 'char *') 2 | typedef signed char *foo; | ^ foo.c:1:15: note: previous definition is here 1 | typedef char *foo; | ^ 1 error generated.Это не прихоть конкретного компилятора. Различие типов char задано в стандарте языка.
no subject
Date: 2025-06-06 19:54 (UTC)C++ может сойти со сцены за следующий десяток лет, уступив место Rust.
no subject
Date: 2025-06-06 20:02 (UTC)https://stackoverflow.com/questions/74180152/why-is-char-different-from-both-signed-char-and-unsigned-char
no subject
Date: 2025-06-06 20:22 (UTC)в MSVC есть встроеный тип __int8
Думаю, signed/unsigned появились из-за неопределенности в начальном стандарте (K&R) насчет `char`
Часть реализаций делала знаковым, часть наоборот
Потом появились опции компиляторов, позволяющие задать.
И стандартизировать знаковость `char` было уже поздно.
no subject
Date: 2025-06-06 21:08 (UTC)