J'ai trouvé du C
code qui imprime de 1 à 1000 sans boucles ni conditionnelles : mais je ne comprends pas comment cela fonctionne. Quelqu'un peut-il parcourir le code et expliquer chaque ligne?
#include <stdio.h>
#include <stdlib.h>
void main(int j) {
printf("%d\n", j);
(&main + (&exit - &main)*(j/1000))(j+1);
}
c
function-pointers
ob_dev
la source
la source
main
en C ++.Réponses:
N'écrivez jamais de code comme ça.
Pour
j<1000
,j/1000
vaut zéro (division entière). Alors:est équivalent à:
Lequel est:
Qui appelle
main
avecj+1
.Si
j == 1000
, alors les mêmes lignes sortent comme:Ce qui se résume à
Ce qui est
exit(j+1)
et quitte le programme.(&exit)(j+1)
etexit(j+1)
sont essentiellement la même chose - citant C99 §6.3.2.1 / 4:exit
est un désignateur de fonction. Même sans l'opérateur d'&
adresse unaire de, il est traité comme un pointeur vers une fonction. (Le&
juste le rend explicite.)Et les appels de fonction sont décrits au §6.5.2.2 / 1 et suivants:
Cela
exit(j+1)
fonctionne à cause de la conversion automatique du type de fonction en type pointeur vers fonction, et(&exit)(j+1)
fonctionne également avec une conversion explicite en type pointeur vers fonction.Cela étant dit, le code ci-dessus n'est pas conforme (
main
prend soit deux arguments, soit aucun), et&exit - &main
est, je crois, indéfini selon le §6.5.6 / 9:L'addition
(&main + ...)
serait valide en soi, et pourrait être utilisée, si la quantité ajoutée était nulle, puisque le §6.5.6 / 7 dit:Donc, ajouter zéro à ce
&main
serait correct (mais pas très utile).la source
foo(arg)
et(&foo)(arg)
sont équivalents, ils appellent foo avec l'argument arg. newty.de/fpt/fpt.html est une page intéressante sur les pointeurs de fonction.foo
est un pointeur,&foo
est l'adresse de ce pointeur. Dans le second cas,foo
est un tableau et&foo
équivaut à foo.((void(*[])()){main, exit})[j / 1000](j + 1);
&foo
n'est pas la même chose quefoo
lorsqu'il s'agit d'un tableau.&foo
est un pointeur vers le tableau,foo
est un pointeur vers le premier élément. Ils ont cependant la même valeur. Pour les fonctions,fun
et&fun
sont tous deux des pointeurs vers la fonction.Il utilise la récursivité, l'arithmétique des pointeurs et exploite le comportement d'arrondi de la division entière.
Le
j/1000
terme s'arrondit à 0 pour tousj < 1000
; une foisj
atteint 1000, il est évalué à 1.Maintenant, si vous avez
a + (b - a) * n
, oùn
est 0 ou 1, vous vous retrouvez aveca
sin == 0
etb
sin == 1
. En utilisant&main
(l'adresse demain()
) et&exit
poura
etb
, le terme(&main + (&exit - &main) * (j/1000))
renvoie&main
lorsquej
est inférieur à 1000,&exit
sinon. Le pointeur de fonction résultant reçoit ensuite l'argumentj+1
.L'ensemble de cette construction entraîne un comportement récursif: tant qu'il
j
est inférieur à 1000, ilmain
s'appelle lui-même de manière récursive; lorsqu'ilj
atteint 1000, il appelle à laexit
place, faisant quitter le programme avec le code de sortie 1001 (ce qui est un peu sale, mais fonctionne).la source
exit
, qui prend le code de sortie comme argument et, eh bien, quitte le processus en cours. À ce stade, j vaut 1000, donc j + 1 vaut 1001, ce qui devient le code de sortie.