Сравнение неподписанных char и EOF
при компиляции следующего кода он переходит в бесконечный цикл:
int main()
{
unsigned char ch;
FILE *fp;
fp = fopen("abc","r");
if(fp==NULL)
{
printf("Unable to Open");
exit(1);
}
while((ch = fgetc(fp))!=EOF)
printf("%c",ch);
fclose(fp);
printf("n",ch);
return 0;
}
компилятор gcc также дает предупреждение о компиляции
abc.c:13:warning: comparison is always true due to limited range of data type
код работает нормально, когда unsigned char
заменить на char
или int
как и ожидалось, т. е. он завершается.
Но код также работает нормально для unsigned int
как хорошо.
как я прочитал в EOF
определяется как -1
на stdio.h
тогда почему этот код не работает для unsigned char, но работает нормально для unsigned int.
6 ответов
золотое правило для написания этой строки
while ((ch = fgetc(stdin)) != EOF)
ch
должно быть int
.Ваш милый трюк создания ch
без знака не удается, потому что EOF
подписанное количество int.
Хорошо, давайте теперь пойдем в глубину......
Шаг 1:
ch=fgetc(fp)
fgetc()
возвращает -1
(подписанного int
). По золотым правилам C ch
получает последний октет битов, который все 1
. И, следовательно, значение 255
. Байтовый шаблон ch
после исполнения
ch = fgetc(fp);
было бы таким образом
11111111
Шаг 2:
ch != EOF
теперь EOF
это целое число со знаком и ch
это unsigned char
...
ch
преобразуется в большой размер int
перед сравнение, поэтому его байтовый шаблон теперь
00000000000000000000000011111111 = (255)10
пока EOF
is
11111111111111111111111111111111 = (-1)10
они не могут быть равны....... Следовательно, оператор для управления следующим while-loop
while ((ch = fgetc(stdin)) != EOF)
никогда не будет false ...
и, следовательно, бесконечный цикл .
происходит несколько неявных преобразований. Они не имеют отношения к конкретному предупреждению, но я включил их в этот ответ, чтобы показать, что компилятор действительно делает с этим выражением.
- ch в вашем примере имеет тип unsigned char.
- EOF гарантированно будет типа int (C99 7.19.1).
таким образом, выражение эквивалентно
(unsigned char)ch != (int)EOF
на правила продвижения целое число в C будет неявно преобразовать char в unsigned беззнаковый инт:
(unsigned int)ch != (int)EOF
затем правила балансировки (он же обычные арифметические преобразования) в C неявно преобразует int в unsigned int, потому что каждый операнд должен иметь один и тот же тип:
(unsigned int)ch != (unsigned int)EOF
на вашем компиляторе EOF, скорее всего, -1:
(unsigned int)ch != (unsigned int)-1
который, предполагая 32-разрядный процессор, совпадает с
(unsigned int)ch != 0xFFFFFFFFu
символ никогда не может иметь такое высокое значение, следовательно, предупреждение.
Я тоже столкнулся с этой проблемой. Мое решение-использовать feof().
unsigned int xxFunc(){
FILE *fin;
unsigned char c;
fin = fopen("...", "rb");
if(feof(fin) != 0) return EOF;
c = fgetc(fin);
fclose(fin);
...
}
и вы можете определить переменную int для сравнения с EOF. Например:
int flag = xxFunc();
while(flag != EOF) {...}
это работает для меня.
**ВАЖНОЕ ОБНОВЛЕНИЕ***
после использования метода, о котором я упоминал ранее, я обнаружил серьезную проблему. feof () не является хорошим способом разорвать цикл while. Вот причина для этого. http://www.gidnetwork.com/b-58.html
поэтому я нахожу лучший способ сделать это. Для этого я использую переменную int. здесь:
int flag;
unsigned char c;
while((flag = fgetc(fin)) != EOF)
{
//so, you are using flag to receive, but transfer the value to c later.
c = flag;
...
}
после моего теста, это работает.
вам нужно использовать int
fgetc () возвращает int специально, чтобы он мог указать конец файла
Он отлично работает со знаком char, потому что EOF (-1) находится в диапазоне, но он сломается, если Вы читаете в char со значением больше 127.
используйте int, приведите его к символу после того, как вы проверили EOF
когда вы сравниваете unsigned int со signed int, он преобразует подписанный int в unsigned int и сравнивает их. Следовательно, когда вы читаете файл с unsigned int 'ch', чтение EOF дает вам 2^32+1 (на 4-байтовой машине int), и при сравнении его с EOF он преобразует EOF в unsigned, который также 2^32+1 и, следовательно, программа останавливается!
Если вы используете unsigned char ch, когда вы читаете файл, чтение EOF возвращает 2^32+1, и это будет приведено к unsigned char, который усекает значение до первых 8 бит (на 1-байтовой машине char) и дает вам выход 255. Следовательно, вы сравниваете 255 и 2^32+1, вызывая бесконечный цикл.
проблема здесь заключается в усечении перед сравнением.
Если вы используете
while((ch = fgetc(fp))!=(unsigned char)EOF)
printf("%c",ch);
программа будет работать нормально!
предупреждение корпии производится с помощью такого рода реализации
сравнение типа 'char' с EOF
// read the data in a buffer
611 ch = getc(csv_file);
612 while (ch != EOF)
исправления:
// read the data in a buffer
while ((ch = getc(csv_file)) != EOF)