2010-11-11 46 views
22

Tôi chỉ thấy này hereMã C: Các mã này hoạt động như thế nào?

#include <stdio.h> 

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

Điều này không là print "Hello World!"

Nhưng những gì thực sự đang diễn ra ở đây?

Điều tốt nhất tôi có thể đoán là nó được biên soạn và ném vào phía trên cùng của việc thực hiện ngăn xếp, nhưng cú pháp thậm chí không nhìn pháp lý đối với tôi ...

Trả lời

21

Mã này sử dụng tính năng mảng có độ dài biến đổi của C99, cho phép bạn khai báo các mảng có kích thước chỉ được biết đến trong thời gian chạy. printf trả lại số nguyên bằng số ký tự đã được in thực sự, vì vậy mã in "Hello, world!" đầu tiên và sử dụng giá trị trả về là kích thước của argv. Chức năng main không làm gì cả. Cuộc gọi thực tế tới số printf có thể đi vào mã khởi động do trình biên dịch tạo ra, lần lượt gọi số main.

Edit: tôi chỉ kiểm tra tháo gỡ của các mã được tạo bởi gcc và dường như cuộc gọi đến printf đi bên main bản thân, trước khi các mã khác.

+0

Cuộc gọi đến 'printf' chắc chắn không đi vào bất kỳ mã khởi động trước' 'nào'. –

+0

@R ..: Đó chỉ là phỏng đoán và điều tốt nhất tôi có thể thực hiện. Nơi nào khác nó có thể được thực hiện? – casablanca

+0

@R ..: Đừng bận tâm, bạn đúng về điều đó và tôi đã trả lời câu hỏi của riêng tôi. :) – casablanca

-1

Tôi không phải chuyên gia C, nhưng có vẻ như các đối số dòng lệnh được khai báo cùng lúc với main.

+4

No. Prasoon và casablanca đã hiểu. – dmckee

4

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

printf() trả về số ký tự in.

Vì vậy

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

tương đương với

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

cộng với một cuộc gọi đến printf() mà in "Hello World"

+1

Điều đó chỉ đúng nếu printf thành công trong việc in tất cả các ký tự. – Puppy

5

Nếu tôi tìm ra cách trình biên dịch phân tích nó, tôi sẽ cập nhật này, nhưng ít nhất cần phải có không phỏng đoán như thế nào nó được biên dịch:


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 

Kể từ khi thực thi Linux dựa bình thường tại 0x8048000 , địa chỉ của đối số để printf là tại một bù đắp của 0x00004a0 từ khi bắt đầu nhị phân:


xxd /tmp/hello | grep 00004a0 

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

Vì vậy, địa chỉ của chuỗi được đẩy, và printf được gọi với một arg. Không có gì huyền diệu ở cấp độ đó, vì vậy tất cả những thứ thú vị được thực hiện bởi gcc.

Các vấn đề liên quan