Подтвердить что ты не робот

Невозможно понять 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?
}
4b9b3361

Ответ 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]