ก่อนที่จะเข้าสู่ส่วนของคำถามเกี่ยวกับสิ่งที่เกิดขึ้นสิ่งสำคัญคือต้องชี้ให้เห็นว่าโปรแกรมมีรูปแบบไม่ถูกต้องตามรายงานข้อบกพร่อง 1886: การเชื่อมโยงภาษาสำหรับ main () :
[... ] โปรแกรมที่ประกาศตัวแปรหลักในขอบเขตส่วนกลางหรือที่ประกาศชื่อหลักด้วยการเชื่อมโยงภาษา C (ในเนมสเปซใด ๆ ) มีรูปแบบไม่ถูกต้อง [... ]
clang และ gcc เวอร์ชันล่าสุดทำให้เกิดข้อผิดพลาดและโปรแกรมจะไม่คอมไพล์ ( ดูตัวอย่าง gcc live ):
error: cannot declare '::main' to be a global variable
int main = ( std::cout << "C++ is excellent!\n", 195 );
^
เหตุใดจึงไม่มีการวินิจฉัยใน gcc และ clang เวอร์ชันเก่า รายงานข้อบกพร่องนี้ยังไม่มีการเสนอข้อยุติจนถึงปลายปี 2014 ดังนั้นกรณีนี้จึงเพิ่งเกิดขึ้นอย่างชัดเจนซึ่งต้องได้รับการวินิจฉัย
ก่อนหน้านี้ดูเหมือนว่านี่จะเป็นพฤติกรรมที่ไม่ได้กำหนดเนื่องจากเรากำลังละเมิดข้อกำหนดที่จะต้องมีของร่างมาตรฐาน C ++ จากส่วน3.6.1
[basic.start.main] :
โปรแกรมจะต้องมีฟังก์ชันส่วนกลางที่เรียกว่า main ซึ่งเป็นจุดเริ่มต้นของโปรแกรมที่กำหนดไว้ [... ]
พฤติกรรมที่ไม่ได้กำหนดนั้นไม่สามารถคาดเดาได้และไม่จำเป็นต้องมีการวินิจฉัย ความไม่สอดคล้องที่เราเห็นจากการสร้างพฤติกรรมซ้ำนั้นเป็นพฤติกรรมที่ไม่ได้กำหนดโดยทั่วไป
แล้วโค้ดนั้นทำอะไรได้จริงและทำไมในบางกรณีจึงให้ผลลัพธ์? มาดูกันว่าเรามีอะไรบ้าง:
declarator
| initializer----------------------------------
| | |
v v v
int main = ( std::cout << "C++ is excellent!\n", 195 );
^ ^ ^
| | |
| | comma operator
| primary expression
global variable of type int
เรามีmain
ซึ่งเป็นint ที่ประกาศในเนมสเปซส่วนกลางและกำลังเริ่มต้นตัวแปรมีระยะเวลาการจัดเก็บแบบคงที่ มันเป็นเรื่องการดำเนินงานที่กำหนดไว้ว่าจะเริ่มต้นที่จะเกิดขึ้นก่อนที่ความพยายามที่จะเรียกร้องmain
จะทำ แต่ปรากฏ GCC main
ไม่ทำเช่นนี้ก่อนที่จะเรียก
รหัสใช้ตัวดำเนินการลูกน้ำตัวถูกดำเนินการด้านซ้ายคือนิพจน์ค่าที่ถูกละทิ้งและใช้ที่นี่เพื่อผลข้างเคียงของการเรียกstd::cout
เท่านั้น ผลของผู้ประกอบการจุลภาคเป็นตัวถูกดำเนินการที่เหมาะสมซึ่งในกรณีนี้คือ prvalue ซึ่งได้รับมอบหมายให้ตัวแปร195
main
เราสามารถเห็นsergej ชี้ให้เห็นการแสดงที่สร้างขึ้นซึ่งcout
ถูกเรียกในระหว่างการเริ่มต้นแบบคงที่ แม้ว่าประเด็นที่น่าสนใจกว่าสำหรับการสนทนาเห็นเซสชัน Godbolt สดจะเป็นดังนี้:
main:
.zero 4
และต่อมา:
movl $195, main(%rip)
สถานการณ์มีแนวโน้มว่าโปรแกรมกระโดดไปสัญลักษณ์main
คาดหวังว่ารหัสที่ถูกต้องที่จะมีและในบางกรณีจะ SEG ดังนั้นหากเป็นเช่นนั้นเราคาดว่าการจัดเก็บรหัสเครื่องที่ถูกต้องในตัวแปรmain
อาจนำไปสู่โปรแกรมที่ใช้งานได้สมมติว่าเราอยู่ในส่วนที่อนุญาตให้เรียกใช้โค้ด เราสามารถมองเห็นนี้ 1984 IOCCC รายการไม่เพียงแค่นั้น
ดูเหมือนว่าเราสามารถรับ gcc เพื่อทำสิ่งนี้ใน C โดยใช้ ( ดูสด ):
const int main = 195 ;
มันเกิดความผิดพลาดหากตัวแปรmain
ไม่ใช่ const น่าจะเป็นเพราะมันไม่ได้อยู่ในตำแหน่งที่เรียกใช้งานได้ Hat Tip สำหรับความคิดเห็นนี้ที่นี่ซึ่งทำให้ฉันมีความคิดนี้
นอกจากนี้โปรดดูคำตอบของ FUZxxl ที่นี่สำหรับคำถามรุ่น C เฉพาะ