Как работает код 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 (который является своего рода грязным, но работает).