#c
#c
Вопрос:
Я пытаюсь прочитать файл bitmap, байт за байтом, и у меня есть цикл, который выполняется до тех пор, пока не будет достигнут EOF. Для этого у меня есть переменная, объявленная как unsigned int
, которая хранит каждый байт. Цикл останавливается, когда эта переменная равна EOF
.
Интересный момент: если я объявляю свою переменную как unsigned int
, она работает. Однако, если я объявляю свою переменную как unsigned short int
, цикл выполняется вечно, потому что он никогда не находит EOF
.
#include <stdio.h>
int main()
{
FILE *file;
unsigned int currentByte;
file = fopen("/home/stanley/Desktop/x.bmp", "rb");
while ((currentByte = fgetc(file)) != EOF) {
printf("%d n", currentByte);
}
fclose(file);
return 0;
}
Приведенный выше код — это код, который я пишу. Если файл имеет размер 90B, на экране выводится 90 байт.
Однако, по какой-то причине, когда я меняю его на unsigned short int currentByte
, цикл продолжает выполняться вечно. Это как если бы currentByte
никогда не было равно EOF
.
Я где-то читал, что EOF
содержит отрицательное значение (-1). Но если EOF
значение отрицательное, почему оно работает, когда я использую только unsigned int
и почему оно выдает ошибку при использовании unsigned short int
? Теоретически, не должна ли проблема быть связана с unsigned
самим short
, а не с,,? Это unsigned, который не может хранить отрицательные значения.
Извините, если это очень глупый вопрос. Я пытаюсь лучше понять, как работают биты и байты, и некоторые концепции пока могут быть для меня странными.
Я компилирую его в следующей среде:
- ОС: Ubuntu 18.04 x64
- GCC: gcc (Ubuntu 7.3.0-27ubuntu1 ~ 18.04) 7.3.0
Заранее спасибо. 🙂
Ответ №1:
Если размер int
больше размера short
, то вы столкнетесь с этой проблемой.
Давайте предположим, что EOF
имеет тип int
и содержит значение -1. Для примера давайте также предположим, что это int
32-разрядное значение, в то время как short
это 16-разрядное значение.
В этом случае, если fgetc
возвращает EOF
значение, оно будет иметь значение 0xFFFFFFFF, если принимать его за unsigned int
. При сравнении его с EOF
(type int
) целое число со знаком -1 будет преобразовано в значение без знака 0xFFFFFFFF. Эти два значения равны, поэтому сравнение работает так, как ожидалось.
Однако значение, EOF
возвращаемое fgetc
, принимаемое за unsigned short
, будет иметь значение 0xFFFF. Поскольку размер unsigned short
меньше размера int, при сравнении этого значения с EOF
, unsigned short
0xFFFF будет преобразован в int
со значением 0x0000FFFF (дополнительные цифры показаны для наглядности). Поскольку -1 не равно 0xFFFF для 32-разрядного значения, это сравнение всегда не равно, и цикл не остановится.
Тот факт, что fgetc
возвращает int
намек на то, что вы должны сохранить его как этот тип, поскольку в противном случае вы отбросите некоторую информацию или вызовете путаницу в сравнениях.
Комментарии:
1. Одна важная деталь заключается в том, что
unsigned short
она будет повышена доint
по сравнению сEOF
. Но из-за неподписанности is будет расширен до нуля и станет0x0000FFFF
.signed short
Снова сработал бы, поскольку он был бы расширен по знаку и стал0xFFFFFFFF
2. В вашем ответе не упоминаются «обычные арифметические преобразования». Если unsigned short меньше
int
, он будет преобразован вint
и 0xFFFF != -13. Итак, из того, что я понял, первое, что происходит: переменные int принимают как отрицательные, так и положительные значения. Для работы с дополнением two он резервирует свой первый бит, чтобы указать, какой сигнал имеет число (положительный или отрицательный). Как только
fgetc()
возвращается-1
значение (которое было бы11111111
в 8-разрядном числе) и преобразуется в unsigned, оно становится255
. Правильно ли это понимание?4. Учитывая, что понимание в последнем комментарии правильное, проблема возникает, когда мне нужно сохранить это
255
вshort
переменной (предположим, что в ней всего 4 бита ). Я не могу сохранить255
в это, но15
самое большее. Итак, в этом случае у меня было бы условие цикла a15 != 255
, которое всегда верно. Как указано в этом и других ответах. Если оба понимания верны, то я определенно думаю, что наконец-то понял это! =)
Ответ №2:
Вы должны использовать тип, int
чтобы соответствовать тому, что fgetc
возвращает, а не unsigned int
. Причина, по которой условие остановки цикла работает с unsigned int
, заключается не в том, что значение всегда отрицательное, а в том, что, когда !=
оператор используется с unsigned
и signed
операндами одинакового ранга, оба получают повышение до unsigned
перед сравнением. Присвоение EOF
результата fgetc
currentByte
и продвижение EOF
к unsigned
дают одинаковый результат, и, таким образом, они сравниваются одинаково.
Комментарии:
1. Спасибо! Я бы никогда не подумал, что сравнение между
unsigned
иsigned
приведет к преобразованию обоих вunsigned
. Где я могу найти информацию такого типа, чтобы узнать больше подробностей о C?
Ответ №3:
Когда вы преобразуете целое число со знаком в целое число без знака (что происходит, когда EOF
присваивается целочисленной переменной без знака), результат преобразуется в целое число без знака путем добавления UINT_MAX 1
. Итак, если EOF
есть -1
, это значение становится UINT_MAX
.
И UINT_MAX
может правильно вписываться только в unsigned int
и не unsigned short
. И результат этого конкретного преобразования определяется реализацией, и поэтому поведение программы будет зависеть от него.
Обратите внимание, что fgetc
функция возвращает int
, поэтому вы должны использовать int
переменную для сохранения ее значения.
Комментарии:
1. Ну, в этом случае это «следует использовать» для,
unsignef int
посколькуunsigned int
это работает без какой-либо неопределенности или определенного реализацией поведения, которое заметно отличалось бы отint