ทำไม main ไม่คืนค่า 0 ที่นี่?


116

ฉันเพิ่งอ่าน

ร่างคณะกรรมการ ISO / IEC 9899: 201x - 12 เมษายน 2554

ซึ่งฉันพบภายใต้ 5.1.2.2.3 การยุติโปรแกรม

..reaching the } that terminates the main function returns a value of 0. 

หมายความว่าหากคุณไม่ได้ระบุคำสั่งส่งคืนใด ๆ ในmain()และหากโปรแกรมทำงานได้สำเร็จที่วงเล็บปีกกาปิด} ของ main จะส่งกลับ 0

แต่ในรหัสต่อไปนี้ฉันไม่ได้ระบุคำสั่งส่งคืนใด ๆ แต่ก็ไม่ส่งคืน 0

#include<stdio.h>
int sum(int a,int b)
{
return (a + b);
}

int main()
{
    int a=10;
    int b=5;
    int ans;    
    ans=sum(a,b);
    printf("sum is %d",ans);
}

รวบรวม

gcc test.c  
./a.out
sum is 15
echo $?
9          // here it should be 0 but it shows 9 why?

69
+1 สำหรับการมีความอดทนในการอ่านข้อมูลจำเพาะ .....
Asher

16
gccโดยตัวมันเอง (สำหรับเวอร์ชัน 4.6.2) รวบรวมภาษาที่คล้ายกันมาก แต่ไม่เหมือน C มันรวบรวม GnuC89 ซึ่งเป็นภาษาที่ "หลวม ๆ " ตาม C89
PMG

2
ไม่จำเป็นต้องใส่ วงเล็บในreturnคำสั่งใน ควรจะเป็น sum()int main()int main(void)
Keith Thompson

24
สับสน! = พิมพ์ผิด บนแป้นพิมพ์ '0' และ 'o' ของฉันอยู่ใกล้พอที่จะทำให้เป็นอย่างหลังได้อย่างง่ายดาย ;-)
The111

2
IMHO เป็นข้อกำหนดที่ค่อนข้างโง่เนื่องจากบังคับให้คอมไพเลอร์จัดการฟังก์ชัน "main" ด้วยวิธีพิเศษโดยการเพิ่ม "return 0" โดยนัย ดังนั้นฟังก์ชันที่ชื่อ "main" จะทำงานในลักษณะที่แตกต่างกันเล็กน้อย สิ่งที่เกี่ยวกับการตรวจสอบเวลาคอมไพล์ ("ไม่มีค่าส่งคืน" ที่คล้ายกัน)?
Giuseppe Guerrini

คำตอบ:


141

กฎดังกล่าวถูกเพิ่มเข้ามาในมาตรฐาน C เวอร์ชัน 1999 ใน C90 สถานะที่ส่งคืนไม่ได้กำหนดไว้

คุณสามารถเปิดใช้งานได้โดยส่ง-std=c99ไปที่ gcc

ในฐานะที่เป็นบันทึกด้านข้างมีการส่งคืน 9 ที่น่าสนใจเพราะเป็นการกลับมาprintfซึ่งเขียนเพียง 9 อักขระ


40
หรือจะเพิ่มreturn 0;ก่อนปิด}ก็ได้ ไม่เป็นอันตรายและทำให้โปรแกรมของคุณพกพาไปยังคอมไพเลอร์รุ่นเก่าได้
Keith Thompson

2
@ Mr.32: สังเกตดีๆ printf () จะคืนค่าความยาวของสตริงดังนั้นจึงเป็น 9 ซึ่งเป็น "return" ของ main (โดยไม่ใช้ -std = c99)
Hicham

1
@cnicutar: โดยทั่วไปแล้วฟังก์ชันจะไม่ส่งคืนค่าเล็ก ๆ บนสแต็กเพราะมันจะเกี่ยวข้องกับป๊อปการผลักและการกระโดดมากกว่าแค่การเคลื่อนที่และการส่งคืนดังนั้นจึงเกือบจะเป็นรีจิสเตอร์eaxโดยเฉพาะบน x86
Jon Purdy

8
ใช่ x86 API มักจะส่งคืนจำนวนเต็มเหมือนค่าผ่านeaxรีจิสเตอร์ ดูen.wikipedia.org/wiki/X86_calling_conventions#cdeclสำหรับข้อมูลเพิ่มเติม
Sylvain Defresne

2
หลายครั้งที่ฉันเห็นโค้ดเช่น "int foo (void) {bar ();}" โดยที่ "return bar ()" มีจุดมุ่งหมาย รหัสนี้ใช้งานได้ดีกับโปรเซสเซอร์ส่วนใหญ่แม้ว่าจะมีข้อบกพร่องที่ชัดเจนก็ตาม
ugoren

15

ส่งคืนค่าส่งกลับprintfซึ่งเป็นจำนวนอักขระที่พิมพ์ออกมาจริงๆ


