Может быть, причина, по которой старшие три байта становятся 0xFFFFFF, требует немного большего объяснения?
Верхние три байта значения, напечатанного для * s, имеют значение 0xFF из-за расширения знака.
Значение char
, переданное в printf, увеличивается до int
перед вызовом printf
.
Это связано с поведением Си по умолчанию.
При отсутствии signed
или unsigned
компилятор может по умолчанию интерпретировать char
как signed char
или unsigned char
. Он последовательно один или другой, если явно не изменен с параметром командной строки или прагмой. В этом случае мы можем видеть, что это signed char
.
При отсутствии дополнительной информации (прототипы или отливки) C передает:
int
, поэтому char
, short
, unsigned char
unsigned short
преобразуются в int
. Он никогда не пропускает символ, неподписанный символ, подписанный символ как один байт, он всегда пропускает int
.
unsigned int
соответствует размеру int
, поэтому значение передается без изменений
Компилятор должен решить, как преобразовать меньшее значение в int
.
signed
значения: старшие байты int
являются знаками, расширенными от меньшего значения, которое фактически копирует верхний знаковый бит вверх для заполнения int
. Если старший бит меньшего значения со знаком равен 0, старшие байты заполнены 0. Если старший бит меньшего значения со знаком равен 1, старшие байты заполнены 1. Следовательно printf ("% x", * s ) печатает ffffffc2
unsigned
значения не расширены знаком, старшие байты int 'дополняются нулями'
Следовательно, причина в том, что C может вызывать функцию без прототипа (хотя компилятор обычно предупреждает об этом)
Так что вы можете написать и ожидать, что это запустится (хотя я надеюсь, что ваш компилятор выдаст предупреждения):
/* Notice the include is 'removed' so the C compiler does default behaviour */
/* #include <stdio.h> */
int main (int argc, const char * argv[]) {
signed char schar[] = "\x70\x80";
unsigned char uchar[] = "\x70\x80";
printf("schar[0]=%x schar[1]=%x uchar[0]=%x uchar[1]=%x\n",
schar[0], schar[1], uchar[0], uchar[1]);
return 0;
}
Это печатает:
schar[0]=70 schar[1]=ffffff80 uchar[0]=70 uchar[1]=80
Значение char
интерпретируется моим компилятором (gcc) для Mac как signed char
, поэтому компилятор генерирует код для подписи расширенного char
до int
перед вызовом printf.
Если значение знака со знаком имеет установленный старший (знаковый) бит (\ x80), преобразование в знак int
расширяет значение char
. Расширение знака заполняет верхние байты (в данном случае еще 3 байта, чтобы получить 4 байта int
) единицами, которые печатаются printf как ffffff80
Если значение знака со знаком имеет верхний бит (знак), очищенный (\ x70), преобразование в int
все еще расширяет значение char
. В этом случае знак равен 0, поэтому расширение знака заполняет старшие байты нулями, которые printf печатает как 70
В моем примере показан случай, когда значение равно unsigned char
. В этих двух случаях значение не является расширенным знаком, поскольку оно равно unsigned
. Вместо этого они расширяются до int с 0 дополнением. Может показаться, что printf печатает только один байт, потому что три соседних байта значения будут равны 0. Но он печатает весь int
, бывает, что это значение 0x00000070 и 0x00000080, потому что значения unsigned char
были преобразованы в
int
без расширения знака.
Вы можете заставить printf печатать только младший байт целого числа, используя подходящее форматирование (% hhx), поэтому это правильно печатает только значение в исходном символе:
/* Notice the include is 'removed' so the C compiler does default behaviour */
/* #include <stdio.h> */
int main (int argc, const char * argv[]) {
char schar[] = "\x70\x80";
unsigned char uchar[] = "\x70\x80";
printf("schar[0]=%hhx schar[1]=%hhx uchar[0]=%hhx uchar[1]=%hhx\n",
schar[0], schar[1], uchar[0], uchar[1]);
return 0;
}
Это печатает:
schar[0]=70 schar[1]=80 uchar[0]=70 uchar[1]=80
потому что printf интерпретирует% hhx для обработки int как unsigned char
. Это не меняет того факта, что символ был расширен до int перед вызовом printf. Это только способ сообщить printf, как интерпретировать содержимое int.
В некотором смысле, для signed char *schar
значение %hhx
выглядит немного вводящим в заблуждение, но формат '% x' все равно интерпретирует int
как unsigned
, и (с моим printf) формат не существует выведите hex для знаковых значений (ИМХО это было бы сбивающим с толку).
К сожалению, ISO / ANSI / ... не публикуют свободно наши стандарты языков программирования, поэтому я не могу указать на спецификацию, но поиск в Интернете может привести к рабочим черновикам. Я не пытался их найти. Я бы порекомендовал «C: Справочное руководство» Сэмюэля П. Харбисона и Гая Л. Стила в качестве более дешевой альтернативы документу ИСО.
НТН