Код C: Как это работает? - PullRequest
22 голосов
/ 11 ноября 2010

Я только что видел это здесь

#include <stdio.h>

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

Что это значит, напечатайте "Hello World!"

Но что на самом деле здесь происходит?

Лучшее, что я могу догадаться, это то, что он компилируется и выбрасывается на вершину стека выполнения, но синтаксис даже не выглядит мне законным ...

Ответы [ 4 ]

21 голосов
/ 11 ноября 2010

Код использует функцию массива переменной длины C99, которая позволяет объявлять массивы, размер которых известен только во время выполнения. printf возвращает целое число, равное количеству фактически напечатанных символов, поэтому код выводит «Hello, world!» сначала и использует возвращаемое значение в качестве размера argv. Функция main сама по себе ничего не делает. Сам фактический вызов printf, вероятно, входит в код запуска, сгенерированный компилятором, который, в свою очередь, вызывает main.

Редактировать: Я только что проверил разбор кода, сгенерированного gcc, и оказалось, что вызов printf идет внутри самого main, перед любым другим кодом.

5 голосов
/ 11 ноября 2010

Если я выясню, как компилятор проанализировал его, я обновлю это, но, по крайней мере, не нужно догадываться, как он компилируется:


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 <printf@plt>
 80483da:       c9                      leave  
 80483db:       c3                      ret    
 80483dc:       90                      nop
 80483dd:       90                      nop
 80483de:       90                      nop
 80483df:       90                      nop

Поскольку исполняемые файлы Linux обычно основаны на 0x8048000, адрес аргумента для printf имеет смещение 0x00004a0 от начала двоичного файла:


xxd /tmp/hello | grep 00004a0

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

Итак, адрес строки передается, и printf вызывается с этим одним аргументом. Ничего волшебного на этом уровне, так что все забавные вещи были сделаны gcc.

4 голосов
/ 11 ноября 2010

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

printf() возвращает количество напечатанных символов.

Итак

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

эквивалентно

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

плюс вызов printf(), который печатает "Hello World"

0 голосов
/ 11 ноября 2010

Я не эксперт по Си, но похоже, что аргументы командной строки объявляются одновременно с main.

...