Сравнение неподписанных 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)