LINUX.ORG.RU

comparison is always true due to limited range of data type

 


0

1

Здравствуйте.

Компиляция программы на СИ.

Поясните пожалуйста такую вещь, вот часть кода...

Читаю из файла:

 
   char ch;
   while(ch!=EOF) 
    {
      ...
    }

Если компилировать на большом компе (х86), то ошибки отсутствуют.

Версия компилятора:

 
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.3)

А если делаю то же самое на RaspberryPi2 (ARM), то говорит, что «сравнение всегда верно из-за ограниченного диапазона типа данных» (comparison is always true due to limited range of data type [-Werror=type-limits]).

Что ему не нравится?

Версия компилятора RPi:

 
gcc version 4.9.2 (Raspbian 4.9.2-10)

Если Вы используете getchar/fgetc, то обращаю Ваше внимание на то, что тип возвращаемого значения указан как int. Причина как раз в том, чтобы EOF заведомо не совпадала ни с одним из возможных значений char. Согласитесь, было бы странно, если бы файл, заполненный значениями 0xFF, останавливал своё чтение на первом же байте (кстати, предлагаю проверить скомпилированную на x86 программу вариантом, когда посреди файла есть байт со значением 0xFF, вбить можно в любом HEX-редакторе). Поэтому порядок таков:

int getcResult;
char gotChar;
while ( (getcResult = getchar()) != EOF ) {
    gotChar = (char) getcResult;
    /* fürther processing */
}
На будущее рекомендую всегда включать предупреждения и добиваться их понимания, дабы избежать настолько досадных ошибок, что их умудряется обнаруживать даже железяка.

Northsoft ★★ ()
Последнее исправление: Northsoft (всего исправлений: 2)
Ответ на: комментарий от Northsoft

Спасибо за ответ.

Делаю так:

   char ch;
   while(ch!=EOF) 
    {
      ch = getc(file);
      send1_array[count_simvol] = ch;
      count_simvol++;
    }

Компилирую с такими флагами:

gcc -Wall -Wextra ...

На х86 никаких предупреждений не даёт, только на RPi.

...

Сделал так:

   char ch;
   while(!feof(file)) 
    {
...

Теперь не на х86, не на RPi ошибок нет.

stD ()
Ответ на: комментарий от stD

Сказали же int надо использовать. На x86 предупреждений нет из-за знакового char по умолчанию. На ARM он беззнаковый, поэтому 0xff не будет расширено до -1 и условие всегда ложно, а на x86 из-за расширения 0xff будет считаться за EOF.

А проверка на feof() может дать неправильный результат, так как такая ситуация вполне нормальная:

assert(!feof(file));
assert(getc(file) == EOF);
assert(feof(file));
Т.е. флаг конца файла не будет взведён до попытки чтения за концом файла и в приложении считается байт 0xff, которого нет в файле.

xaizek ★★★★★ ()
Ответ на: комментарий от xaizek

Сказали же int надо использовать.

int не использовал потому-что справедливо полагал, что один символ поместится в один байт и ему не понадобится четыре)))

На x86 предупреждений нет из-за знакового char по умолчанию.

Да, дело в этом (собственно вопрос в этом и заключался), сейчас прочитал про ARM. Но кто бы мог подумать, что char окажется беззнаковый.

А проверка на feof() может дать неправильный результат

Спасибо.

stD ()
Ответ на: комментарий от Northsoft

Причина как раз в том, чтобы EOF заведомо не совпадала ни с одним из возможных значений char.

Я понял что вы хотели сказать. Большое спасибо. Получается что в int значение 0xFF будет выглядеть как 255, а в char как -1. Правильно я понял? А как тогда определяется конец файла? Или я ошибаюсь?

stD ()
Ответ на: комментарий от stD

Поймите одну простую вещь. Значение (типа int), возвращённое функциями getchar/fgetc, может быть либо символом (и подлежит приведению, как в моём примере), либо значением EOF (0xFFFFFFFF для int).

Получается что в int значение 0xFF будет выглядеть как 255, а в char как -1

Символ '\xFF' (например, 'я' в cp1251) в char будет равен 0xFF, в int будет равен 0x000000FF.
EOF в char будет равен 0xFF, в int будет равен 0xFFFFFFFF. Как видите, при чтении в переменную типа char эти случаи сливаются, от чего Вас и пытается предостеречь компилятор.

А как тогда определяется конец файла? Или я ошибаюсь?

Возвращённое функциями getchar/fgetc значение сохраняется в переменную типа int, где и сравнивается на равенство с EOF. При неравенстве — производится приведение к переменной типа char.

Northsoft ★★ ()
Ответ на: комментарий от Northsoft

Благодарю, за доходчивое объяснение.

Сделал вот так:

   FILE *file; 
   file = fopen(full_patch_file,"r");

   if(file == NULL) error_log("Error open file");

   int ch;
   while(ch!=EOF)
    {
      ch =  getc(file);
      send1_array[count_simvol] = (char) ch;
      count_simvol++;
      if(count_simvol == ARRAY_SIZE - 2) break;
    }

   fclose(file);

...

Эх, вот найти бы место, где функции расписаны так же лаконично как это делаете Вы...

Если не затруднит, объясните такой момент (не подумайте, что я бездельник занимающийся копипастой кода из сети, учусь по книге Ричарда Стивенса «Профессиональное программирование» второе издание)...

Мне нужно скопировать в массив «full_patch_file» (объявляется в начале программы - char patch_index_file[64] = {0,};) некую строку:

snprintf(full_patch_file, 6, "%s", "blabla");

