2010-11-11 8 views
22

Ich sah nur diese hereC-Code: Wie funktionieren diese überhaupt?

#include <stdio.h> 

int main(int argc, char *argv[printf("Hello, world!\n")]) {} 

Was das bedeutet Druck ist „Hallo Welt!“

Aber was ist eigentlich hier los?

Das Beste, was ich denke, ist, dass es an der Spitze des Ausführungsstapels zusammengestellt und geworfen wird, aber die Syntax nicht einmal mir legal aussehen ...

Antwort

21

Der Code verwendet die C99-Array-Funktion mit variabler Länge, mit der Sie Arrays deklarieren können, deren Größe nur zur Laufzeit bekannt ist. printf gibt eine Ganzzahl zurück, die der Anzahl der tatsächlich gedruckten Zeichen entspricht, damit der Code "Hello, world!" zuerst und verwendet den Rückgabewert als die Größe argv. Die main Funktion selbst tut nichts. Der eigentliche Aufruf an printf selbst geht wahrscheinlich in den Startup-Code, der vom Compiler generiert wird, der wiederum main aufruft.

Edit: Ich habe gerade überprüft die Zerlegung des Codes erzeugt durch gcc und es scheint, dass der Anruf an printf innerhalb main geht selbst, vor jedem anderen Code.

+0

Der Aufruf von 'printf' geht definitiv nicht in irgendeinen Startup-Code. –

+0

@R ..: Das war nur eine Vermutung, und das Beste, was ich machen kann. Wo könnte es sonst noch ausgeführt werden? – casablanca

+0

@R ..: Macht nichts, du hast recht damit und ich habe meine eigene Frage beantwortet. :) – casablanca

-1

Ich bin kein C-Experte, aber Es sieht so aus, als ob die Befehlszeilenargumente gleichzeitig mit main deklariert sind.

+4

Nein. Prasoon und Casablanca haben es. – dmckee

4

char *argv[printf("Hello, world!\n")])

printf() gibt die Anzahl der gedruckten Zeichen.

So

int main(int argc, char *argv[printf("Hello, world!\n")]) {}

entspricht

int main(int argc, char *argv[14]) {}

plus ein Aufruf an printf() diedruckt

+1

Das ist nur wahr, wenn printf alle Zeichen erfolgreich drucken kann. – Puppy

5

Wenn ich herausfinden, wie der Compiler analysiert, ich werde diese aktualisieren, aber zumindest muss es keine Vermutungen sein, wie es kompiliert:


objdump --disassemble /tmp/hello (edited): 

080483c4 <main>: 
80483c4:  55      push %ebp 
80483c5:  89 e5     mov %esp,%ebp 
80483c7:  83 e4 f0    and $0xfffffff0,%esp 
80483ca:  83 ec 10    sub $0x10,%esp 
80483cd:  b8 a0 84 04 08   mov $0x80484a0,%eax 
80483d2:  89 04 24    mov %eax,(%esp) 
80483d5:  e8 22 ff ff ff   call 80482fc <[email protected]> 
80483da:  c9      leave 
80483db:  c3      ret  
80483dc:  90      nop 
80483dd:  90      nop 
80483de:  90      nop 
80483df:  90      nop 

Da ausführbare Linux basieren normalerweise auf 0x8048000 ist die Adresse des Arguments printf auf einem von 0x00004a0 von Anfang der binären Offset:


xxd /tmp/hello | grep 00004a0 

00004a0: 4865 6c6c 6f2c 2077 6f72 6c64 210a 0000 Hello, world!... 

die Adresse des Strings also gedrückt wird, und printf wird mit, dass man arg genannt. Nichts magisches auf diesem Level, also wurde all das lustige Zeug von gcc gemacht.