Поведение строк C и функция atoi

Интересно, почему два значения int не подтверждают условие if, даже если оно истинно. printf показывает, что оба они равны.

Может ли переполнение буфера влиять на поведение условий if, нарушая поведение других разделов кода.

#include <stdio.h> 
#include <stdlib.h> 
#include <time.h>

  int main(void) {
    srand(time(NULL));
    char instring[2]; // when this increases somehow I get the right behavior
    int inint;
    int guess;
    guess = rand() % 127;
    inint = ~guess;
    printf("%i\n", guess); //testing with printf()
    while (guess != inint) {
      printf("Guess Number\r\n");
      gets(instring);
      inint = atoi(instring);
      printf("%i\n", inint);

      if (inint > guess) {
        printf("%i\n", inint);
        puts("too high");
      } else if (guess > inint) {
        puts("too low");
      } else {
        puts("right");
      }
    }
    return 0;
  }

person Community    schedule 16.04.2018    source источник
comment
Так вы действительно спрашиваете, почему слишком короткая строка переполняется?   -  person Weather Vane    schedule 16.04.2018
comment
Никогда не используйте gets! Это опасная функция, которая склонна к переполнению буфера (которое у вас есть), и поэтому она была удалена из спецификации C. Используйте, например. вместо этого fgets или getline, если вы работаете в системе POSIX.   -  person Some programmer dude    schedule 16.04.2018
comment
нет, я сказал, что спрашиваю о условии, почему оно не выполняется, даже значения равны, я сравниваю два числа   -  person    schedule 16.04.2018
comment
Что касается почему происходит переполнение буфера, помните, что char строки в C на самом деле называются заканчивающимися нулем байтовыми строками. Этот нуль-терминатор важен, и ему нужен собственный элемент в массиве. Это означает, что строка из двух символов требует места для трех, чтобы включить терминатор.   -  person Some programmer dude    schedule 16.04.2018
comment
Переполнение буфера приводит к неопределенному поведению. Когда у вас есть UB, все обсуждения поведения становятся неуместными.   -  person Some programmer dude    schedule 16.04.2018
comment
Ваши проблемы вызваны переполнением буфера   -  person M.M    schedule 16.04.2018
comment
Я использую printf, чтобы показать мне угаданное значение, в то же время я угадываю одно и то же значение и печатаю его, и оно печатается правильно, но если оно не выполняется, вероятно   -  person    schedule 16.04.2018
comment
но мои условия после получения реальных значений.   -  person    schedule 16.04.2018
comment
Сначала исправьте переполнение буфера.   -  person Weather Vane    schedule 16.04.2018
comment
@root Возможно ли, что вы вводите UTF-16? Таким образом, у вас есть 2 байта для одного символа. Из-за этого вы пропускаете выделение памяти для нулевого терминатора (\0). Поскольку ваш массив составляет 2 байта.   -  person    schedule 16.04.2018
comment
Я говорю о двух значениях ints, которые происходят между двумя int.   -  person    schedule 16.04.2018
comment
Странные вещи случаются, если у вас переполнение буфера. Во-первых, вам нужно исправить это...   -  person    schedule 16.04.2018


Ответы (1)


Проблема действительно здесь.

char instring[2];

Теперь давайте подумаем об этой линии.

gets(instring);

Допустим, вы набираете 10 и нажимаете Enter. В instring войдет три байта.

  1. 1
  2. 0
  3. Завершающий ноль.

instring может содержать только два байта, но gets все равно вставит (как минимум) три. Этот дополнительный байт переполнит соседнюю память, повредив память какой-либо другой переменной, что вызовет странную ошибку.

И именно поэтому создание instring достаточно большого размера для хранения результата gets исправляет программу.

Чтобы избежать этого при работе со строками, используйте функции, которые ограничивают себя доступной памятью. В этом случае fgets.

fgets(instring, sizeof(instring), stdin);

Это ограничит себя чтением только того, что может поместиться в instring.

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

person Schwern    schedule 16.04.2018
comment
поэтому поведение условий повреждено переполнением стека? - person ; 16.04.2018
comment
@root Не переполнение стека, это нечто другое, а переполнение буфера. Вероятно, в guess. Вы можете проверить их расположение в памяти с помощью printf("%p / %p", &instring, &guess). - person Schwern; 16.04.2018
comment
Скорее всего, значения переменных int, объявленных после переполнения массива символов, изменяются. - person o_weisman; 16.04.2018
comment
@root Обратите внимание, что их порядок в памяти не обязательно соответствует их порядку в программе. И он может меняться от компилятора к компилятору и даже между опциями компилятора. Когда я компилирую с -fsanitize=address, guess идет после instring. Но когда я удаляю это guess, оно появляется перед instring. В любом случае, переполнение буфера вызывает неопределенное поведение, и все ставки сняты. - person Schwern; 16.04.2018