Нужно ли перед этим очищать «full_patch_file» (при условии что он уже использовался в другом месте)? То есть, функция «snprintf» будет записывать новую строку в массив «full_patch_file» начиная с нулевого значения или допишет к уже существующим там символам? Просто в книге это явно не объясняется. Или я что-то перемудрил?

stD ()
Ответ на: комментарий от stD

Сделал вот так

Пример вызывает сразу же несколько вопросов. Возможно, их обнаружит даже компилятор. Во-первых, «while(условие)» — это цикл с предусловием, т.е. условие «ch!=EOF» проверяется в том числе перед первой итерацией, т.е. перед первым чтением «ch = getc(file);». Это может привести к тому, что изначально неинициализированное значение может содержать как раз 0xFFFFFFFF (EOF). Предлагаю внимательно ознакомиться с моим примером выше. Либо можно сделать так:

while( ch = getc(file), ch != EOF ){
    /* fürther processing */
}
Пример выше работает так же, как и пример ниже:
int a=5, b=10;
printf("%d",
    (a=a+1, b=b+2, a+b)
); /* выведется 18 */

Эх, вот найти бы место, где функции расписаны так же лаконично как это делаете Вы...

Маны. Если их недостаточно, можете обратиться к какому-нибудь преподавателю.

функция «snprintf» будет записывать новую строку в массив «full_patch_file» начиная с нулевого значения

Да. Но это же самое можно сделать с помощью strncpy. Функция snprintf предназначеня для более сложных случаев, когда необходимо указать форматную строку более сложную, чем «%s».

или допишет к уже существующим там символам

Это называется strncat.

Нужно ли перед этим очищать «full_patch_file»

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

И ещё.

char strbuffer[100];
snprintf(strbuffer, 6, "%s", "blabla");
printf("%s\n", strbuffer); /* выведет «blabl» */

Northsoft ★★ ()
Ответ на: комментарий от Northsoft

Безмерно благодарен, ссылку на преподавателя положил в закладки.

...

snprintf(strbuffer, 6, «%s», «blabla»);

Я понял.

...

Клянусь, последний вопрос...) Очистку «full_patch_file» хочу делать так:

memset(full_patch_file, 0, sizeof(full_patch_file));

Хоть в том то я прав? )

stD ()
Ответ на: комментарий от stD

Правы только в том случае, если массив full_patch_file был определён с точностью до размера в той же функции, что и вызов sizeof. Приведу пример:

#include <stdio.h>

void demoSizeof(int a[])
{
    printf("demoSizeof: sizeof(a) = %zu\n", sizeof(a));
}

int main(int argc, char *argv[], char **env)
{
    int strbuffer[100];
    printf("main: sizeof(strbuffer) = %zu\n", sizeof(strbuffer));
    demoSizeof(strbuffer);
    return 0;
}
Вывод:
main: sizeof(strbuffer) = 400
demoSizeof: sizeof(a) = 8
Так что более универсальным решением будет явное взятие размера массива, например, «memset(full_patch_file, 0, FILENAME_BUFFER_LEN*sizeof(char));».

Northsoft ★★ ()
Ответ на: комментарий от stD

На самом деле, это зависит от того, зачем ты хочешь его занулить. Если собираешься переиспользовать его ниже и имеющиеся данные могут этому мешать — тогда да, если full_patch_file в данном контексте действительно массив, а не указатель. Если же хочешь затереть какие-нибудь конфиденциальные данные, чтобы их случайно не слить куда не надо, и не собираешься больше использовать этот массив, то компилятор запросто может выкинуть вызов memset как ненужный — в этом случае лучше использовать memset_s.

Softwayer ★★ ()
Ответ на: комментарий от Northsoft

Так что более универсальным решением будет явное взятие размера массива

Блин, как же у вас всё красиво получается, я прям завидую!

Массив «full_patch_file» объявлен глобально:

char full_patch_file[64] = {0,};

Буду делать так:

memset(full_patch_file, 0, 64*sizeof(char));

П.С. Я за это утро узнал столько... Спасибо Вам большое!

...

если full_patch_file в данном контексте действительно массив, а не указатель

Я, в некотором роде, побаиваюсь указателей, точнее боюсь запутаться. Программа не маленькая и постоянно добавляются функции, вот и решил, что сделаю всё на выделенных массивах.

stD ()
Ответ на: комментарий от stD

Кстати, sizeof(char) по определению равен единице, так что это какая-то тавтология.

Softwayer ★★ ()
Ответ на: комментарий от Softwayer

sizeof(char) по определению равен единице

Это я понимаю, однако теперь у меня нет уверенности ни в чём)))

Считаете что достаточно будет так?

memset(full_patch_file, 0, 64);
stD ()
Ответ на: комментарий от Softwayer

Кстати, sizeof(char) по определению равен единице, так что это какая-то тавтология.

Уж лучше пускай stD пишет по одному соглашению. Будет удобней и читабельней.

Считаете что достаточно будет так?

Предлагаю вводить константы на любое число, которое повторяется где-либо дважды и больше. Вариант для C99:

int main(int argc, char *argv[])
{
    const unsigned int FILENAME_SIZE = 100;
    char filename[FILENAME_SIZE];
    memset(filename, 0, FILENAME_SIZE*sizeof(char));
    strncpy(filename, "file.txt", FILENAME_SIZE);
    printf("%s\n", filename);
    return 0;
}

Northsoft ★★ ()
Ответ на: комментарий от Northsoft

Спасибо ещё раз!

Возьму за привычку описывать длину типа переменной:

memset(full_patch_file, 0, 64*sizeof(char));

Константы у меня определены:

#define FPF 64

Просто сюда писал цифрами для удобства.

stD ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.