Как работает код C, который печатает от 1 до 1000 без циклов или условных операторов?
Я нашел C
код печатает от 1 до 1000 без петель или условных обозначений :
Но я не понимаю, как это работает. Может кто-нибудь пройти через код и объяснить каждую строку?
#include <stdio.h>
#include <stdlib.h>
void main(int j) {
printf("%dn", j);
(&main + (&exit - &main)*(j/1000))(j+1);
}
2 ответа:
никогда не пишите такой код.
на
j<1000
,j/1000
равна нулю (целочисленное деление). Итак:(&main + (&exit - &main)*(j/1000))(j+1);
эквивалентно:
(&main + (&exit - &main)*0)(j+1);
что:
(&main)(j+1);
которых звонки
main
Сj+1
.если
j == 1000
, то же самое строки выходит как:(&main + (&exit - &main)*1)(j+1);
что сводится к
(&exit)(j+1);
что это
exit(j+1)
и оставляет программа.
(&exit)(j+1)
иexit(j+1)
по существу то же самое-цитируя C99 §6.3.2.1/4:указатель функции-это выражение, которое имеет тип функции. За исключением тех случаев, когда это операнд оператора sizeof или унарный & оператор, а обозначение функции типа " функция возвращает тип" преобразуется в выражение, которое имеет тип "указатель на функция, возвращающая типа".
exit
является указателем функции. Даже без унарного&
адрес-оператора, он рассматривается как указатель на функцию. (Тег&
просто делает явными.)и вызовы функций описаны в §6.5.2.2/1 и ниже:
выражение, обозначающее вызываемую функцию, должно иметь тип указатель на функцию возвращающий Void и возвращает объект типа, кроме массива тип.
так
exit(j+1)
работает из-за автоматического преобразования типа функции в указатель на функцию типа, и(&exit)(j+1)
работает также с явным преобразованием в тип указателя на функцию.как говорится, приведенный выше код не соответствует (
main
принимает либо два аргумента, либо вообще ни одного), и&exit - &main
является, Я полагаю, неопределенным в соответствии с §6.5.6 / 9:когда вычитаются два указателя,оба указывают на элементы одного и того же объекта массива, или один после последнего элемента массива объекта; ...
дополнение
(&main + ...)
будет действителен сам по себе, и может быть использован,если добавленное количество было равно нулю, так как §6.5.6 / 7 говорит:для целей настоящих операторов, указатель на объект, который не является элементом массив ведет себя так же, как указатель на первый элемент массива длины с типа объект как его тип элемента.
Итак, добавление нуля к
&main
было бы хорошо (но не много пользы).
он использует рекурсию, арифметику указателя и использует поведение округления целочисленного деления.
The
j/1000
срок округляется до 0 для всехj < 1000
, один разj
достигает 1000, он оценивает до 1.теперь, если у вас есть
a + (b - a) * n
, гдеn
либо 0, либо 1, Вы в конечном итоге сa
еслиn == 0
иb
еслиn == 1
. Используя&main
(адресmain()
) и&exit
наa
иb
термин(&main + (&exit - &main) * (j/1000))
возвращает&main
когдаj
ниже 1000,&exit
в противном случае. Полученный указатель функции затем подается аргументj+1
.вся эта конструкция приводит к рекурсивному поведению: while
j
ниже 1000,main
рекурсивно вызывает саму себя, когдаj
достигает 1000, он вызываетexit
вместо этого, делая выход программы с кодом выхода 1001 (который является своего рода грязным, но работает).