2011-10-05 29 views
59

Bất cứ ai có thể vui lòng giải thích những gì đang xảy ra trong mã C++ này. Nó biên dịch và thực hiện tốt trên Linux.Điều gì đang xảy ra ở đây trong mã C++ này?

#include <iostream> 
using namespace std; 
int main = (cout << "Hello world!\n", 195); 
+3

Ở đây nó biên dịch, nhưng segfaults. – evnu

+6

Bạn tìm mã đó ở đâu? (Tôi muốn ở xa, cách xa đó, bất kể nó là gì. Mã này khó chịu.) – Mat

+54

Tại sao một người nào đó bỏ phiếu để đóng này? Đồng ý, đó là một đoạn mã khó chịu và người ta không nên viết mã như vậy, nhưng liệu lý do đó đủ để bỏ phiếu để đóng? Tôi thấy quá nhiều Q chỉ bình chọn để đóng những ngày này bởi vì mọi người không * như * the Q. Xin lỗi nhưng đó không phải là một tiêu chí hợp lệ để đóng Q. –

Trả lời

68

Số "195" là mã hướng dẫn RET trên x86.

Trình biên dịch C++ (gcc trong trường hợp của tôi) không thể nhận ra rằng "chính" không được khai báo là hàm. Trình biên dịch chỉ thấy rằng có biểu tượng "chính" và giả định rằng nó chỉ một hàm.

Các mã C++

int main = (cout << "Hello world!\n", 195); 

được khởi tạo một biến tại file-phạm vi. Mã khởi tạo này được thực thi trước khi môi trường C/C++ gọi chính(), nhưng sau khi nó khởi tạo biến "cout". Bản in khởi tạo "Hello, world! \ N" và đặt giá trị của biến "main" thành 195. Sau khi tất cả khởi tạo xong, môi trường C/C++ thực hiện cuộc gọi đến "main". Chương trình trả về ngay lập tức từ cuộc gọi này bởi vì chúng tôi đặt lệnh RET (mã 195) tại địa chỉ "chính". sản lượng GDB

mẫu:

$ gdb ./a 
(gdb) break _fini 
Breakpoint 1 at 0x8048704 
(gdb) print main 
$1 = 0 
(gdb) disass &main 
Dump of assembler code for function main: 
    0x0804a0b4 <+0>:  add %al,(%eax) 
    0x0804a0b6 <+2>:  add %al,(%eax) 
End of assembler dump. 
(gdb) run 
Starting program: /home/atom/a 
Hello world! 

Breakpoint 1, 0x08048704 in _fini() 
(gdb) print main 
$2 = 195 
(gdb) disass &main 
Dump of assembler code for function main: 
    0x0804a0b4 <+0>:  ret  
    0x0804a0b5 <+1>:  add %al,(%eax) 
    0x0804a0b7 <+3>:  add %al,(%eax) 
End of assembler dump. 
+8

+1 để có giải thích chi tiết về lý do tại sao nó * hoạt động. – new123456

+3

@ new123456: về lý do tại sao nó * đôi khi * hoạt động. Như đã nói, trên OS X nó bị treo, và tiêu chuẩn xác định rằng đây không phải là một chương trình C++ hợp lệ. –

+0

Thú vị, tôi nghĩ phân đoạn dữ liệu và phân đoạn mã là các phân đoạn riêng biệt và không nên chuyển sang một địa chỉ trong phân đoạn dữ liệu. Nhưng có lẽ điều này là không đúng cho tất cả các triển khai. – Giorgio

1

Nó sẽ thiết lập các biến toàn cầu main (một số nguyên) với giá trị của 195 sau khi in ra Hello world. Bạn sẽ vẫn cần xác định hàm main để nó thực thi.

+2

Và nếu bạn xác định hàm main, bạn có hành vi không xác định (vì ODR). –

39

Đây không phải là chương trình C++ hợp lệ. Trong thực tế, nó treo cho tôi trên Mac OSX sau khi in "Hello World".

Tháo thấy main là một biến tĩnh, và có initializers cho nó:

global constructors keyed to main: 
0000000100000e20 pushq %rbp 
0000000100000e21 movq %rsp,%rbp 
0000000100000e24 movl $0x0000ffff,%esi 
0000000100000e29 movl $0x00000001,%edi 
0000000100000e2e leave 
0000000100000e2f jmp __static_initialization_and_destruction_0(int, int) 

