2017-06-11 4 views
3

Je perdais ma week-end sur StackOverflow et a vu this challenge dans le Hot Network Questions.Impossible de comprendre ce code de CodeGolf

Contexte

golfeurs Bonjour! Je voudrais apprendre tous les langages de programmation! Mais j'ai un peu une courte durée d'attention ... et copier tous les exemples Bonjour du monde est ennuyeux ... mais je comme le feu!^W^

Défi

Voici donc le plan! Je veux que vous écriviez le plus petit code que va compiler, imprimer Goodbye Cruel World !, et ensuite planter. Ou, en tant que défi de torsion bonus , imprimer Hello World! et s'écraser avec Goodbye Cruel Monde!

En tant qu'étudiant désireux de comprendre entièrement le langage C, je suis très confus lorsqu'ils tombent sur le C answer à ce défi:

main(){puts(puts("Goodbye Cruel World!"));} 

Imprime la chaîne et tente ensuite d'utiliser le retour valeur en tant que pointeur à une autre chaîne à imprimer, ce qui provoque une erreur de segmentation.

Merci à puts()documentation J'ai trouvé que puts() retourne une valeur non négative sur le succès. Donc, si je comprends bien, cela équivaut à quelque chose comme:

puts(2); 

Comment 2 est « un pointeur vers une autre chaîne à imprimer » ??

Plus tard, une amélioration a été ajoutée à cette même réponse:

main(i){i=puts("Goodbye Cruel World!")/0;} 

Et cette fois, je suis tout à fait perdu. Donc, i est pris comme un argument de main, utilisé pour stocker la valeur de retour de puts(). D'accord. Mais qu'en est-il du \0? Pourquoi utiliser le caractère NUL-TERMINATOR là-bas?

Si vous pouviez me alléger s'il vous plaît un peu, il serait très intéressant pour moi de comprendre cela. Aussi, je pense que le titre de la question pourrait être un peu plus précis si reformulé mais je n'ai pas pu mettre en mots mon malentendu.

+0

@Downvoter un indice? – Badda

+0

Rien de tout cela n'est un code C valide et votre compilateur devrait se plaindre pour diverses raisons. Si ce n'est pas le cas, en obtenir un nouveau ou passer à la norme C et activer les avertissements recommandés.Et nous ne sommes pas un service d'explication pour obfuscated == mauvais code. – Olaf

+5

'/ 0' signifie" diviser par zéro " –

Répondre

2

Les deux solutions provoquent un comportement indéfini.

La première solution:

main(){puts(puts("Goodbye Cruel World!"));} 

évalue puts("Goodbye Cruel World!"), qui retourne une valeur non négative en cas de succès. Cette valeur est transmise à puts(). Maintenant, selon §6.5.2.2 7:

Si l'expression qui désigne la fonction appelée a un type qui inclut un prototype, les arguments sont implicitement convertis, comme si par cession, aux types de correspondant paramètres, en prenant le type de chaque paramètre à être la version non qualifiée de son type déclaré .

Ainsi, le code tente de convertir la valeur renvoyée par le premier appel à puts(), comme par la cession, à une valeur de type char *. C'est le type de l'opérande de gauche dans l'affectation, alors que le type de l'opérande de droite est int. L'opérande de gauche étant un type de pointeur, l'opérande de droite doit être un pointeur vers une version qualifiée ou non d'un type compatible, un pointeur vers void ou une constante de pointeur nulle (§6.5.16.1 1). Aucun d'entre eux n'est vrai, il s'agit donc d'une violation de contrainte et le compilateur doit émettre un avertissement.

Ce comportement est indéfini, en ce sens qu'aucun comportement n'est défini pour ce qui doit se produire si vous exécutez ce code.

La deuxième solution est également un comportement non défini, étant donné que la division par zéro entraîne un comportement non défini (§6.5.5 5):

Le résultat de l'opérateur/est le quotient de la division du premier opérande par le second ; le résultat de l'opérateur% est le reste . Dans les deux opérations, si la valeur du deuxième opérande est zéro, le comportement n'est pas défini.

Un comportement indéfini peut inclure ou non le "plantage" de votre programme.

2

Le code échoue car le type de l'argument puts() est const char *, ce qui signifie "pointeur en lecture seule char". Ceci est statique, il ne change pas juste parce que vous essayez de lui passer quelque chose d'autre, la fonction interprète la valeur de l'argument comme s'il s'agissait d'un pointeur sur un caractère (en supposant que le compilateur ait même réussi à le compiler, est une hypothèse difficile ici car la valeur retournée int ne convertit pas const char *).En général, les petits entiers comme 2 ne sont pas valides en tant que pointeurs sur les systèmes de classe serveur/serveur (et pas sur tous les systèmes embarqués non plus), c'est-à-dire qu'il n'y a pas de mémoire disponible pour un processus typique à cette adresse. arrive est que le système d'exploitation arrête le processus pour violer ses limites. Mais, comme cela a été mentionné dans les commentaires, cette partie est undefined.

+1

Le pointeur passé n'a pas besoin d'être 'const' (C n'a pas de concept de" lecture seule "). Cela garantit simplement que 'puts' ne modifie pas la pointee. "En général les petits entiers comme 2 ne sont pas valides comme des pointeurs" - Hypothèse très risquée. La plupart des systèmes sont des systèmes embarqués et sur la plupart de ces valeurs d'adresse inférieures sont valides. Il vaut mieux limiter cela aux systèmes d'exploitation de taille normale comme Linux, Windows, etc. – Olaf

3

Et pour répondre à votre deuxième question:

main(i){i=puts("Goodbye Cruel World!")/0;} 

Il y a une différence entre '\0' et /0 Le premier est le caractère NUL, mais le second est une division par zéro. Donc, ce code essaie de diviser le résultat de puts par zéro.