Этот код не является допустимым 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
.
В общем, аккуратный хак, но сильно зависит от неопределенного поведения.Пожалуйста, не копируйте это в реальном коде.