Как работает код? - PullRequest
       28

Как работает код?

2 голосов
/ 21 февраля 2011

Код, приведенный ниже, написан на C. Когда задан набор входных чисел, он пропускает первый номер и печатает остальные.

main(i)  
{
    if(~scanf("%d",gets(&i)))
        printf("%d\n",i),main();
}

Хотелось бы узнать, как работает этот код?

РЕДАКТИРОВАТЬ: Для тех из вас, кто думает не работает http://www.ideone.com/cENzy

Ответы [ 2 ]

8 голосов
/ 21 февраля 2011

Этот код не является допустимым C. main должен принимать либо ноль, либо два, либо три параметра.Один параметр не является допустимым.gets каракули над стеком.Честно говоря, это чудо, если это работает вообще - неопределенное поведение изобилует!

С учетом сказанного давайте посмотрим на типичный способ компиляции кода C на x86, чтобы понять, как это работает.Во-первых, main(i) - это старое объявление в стиле K & R.Он интерпретируется как int main(int i), но не устанавливает истинного прототипа - поэтому будущие вызовы main не будут проверяться на предмет типа аргументов.Напомним, что в x86 аргументы передаются путем помещения их в стек непосредственно перед вызовом целевой функции.Так что, если у нас неверное количество аргументов, он не рухнет (при условии, что вы на ABI такого типа!), А просто выдаст фиктивные данные.

Также обратите внимание, что на x86 стек растет вниз- при вызове функций текущий указатель стека уменьшается .Это означает, что если вы повредите память выше одного из этих параметров, вы повредите память, принадлежащую вызывающей функции , и можете не заметить, пока не вернетесь.

Теперь давайте посмотрим напоток исполнения.gets(&i) выполняется первым, и (при условии, что компилятор игнорирует несоответствие типов!) Получает строку текста и сохраняет ее в стеке, перезаписывая фрейм стека вызывающего абонента! Это предполагает, что стек растет внизобъем памяти;в растущих стеках это, в зависимости от длины строки, перезапишет адрес возврата gets и, возможно, приведет к падению.

Хотя gets захватил строку текста, этот текст будет игнорироваться иотбрасываются.Это связано с тем, что возвращаемое значение gets, равное &i, будет передано scanf.Так что scanf читает целое число и сохраняет его в i.Нет проблем.scanf затем возвращает 1, что в двоичном виде отрицается до некоторого отрицательного ненулевого значения, которое является истиной, поэтому printf затем печатает значение.Затем оператор запятой служит для повторного рекурсивного вызова main с неправильным числом аргументов (аргумент обычно инициализируется некоторым поддельным значением), который действует как цикл.

Обратите внимание, что после возврата scanf,новая строка остается на входе неиспользованной, поэтому gets будет иметь дело с этим в следующий раз.Также обратите внимание, что когда происходит EOF, scanf вернет EOF (0xFFFFFFFF), который будет логически сведен к нулю. Затем вернется main и быстро завершит работу, потому что стек его вызывающей стороны, вероятно, был перезаписан на gets.

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

0 голосов
/ 21 февраля 2011

Поведение этого кода не определено. Без дальнейшей квалификации i имеет тип int. gets ожидает получить адрес буфера char *, в который он может записывать данные, так что это будет переполнение памяти.

...