คำถามไม่ได้พูดถึงสิ่งที่พิมพ์ในคอนโซลเมื่อรันโปรแกรมจะพูดถึงค่าส่งคืนของโปรแกรม: (คุณสามารถรับได้ใน linux ด้วยคำสั่ง schell: echo $? และใน windows ด้วย: echo% errorlevel% )
Hicham

1
@eharvest แม้ว่าฉันจะไม่รู้วิธีตรวจสอบ%errorlevel%ใน Windows แต่ความแตกต่างระหว่างรหัสออกและค่าส่งคืนของmainใน linux หรือไม่
Summer_More_More_Tea

1
@eharvest: คำตอบไม่ได้พูดถึงสิ่งที่พิมพ์ในคอนโซลด้วย มันพูดเกี่ยวกับค่าตอบแทนของprintfซึ่งในกรณีนี้คือ 9 ที่แล้วได้รับ "การเลื่อน" เป็นรหัสออกจากmainอย่างใดเมื่อใช้รุ่น GCC บางอย่าง
AH

ขอโทษ ความผิดพลาดของฉัน. คุณพูดถูก ฉันอ่านคำตอบนี้เร็วเกินไป: /
Hicham

1
โปรดทราบว่าสิ่งนี้ไม่รับประกัน ใน C89 / C90 สถานะกลับมาในกรณีนี้จะไม่ได้กำหนด ; มันอาจเป็นอะไรก็ได้ มันเกิดขึ้นเพื่อส่งคืน 9 เนื่องจากคอมไพเลอร์ไม่ได้พยายามส่งคืนสิ่งอื่นใด คอมไพเลอร์อื่น ๆ มักจะทำงานแตกต่างกัน
Keith Thompson

6

โดยปกติค่าที่ส่งคืนจากฟังก์ชันจะถูกเก็บไว้ใน eax register ของ cpu ดังนั้นคำสั่ง "return 4;" มักจะรวบรวมเป็น

mov eax, 4;
ret;

และคืนค่า x (ขึ้นอยู่กับคอมไพเลอร์ของคุณ) จะเป็นดังนี้:

mov eax, [ebp + 4];
ret;

หากคุณไม่ระบุค่าที่ส่งคืนคอมไพลเลอร์จะยังคงคาย "ret" ออกมา แต่ไม่เปลี่ยนค่าของ eax ดังนั้นผู้โทรจะคิดว่าสิ่งที่เคยเหลืออยู่ใน eax register ก่อนหน้านี้คือค่าส่งคืน สำหรับตัวอย่างนี้โดยปกติจะเป็นค่าส่งกลับ printf แต่คอมไพเลอร์ต่างกันจะสร้างรหัสเครื่องที่แตกต่างกันและใช้การลงทะเบียนบางอย่างแตกต่างกัน

นี่เป็นคำอธิบายที่เรียบง่ายรูปแบบการโทรที่แตกต่างกันและแพลตฟอร์มเป้าหมายจะมีบทบาทสำคัญ แต่ควรมีข้อมูลเพียงพอที่จะอธิบายสิ่งที่เกิดขึ้น 'เบื้องหลัง' ในตัวอย่างของคุณ

หากคุณมีความเข้าใจพื้นฐานเกี่ยวกับแอสเซมเบลอร์คุณควรเปรียบเทียบการแยกชิ้นส่วนของคอมไพเลอร์ที่แตกต่างกัน คุณอาจพบว่าคอมไพเลอร์บางตัวกำลังล้างทะเบียน eax เพื่อเป็นการป้องกัน


11
คอมไพเลอร์อาจทำเช่นนั้น ยังไม่ได้กำหนด มันอาจเลือกที่จะยิงคุณด้วยตลับหมึกพิมพ์แทน
Lightness Races ใน Orbit

@LightnessRacesinOrbit undefined ไม่เหมือนกับที่คาดเดาไม่ได้กล่าวคือจาก "ถ้าคอมไพเลอร์ทำ X มันจะเป็นไปตามมาตรฐาน" มันไม่เป็นไปตามเหตุผล "คอมไพเลอร์อาจทำ X"
Owen

@ โอเว่น: อ้างอิงข้อความมาตรฐานที่กำหนดสิ่งนี้
Lightness Races ในวงโคจร

2
@LightnessRacesinOrbit ขอโทษที่ไม่ค่อยได้ติดตาม ฉันเดาว่าสิ่งที่ฉันต้องการจะพูดจริงๆก็คือฉันคิดว่าข้อมูลเชิงลึกภายใต้ฝากระโปรงเช่น noggin182 ที่ให้ไว้ในคำตอบนี้มีประโยชน์อย่างเหลือเชื่อสำหรับการดีบัก เมื่อโปรแกรมของคุณให้ผลลัพธ์ที่ไม่คาดคิดหลายครั้งที่คุณไม่รู้ว่าพวกเขามาจากไหนหรือแม้แต่จะดูโค้ดที่ใดและการมีความเข้าใจในรายละเอียดการใช้งานสามารถชี้ให้คุณไปในทิศทางที่ถูกต้องได้
Owen

@ โอเว่นฉันไม่เคยอ้างสิทธิ์เป็นอย่างอื่น
Lightness Races in Orbit
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.