C代码:这些甚至如何工作?

时间:2022-09-02 12:14:46

I just saw this here

我刚看到这里

#include <stdio.h>

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

What this does is print "Hello World!"

这样做是打印“Hello World!”

But what's actually going on here?

但是这里到底发生了什么?

The best I can guess is that it gets compiled and thrown at the top of the execution stack, but the syntax doesn't even look legal to me ...

我能猜到的最好的是它被编译并抛出执行堆栈的顶部,但语法对我来说看起来不合法......

4 个解决方案

#1


21  

The code makes use of C99's variable-length array feature, which lets you declare arrays whose size is known only at run-time. printf returns an integer equal to the number of characters that were actually printed, so the code prints "Hello, world!" first and uses the return value as the size of argv. The main function itself does nothing. The actual call to printf itself probably goes into the startup code generated by the compiler, which in turn calls main.

该代码使用了C99的可变长度数组功能,该功能允许您声明仅在运行时知道其大小的数组。 printf返回一个等于实际打印的字符数的整数,因此代码打印出“Hello,world!”首先使用返回值作为argv的大小。主要功能本身什么都不做。对printf本身的实际调用可能会进入编译器生成的启动代码,而编译器又调用main。

Edit: I just checked the disassembly of the code generated by gcc and it appears that the call to printf goes inside main itself, before any other code.

编辑:我刚检查了gcc生成的代码的反汇编,看来printf的调用是在main之前,在任何其他代码之前。

#2


5  

If I figure out how the compiler parsed it, I'll update this, but at least there needs to be no guesswork as to how it compiled:

如果我弄清楚编译器如何解析它,我会更新它,但至少不需要猜测它是如何编译的:


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

Since Linux executables are based normally at 0x8048000, the address of the argument to printf is at an offset of 0x00004a0 from the start of the binary:

由于Linux可执行文件通常位于0x8048000,因此printf参数的地址与二进制文件的起始位置的偏移量为0x00004a0:


xxd /tmp/hello | grep 00004a0

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

So, the address of the string is pushed, and printf is called with that one arg. Nothing magical at that level, so all the fun stuff was done by gcc.

因此,推送字符串的地址,并使用该arg调用printf。没有什么神奇的东西,所以所有有趣的东西都是由gcc完成的。

#3


4  

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

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

printf() returns the number of characters printed.

printf()返回打印的字符数。

So

所以

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

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

is equivalent to

相当于

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

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

plus a call to printf() which prints "Hello World"

加上对printf()的调用,打印出“Hello World”

#4


-1  

I'm no C expert, but it looks like the command line arguments are declared at the same time as main.

我不是C专家,但看起来命令行参数与main同时声明。

#1


21  

The code makes use of C99's variable-length array feature, which lets you declare arrays whose size is known only at run-time. printf returns an integer equal to the number of characters that were actually printed, so the code prints "Hello, world!" first and uses the return value as the size of argv. The main function itself does nothing. The actual call to printf itself probably goes into the startup code generated by the compiler, which in turn calls main.

该代码使用了C99的可变长度数组功能,该功能允许您声明仅在运行时知道其大小的数组。 printf返回一个等于实际打印的字符数的整数,因此代码打印出“Hello,world!”首先使用返回值作为argv的大小。主要功能本身什么都不做。对printf本身的实际调用可能会进入编译器生成的启动代码,而编译器又调用main。

Edit: I just checked the disassembly of the code generated by gcc and it appears that the call to printf goes inside main itself, before any other code.

编辑:我刚检查了gcc生成的代码的反汇编,看来printf的调用是在main之前,在任何其他代码之前。

#2


5  

If I figure out how the compiler parsed it, I'll update this, but at least there needs to be no guesswork as to how it compiled:

如果我弄清楚编译器如何解析它,我会更新它,但至少不需要猜测它是如何编译的:


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

Since Linux executables are based normally at 0x8048000, the address of the argument to printf is at an offset of 0x00004a0 from the start of the binary:

由于Linux可执行文件通常位于0x8048000,因此printf参数的地址与二进制文件的起始位置的偏移量为0x00004a0:


xxd /tmp/hello | grep 00004a0

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

So, the address of the string is pushed, and printf is called with that one arg. Nothing magical at that level, so all the fun stuff was done by gcc.

因此,推送字符串的地址,并使用该arg调用printf。没有什么神奇的东西,所以所有有趣的东西都是由gcc完成的。

#3


4  

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

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

printf() returns the number of characters printed.

printf()返回打印的字符数。

So

所以

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

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

is equivalent to

相当于

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

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

plus a call to printf() which prints "Hello World"

加上对printf()的调用,打印出“Hello World”

#4


-1  

I'm no C expert, but it looks like the command line arguments are declared at the same time as main.

我不是C专家,但看起来命令行参数与main同时声明。