Первый отзыв C имеет unsigned char
, signed char
и char
: 3 различных типа. char
имеет тот же диапазон, что и unsigned char
или signed char
.
[Edit]
OP добавлен «Когда я читаю / записываю двоичный буфер», поэтому разделы, расположенные ниже (мои исходный пост) посвящен тому, "в чем разница между char*
и unsigned char*
" с примером случая без этой проблемы r / w. В этом разделе ....
Чтение / запись двоичного файла через <stdio.h>
может быть выполнено с помощью любой функции ввода-вывода, хотя чаще используется fread()/fwite()
.
Для байта ориентированных данных, все функции ввода / вывода ведут себя так, как если бы
Функции байтового ввода считывают символы из потока, как если бы они выполнялись последовательными вызовами функции fgetc
. C17dr § 7.21.3 11 Функции вывода байтов записывают символы в поток, как если бы они были последовательными вызовами функции fputc
. § 7.21.3 12
Итак, давайте посмотрим на эти два.
... функция fgetc
получает этот символ как unsigned char
... § 7.21.7.1 2 Функция fputc
записывает символ, указанный в c (преобразованный в unsigned char
) § 7.21.7.3 2
Таким образом, все операции ввода-вывода на самом низком уровне лучше всего рассматривать как чтение / writing unsigned char
.
Теперь, чтобы напрямую обращаться к
Когда я не должен использовать char*
и должен использовать unsigned char*
? (OP)
При записи указатели, такие как char*
, unsigned char*
или другие, могут использоваться в коде уровня OP, но основная функция вывода обращается к данным через unsigned char *
. Это не влияет на выполнение OP записи, кроме случаев, когда char
был закодирован как величина дополнения / знака единицы - код прерывания не будет обнаружен.
Аналогично с чтением, базовая функция ввода сохраняет данные через unsigned char *
, и ловушек не происходит. При чтении одного байта через int fgetc()
будут представлены значения в диапазоне unsigned char
, даже если char
равно со знаком .
Важность использования unsigned char*
по сравнению с char*
в чтение / запись двоичного буфера происходит не столько в самом вызове ввода / вывода (все это unsigned char *
доступ), сколько в настройке данных перед записью и интерпретации данных после чтения - см. memcmp()
ниже.
Когда я не должен использовать char*
и должен использовать unsigned char*
?
Хороший пример - код, связанный со строкой.
Хотя функции в <string.h>
использовать char*
в параметрах функции, реализация выполняется так, как если бы char
было unsigned char
, даже если char
равно подписано .
Для всех функций в В этом подпункте каждый символ должен интерпретироваться так, как если бы он имел тип unsigned char
(и поэтому каждое возможное представление объекта является допустимым и имеет другое значение). C17dr § 7.24.1 3
Таким образом, даже если char
является подписанным char
, такие функции, как int strcmp(char *a, char *b)
, выполняются так, как если бы int strcmp(unsigned char *a, unsigned char *b)
.
Это имеет значение, когда строка отличается подписью char c
и char d
со значениями разных знаков. Например, предположим, что c < 0, d > 0
// Доступ через char *
и char
подписан c unsigned char * c> d is false
Это приводит к знаку, отличному от знака strcmp()
, что влияет на сортировку строк.
// Incorrect code when `char` is signed.
int strcmp(const char *a, const char *b) {
while (*a == *b && *a) { a++; b++; }
return (*a > *b) - (*a < *b);
}
// Correct code when `char` is signed or unsigned, 2's complement or not
int strcmp(const char *a, const char *b) {
const char *ua = a;
const char *ub = b;
while (*ua == *ub && *ua) { ua++; ub++; }
return (*ua > *ub) - (*ua < *ub);
}
[Edit]
То же самое относится и к двоичным данные прочитаны и сравнены с memcmp()
.
В старых реализациях C, в которых не использовалось дополнение 2, могло быть 2 нуля: +0 и -0 (или ловушка).
+ 0 заканчивал строку при правильном просмотре как а unsigned char
. -0 не является нулевым символом для завершения строки, хотя как подписанный char
он имеет нулевое значение.
// Incorrect code when `char` is signed and not 2's complement.
// Conversion to `unsigned char` done too late.
int strcmp(const char *a, const char *b) {
while ((unsigned char)*a == (unsigned char)*b && (unsigned char)*a) { a++; b++; }
return ((unsigned char)*a > (unsigned char)*b) - ((unsigned char)*a < (unsigned char)*b);
}