Tại sao nó in "Hello World"?

Lý do bạn thấy "Hello World" được in ra là vì nó chạy trong khi khởi tạo tĩnh main, biến số nguyên tĩnh. Khởi tạo tĩnh được gọi trước khi chạy C++ thậm chí cố gắng gọi main(). Khi nó xảy ra, nó bị treo, bởi vì main không phải là một hàm hợp lệ, chỉ có một số nguyên 195 trong phần dữ liệu của tệp thực thi.

Câu trả lời khác cho biết đây là hướng dẫn hợp lệ ret và chạy tốt trong Linux, nhưng lỗi này bị treo trên OSX, vì phần này được đánh dấu là không thể thực thi theo mặc định.

Tại sao trình biên dịch C++ không thể cho biết hàm main() không phải là một hàm và dừng với lỗi trình liên kết?

main() có liên kết C, vì vậy trình liên kết không thể cho biết sự khác biệt giữa loại biểu tượng. Trong trường hợp của chúng tôi, _main nằm trong phần dữ liệu.

start: 
0000000100000eac pushq $0x00 
0000000100000eae movq %rsp,%rbp 
... 
0000000100000c77 callq _main ; 1000010b0 
0000000100000c7c movl %eax,%edi 
0000000100000c7e callq 0x100000e16 ; symbol stub for: _exit 
0000000100000c83 hlt 
... 
; the text section ends at 100000deb 
+12

Có [địa điểm trên web] (http://d.hatena.ne.jp/qnighy/20090418/1240064403) tuyên bố rằng điều này được cho là hoạt động trên kiến ​​trúc IA32 vì '195' là' 0xC3', tức là ' Hướng dẫn RET'. Tuy nhiên, trông có vẻ lạ với tôi ... –

+1

@ FrédéricHamidi, cảm ơn, tìm tốt. –

+1

@ FrédéricHamidi Liên kết mà bạn cung cấp cho tuyên bố đó là nghĩa vụ phải làm việc trong C, không phải C++. Tuy nhiên, nó là bất hợp pháp trong cả hai ngôn ngữ, vì lý do hơi khác nhau. Trong cả hai ngôn ngữ, bạn được yêu cầu ** để xác định một hàm toàn cục 'main', trả về' int' và lấy một trong một tập hợp các đối số đã được định nghĩa thực hiện. Bất kỳ định nghĩa nào khác cho 'main' là bất hợp pháp (và sẽ gây ra một trình biên dịch tốt để khiếu nại, đặc biệt là trong trường hợp của C++, trong đó' main' phải được xử lý đặc biệt). –

5

Đây không phải là chương trình hợp pháp, nhưng tôi nghĩ tiêu chuẩn hơi mơ hồ về việc liệu chẩn đoán là bắt buộc hay hành vi không xác định. (Từ chất lượng điểm triển khai, tôi mong đợi chẩn đoán.)

+0

Về QOI: có thể trong trường hợp triển khai hỗ trợ môi trường độc lập, trình biên dịch thích hợp không thể cho biết môi trường tập tin đối tượng được đặt cho (ngay cả khi TU bao gồm các tiêu đề không được bảo đảm là tự do hiện tại, việc thực hiện được phép cung cấp cho họ). Và sau đó người liên kết không thể biết biểu tượng là một chức năng hay không. Tôi đang suy đoán mặc dù, tôi không biết đủ về nội bộ trình biên dịch để nói cho chắc chắn những gì trình biên dịch "nên" biết về lưu trữ vs freestanding. –

+2

@SteveJessop Nếu biên dịch của bạn cho một môi trường tự do, tất nhiên, đó là tất cả việc thực hiện được xác định. Nhưng trình biên dịch nên biết điều này (vì nó phải biết liệu có nên xử lý 'main' đặc biệt hay không), và nếu nó đang xử lý hàm' main' đặc biệt (không mangling, vv) để nó được gọi từ ' crt0', sau đó nó biết rằng 'main' trong không gian tên chung là đặc biệt và có thể tạo ra lỗi cho mã mẫu. –

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