27 votes

Interview Explication de Hello World

Ce site entrée classique de l'ioccc est un programme "hello world" écrit en c. Quelqu'un peut-il fournir une explication de son fonctionnement ?

Code original (la coloration syntaxique est intentionnellement absente) :

int i;main(){for(;i\["\]<i;++i){--i;}"\];read('-'-'-',i+++"hell\\
o, world!\\n",'/'/'/'));}read(j,i,p){write(j/p+p,i---j,i/i);}

Légèrement plus propre :

int i;
main()
{
  for ( ; i["]<i;++i){--i;}"]; read('-' - '-', i++ + "hello, world!\n", '/' / '/'));
}

read(j, i, p)
{
  write(j / p + p, i-- - j, i / i);
}

56voto

dan04 Points 33306

Pour la condition de boucle

i["]<i;++i){--i;}"]

Cette expression tire parti du fait que l'indexation des tableaux est commutative en C. Elle est équivalente à .

"]<i;++i){--i;}"[i]

Ainsi, la boucle se terminera lorsque le caractère en position i est \0 c'est-à-dire à la fin de la chaîne, qui compte 14 caractères (ce qui se trouve être la même longueur que "hello, world ! \n "). Ainsi, le for La condition de boucle peut être réécrite comme suit :

i != 14

arithmétique des caractères

read('-' - '-', i++ + "hello, world!\n", '/' / '/')

char est un type entier, et donc :

  • '-' - '-' est de 0
  • '/' / '/' est 1

    read(0, i++ + "hello, world ! \n ", 1)


Après avoir corrigé tous les avertissements du compilateur (comme la conversion implicite de int en pointeur), et simplifié les choses mentionnées ci-dessus, le code devient :

#include <unistd.h>

int i = 0;

void read2(int, char*, int);

int main()
{
   while (i != 14)
   {
      read2(0, i++ + "hello, world!\n", 1);
   }

   return 0;
}

void read2(int j, char* i, int p)
{
   write(j / p + p, i-- - j, 1);
}

(J'ai renommé read à read2 pour éviter tout conflit avec le système Unix read fonction.)

Notez que le j et p arguments pour read2 ne sont pas nécessaires, car la fonction est toujours appelée avec j=0 et p=1.

#include <unistd.h>

int i = 0;

void read2(char*);

int main()
{
   while (i != 14)
   {
      read2(i++ + "hello, world!\n");
   }

   return 0;
}

void read2(char* i)
{
   write(1, i--, 1);
}

L'appel write(1, i--, 1) écrit 1 caractère de i vers le descripteur de fichier 1 (stdout). Et la post-décrémentation est superflue parce que ce i est une variable locale qui ne sera plus jamais référencée. Cette fonction est donc équivalente à putchar(*i) .

Mise en ligne de la read2 dans la boucle principale donne

#include <stdio.h>

int i = 0;

int main()
{
   while (i != 14)
   {
      putchar(*(i++ + "hello, world!\n"));
   }

   return 0;
}

dont le sens est évident.

17voto

DevSolar Points 18897

Je n'ai pas l'intention de le démonter complètement, mais il y a quelques indices :

  • '-' - '-' est obfusqué pour 0
  • '/' / '/' et le i / i dans read() sont obfusquées pour 1

Rappelez-vous que [] est commutatif, c'est-à-dire que i["]<i;++i){--i;}"] est la même chose que "]<i;++i){--i;}"[i] (ayant un tableau de caractères et pointant sur le i-ième élément de celui-ci). Le site contenu de la chaîne de caractères n'a aucune importance, seul son longueur est utilisée pour définir le nombre d'itérations de la boucle. Toute autre chaîne de la même longueur (incidemment, la même longueur que la sortie...) fonctionnerait. Après ce nombre d'itérations, "string"[i] renvoie le caractère nul qui termine la chaîne. Zéro == faux, la boucle se termine.

Avec cela, il devrait être relativement facile de comprendre le reste.

Edit : Les votes positifs m'ont suffisamment intéressé pour que je regarde ça de plus près.

Bien sûr, i++ + "Hello, world!\n" est la même chose que "Hello, world!\n"[ i++ ] .

codelark a déjà souligné que 0 est le fid pour stdout. (Donnez lui +1 pour ça, pas moi. Je le mentionne juste par souci d'exhaustivité).

Le site i sur read() est bien sûr locale, c'est-à-dire que la i-- sur read() n'affecte pas le i sur main() . Puisque les i / i est toujours 1 de toute façon, le -- L'opérateur ne fait rien du tout.

Oh, et dis à l'interviewer de virer celui qui a écrit ce code. Il n'effectue pas de contrôle d'erreur sur la valeur de retour de la fonction write() . Je peux vivre avec du code notoirement mal écrit (et je l'ai fait pendant de nombreuses années), mais ne pas vérifier les erreurs est pire que mauvais, c'est défectueux . :-)

Le reste n'est que des maths de troisième année. Je passerais ça à un stagiaire ;-)

10voto

codelark Points 7168

L'indice de chaîne de i["..."] entraîne l'exécution de la boucle pour la longueur de la chaîne. i++ + la chaîne hello world signifie que chaque itération passe la chaîne commençant une lettre plus loin à la fonction locale de lecture.

première itération = "hello " deuxième itération = "ello "

Le premier paramètre de la fonction read se simplifie en 0, le descripteur de fichier de stdout. Le dernier paramètre se simplifie à 1, de sorte qu'un seul caractère est écrit par appel. Cela signifie que chaque itération écrira le premier caractère de la chaîne passée à stdout. Donc, en suivant l'exemple des chaînes de caractères ci-dessus :

première itération = "h" deuxième itération = "e"

L'index de la chaîne dans la boucle for est de la même longueur que la chaîne hello world, et comme [] est commutatif, et que les chaînes sont terminées par un caractère nul, la dernière itération renverra le caractère nul et quittera la boucle.

4voto

ymv Points 1088

Loin d'être un expert, mais je vais essayer :

i["]<i;++i){--i;}"]
// is actually
char* x = "]<i;++i){--i;}"
*(i + x)
// and will turn to zero when i == 14 (will point to string's ending zero)
// '-' - '-' and '/' / '/' are always 0 and 1
// int i is initiated to zero
// i++ will return original value, so main turns to
main()
{
    char* hello = "hello, world!\n";
    for (i = 0 ; i != 14; i++) read(0, hello[i], 1);
}

// read becomes
// i-- does nothing here, i is in read's scope, not the global one
read(j, i, p)
{
  write(1, i, 1);
}

// and at last
main()
{
    char* hello = "hello, world!\n";
    for (i = 0 ; i<14; i++) write(1, hello[i], 1);
}

1voto

Elemental Points 4997

Encore une autre question d'entretien qui semble refléter davantage l'intervieweur que la personne interrogée. Les questions clés ici : * j est toujours 0 (('-' - '-') == 0) * p est toujours égal à 1 (('/' / '/') == 1) * i (dans la fonction de lecture) est (i++ + "hello world") == le iième caractère de hello world (puis incrémenter i)

Ainsi, la fonction de lecture devient

read(0, NextChar, 1)
{
  write(1, NextChar , 1);
}

Le seul autre élément est la commutativité de l'opérateur []dans la boucle for.

La compréhension et l'analyse de ce type de code n'ont aucune valeur à mes yeux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X