Невозможно понять Obfuscated C code
Я не могу это понять. Пожалуйста, объясните.
Изменить. Он печатает: 'hello, world!'
#include <stdio.h>
int i;
main()
{
for(;i["]<i;++i){--i;}"];read('-'-'-',i+++"hell\o, world!\n",'/'/'/'));
//For loop executes once, calling function read with three arguments.
}
read(j,i,p)
{
write(j/p+p,i---j,i/i); //how does it work? like printf?
}
Ответы
Ответ 1
Нарушение вниз:
for({initial expr};{conditional expr};{increment expr})
"{initial expr}" пуст, поэтому он ничего не делает.
"{Условный expr}" равен 'i["]<i;++i){--i;}"]'
что совпадает с
"]<i;++i){--i;}"[i]
или
const char* str = "]<i;++i){--i;}";
for (; str[i]; )
поэтому он зацикливается до тех пор, пока выражение не станет ложным (т.е. удаляет нуль в конце строки).
{increment expr}
-
read('-'-'-',i+++"hell\o, world!\n",'/'/'/')
Если вы нарушите эти параметры чтения, вы:
'-' - '-' == char('-') - char('-') == 0
Для параметра two у вас есть:
i+++"hell\o, world!\n"
который совпадает с: i ++ + "hell\o, world!\n"
Таким образом, он увеличивает значение переменной "i", это означает, что цикл for будет зацикливаться на количество символов в условной строке "]
В первый раз вокруг вас заканчивается:
0 + "hell\o, world!\n"
Второй раз вокруг цикла будет 1 + "ад \, мир! \n" и т.д.
Итак, второй параметр - это указатель на "ад \, мир! \n".
Третий параметр:
'/'/'/' == '/' / '/' == char('/') / char('/') == 1
Итак, третий параметр всегда равен 1.
Теперь мы разрушаем функцию чтения, которая вызывает запись:
write(j/p+p,i---j,i/i);
Существует три параметра, первый из которых:
j/p+p where j == 0, p == 1 so 0/1+1 == 1.
Если прочитать ссылку на функцию записи 1, она жестко запрограммирована для записи в стандартную версию.
Второй параметр для записи -
i---j
который является тем же самым, i-- - j
, где я - указатель на строку и j = 0
, так как я пост-декрементированный - ничего не делает, а '- 0'
ничего не делает, он просто передает указатель через к функции записи.
Третий параметр 'i / i
', который всегда будет 1
.
Итак, для каждого вызова "читать" он каждый раз записывает один символ из строки "hell\o, world!\n".
Ответ 2
read('-'-'-',i+++"hell\o, world!\n",'/'/'/')
Вызывает read
с первым аргументом:
'-' - '-'
Итак, вычитание a char из себя, т.е. нуль.
Второй аргумент:
i++ + "hell\o, world!\n"
Итак, это адрес внутри строковой константы "hell\o world!\n"
, который будет зависеть от значения i
.
Третий аргумент:
'/' / '/'
Повторение арифметики на тему символов либералов, на этот раз производя 1
.
Вместо обычного read
этот вызов переходит к методу, определенному внизу, который фактически выполняет write
.
Аргумент 1 для записи:
j/p+p
Что такое 0/1 + 1 = 1.
Аргумент 2:
i-- - j
Что отменяет преобразование в более раннем строковом литерале, оценивая обратно строку "hell\o world..."
.
Третий аргумент:
i/i
то есть. 1.
Таким образом, чистый эффект чтения состоит в том, чтобы написать один байт из строки, переданной в дескриптор файла 1.
Он ничего не возвращает, хотя и должен, поэтому результат и, следовательно, точное поведение более раннего цикла undefined.
Подстрочный индекс i
в цикле for идентичен записи:
*((i) + (the string given))
то есть. он захватывает байт из этой строки. Поскольку начальное значение i
равно undefined, это может быть доступ за пределами границ.
Обратите внимание, что i
внутри read
является локальным, а не глобальным. Таким образом, глобальный продолжает увеличиваться, проходя по одному символу за раз, пока он не достигнет завершающего нуля в другом строковом литерале.
Если i
было задано 0 как начальное значение, то этот код был бы правильным.
(EDIT: как было указано в другом месте, я был здесь не так: i
изначально нулевым, потому что он глобальный. Телеологически он не стоит ничего во время выполнения, чтобы дать глобальным значениям начальные значения, поэтому C это будет стоить дать что-нибудь в стеке начальное значение, поэтому C не делает.)
Ответ 3
Сначала посмотрите синтаксис функции read
и write
в C и что они делают:
ssize_t read(int fildes, void *buf, size_t nbyte);
Функция read()
должна попытаться прочитать nbyte
байты из файла, связанного с дескриптором открытого файла, fildes
, в буфер, на который указывает buf
.
ssize_t write(int fildes, const void *buf, size_t nbyte);
Функция write()
должна попытаться записать nbyte
байты из буфера, на который указывает buf
, в файл, связанный с дескриптором открытого файла, fildes
.
Теперь, переписывая цикл for
как
for(;i["]<i;++i){--i;}"]; read('-' - '-', i++ + "hell\o, world!\n", '/' / '/'));
Начиная с i["]<i;++i){--i;}"]
;
"]<i;++i){--i;}"
- строка. В C, если
char ch;
char *a = "string";`
тогда вы можете написать ch = "string"[i]
, что эквивалентно i["string"]
(как a[i] = i[a]
). Это в основном добавляет адрес string
в i
(i
инициализируется на 0
, поскольку он определен глобально). Итак, i
инициализируется начальным адресом строки hell\o, world!\n
.
Теперь дело в том, что цикл for
не повторяется только один раз!
Выражение read('-' - '-', i++ + "hell\o, world!\n", '/' / '/')
можно переписать как (для удобства);
read(0, i++ + "hell\o, world!\n", 1)
^ read only one byte (one character)
Теперь то, что он будет делать, это вызвать read
и increment i
(используя его предыдущее значение). Начальный адрес строки hell\o, world!
добавляется в i
. Поэтому первый вызов чтения просто распечатает H
. На следующей итерации i
увеличивается (содержит адрес следующего символа), и вызов для чтения будет печатать следующий символ.
Это будет продолжаться до тех пор, пока i["]<i;++i){--i;}"]
не станет false
(at \0
).
В целом поведение кода undefined!
ОБЪЯСНЕНИЕ для UB:
Обратите внимание, что вызов функции f(a,b,c)
не является использованием оператора запятой и порядок оценки для a
, b
и c
неуказан.
Также C99 заявляет, что:
Между предыдущей и следующей точками последовательности объект должен иметь измененное значение хранимого значения не более одного раза путем оценки выражения. Кроме того, следует получить доступ к предыдущему значению только для определения значения, которое будет храниться.
Следовательно, вызов
write(j/p+p, i-- -j, i/i);
вызывает UB. Вы не можете изменять и использовать переменную в том же выражении. Компилятор должен предупредить
[Предупреждение] операция на "i" может быть undefined [-Wsequence-point]