2010-11-11 15 views
22

Właśnie widziałem ten hereKod C: Jak one działają?

#include <stdio.h> 

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

Co to print "Hello World!"

Ale co się tutaj dzieje?

Najlepszym mogę domyślać się, że zostanie opracowany i rzucony na szczycie stosu wykonania, ale składnia nawet nie patrzeć prawny do mnie ...

Odpowiedz

21

Kod wykorzystuje funkcję tablic o zmiennej długości C99, która pozwala zadeklarować tablice, których rozmiar jest znany tylko w czasie wykonywania. printf zwraca liczbę całkowitą równą liczbie znaków, które zostały faktycznie wydrukowane, więc kod drukuje "Hello, world!" pierwszy i używa wartości zwracanej jako rozmiaru argv. Funkcja main sama nic nie robi. Samo połączenie z numerem printf prawdopodobnie przechodzi do kodu startowego wygenerowanego przez kompilator, który z kolei wywołuje main.

Edit: Właśnie sprawdziłem demontaż kodu wygenerowanego przez gcc i wydaje się, że wezwanie do printf idzie wewnątrz main się przed każdym innym kodem.

+0

Wywołanie 'printf' z pewnością nie wpisuje się w żaden wcześniejszy kod startowy. –

+0

@R ..: To był tylko domysł i najlepsze, co mogę zrobić. Gdzie indziej można go było wykonać? – casablanca

+0

@R ..: Nieważne, masz rację, a ja odpowiedziałem na własne pytanie. :) – casablanca

-1

nie jestem ekspertem C, ale wygląda na to, że argumenty wiersza poleceń są zadeklarowane w tym samym czasie co main.

+4

Nie. Prasoon i Casablanca mają to. – dmckee

4

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

printf() zwraca liczbę znaków drukowanych.

Więc

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

jest równoważna

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

plus wezwanie do printf() która drukuje "Hello World"

+1

Jest to prawdą tylko wtedy, gdy printf kończy drukowanie wszystkich znaków. – Puppy

5

Jeśli dowiem się, jak kompilator analizowany go, będę aktualizować ten, ale przynajmniej nie musi być bez zgadywania, jak to kompilowane:


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 

Od wykonywalnych Linux oparte są zwykle na 0x8048000 , adres argument printf jest przesunięcie 0x00004a0 od początku binarny:


xxd /tmp/hello | grep 00004a0 

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

Tak, adres łańcucha jest wciśnięty, a printf jest wywołany z tego jednego arg. Nic magicznego na tym poziomie, więc wszystkie fajne rzeczy zostały wykonane przez gcc.

Powiązane problemy