Сравнение неподписанного символа и EOF - PullRequest
7 голосов
/ 21 декабря 2011

когда следующий код скомпилирован, он входит в бесконечный цикл:

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, тогда почему этот код не работает для беззнакового символа, но работает нормально для беззнакового целого.

Ответы [ 6 ]

8 голосов
/ 21 декабря 2011

Золотое правило для написания этой строки:

   while ((ch = fgetc(stdin)) != EOF)

ch должно быть int. Ваша милая хитрость в создании ch без знака не удалась, потому что EOF - это целое число со знаком.

Хорошо, теперь давайте углубимся в глубину ......

Шаг 1:

ch=fgetc(fp)

fgetc() возвращает -1 (подписано int). По золотым правилам C ch получает последний октет битов, который равен всем 1. И, следовательно, значение 255. Шаблон байтов ch после выполнения

ch = fgetc(fp); 

будет, таким образом,

11111111

Шаг 2:

ch != EOF

Теперь EOF - это целое число со знаком , а ch - это unsigned char ...

Снова я обращаюсь к золотому правилу C ... меньший парень ch преобразуется в большой размер int перед сравнением, поэтому его байтовый шаблон теперь

00000000000000000000000011111111 = (255)10

пока EOF равно

11111111111111111111111111111111 = (-1)10

Нет никакого способа, которым они могут быть равны ....... Отсюда утверждение, чтобы управлять следующим циклом while

while ((ch = fgetc(stdin)) != EOF)

никогда не будет оцениваться как ложное ...

И, следовательно, бесконечный цикл.

7 голосов
/ 21 декабря 2011

Существует несколько неявных преобразований.Они не имеют никакого отношения к конкретному предупреждению, но я включил их в этот ответ, чтобы показать, что на самом деле делает компилятор с этим выражением.

  • ch в вашем примере имеет тип unsigned char.
  • EOF гарантированно имеет тип int (C99 7.19.1).

Таким образом, выражение эквивалентно

(unsigned char)ch != (int)EOF

Целочисленные правила продвижения в C будут неявно преобразовывать символ без знака в целое число без знака:

(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

Символ никогда не может иметь такого высокого значения,отсюда и предупреждение.

2 голосов
/ 15 марта 2013

Я тоже столкнулся с этой проблемой. Мое решение состоит в том, чтобы использовать 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;
  ... 
}

После моего теста это работает.

2 голосов
/ 21 декабря 2011

вам нужно использовать int

fgetc () возвращает int специально, чтобы он мог указывать конец файла

он отлично работает со знаком char, потому что EOF (-1)в диапазоне, но он сломается, если вы прочитаете в char со значением больше 127.

Используйте int, приведите его к char после того, как вы проверили EOF

0 голосов
/ 26 мая 2017

предупреждение о выпуске ворса выдается с помощью такой реализации

Сравнение типа 'char' с EOF

 // read the data in a buffer
611     ch = getc(csv_file);
612     while (ch != EOF)

FIX:

// read the data in a buffer
    while ((ch = getc(csv_file)) != EOF)
0 голосов
/ 21 декабря 2011

Когда вы сравниваете беззнаковое целое с подписанным целым, оно преобразует подписанное целое в беззнаковое целое и сравнивает их. Следовательно, когда вы читаете файл с беззнаковым 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-байтовой машине символов) и дает вывод 255. Следовательно, вы сравниваете 255 и 2 ^ 32 + 1, вызывая бесконечный цикл.

Проблема здесь состоит в том, чтобы обрезать перед сравнением.

Если вы используете

while((ch = fgetc(fp))!=(unsigned char)EOF)
    printf("%c",ch);

Ваша программа будет работать нормально!

...