Чому main не повертає 0 тут?


116

Я просто читав

Проект комітету ISO / IEC 9899: 201x - 12 квітня 2011 року

в якому я виявив під 5.1.2.2.3 завершення програми

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

це означає, що якщо ви не вкажете жодного твердження про повернення в 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 за терпіння читати характеристики .....
Ашер

16
gccсам по собі (для версії 4.6.2) компілює мову, дуже схожу, але не зовсім схожу на C. Він компілює GnuC89 - мову, "вільно" на основі C89.
pmg

2
Круглі дужки у returnвикладі в sum()не потрібні. int main()повинно бути int main(void).
Кіт Томпсон

24
Плутанина! = Помилка. На моїй клавіатурі '0' і 'o' досить близькі, щоб легко було останнє. ;-)
The111

2
IMHO - досить дурна специфікація, оскільки вона змушує компілятора спеціальним чином керувати "основною" функцією, додаючи неявне "return 0". Тож функція під назвою "головна" поводиться дещо по-іншому. Як щодо перевірки часу компіляції ("немає значення повернення" аналогічно)?
Джузеппе Герріні

Відповіді:


141

Це правило було додано у версії стандарту С 1999 року. У C90 повернення статусу не визначене.

Ви можете ввімкнути це, перейшовши -std=c99на gcc.

Як бічна примітка, цікаво повертається 9, тому що це повернення, printfяке щойно написало 9 символів.


40
Або ви можете просто додати return 0;перед закриттям }. Це нешкідливо і робить вашу програму портативною для старих компіляторів.
Кіт Томпсон

2
@ Mr.32: добре спостереження, printf () повертає довжину рядка, так що це 9, яке є "поверненням" головного (без використання -std = c99).
Хічем

1
@cnicutar: Функції, як правило, не повертають малі значення на стеку - тому що це передбачає поп, поштовх і стрибок, а не просто переміщення і повернення - так це майже напевно реєстр, eaxзокрема на x86.
Джон Перді

8
Так, API x86 зазвичай повертає цілочисельне значення через 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% )
Хічем

1
@eharvest Хоча я не знаю, як перевірити %errorlevel%в Windows, чи є різниця між кодом виходу та значенням повернення mainв Linux?
Summer_More_More_Tea

1
@eharvest: Відповідь не говорить про те, що надруковано на консолі. Це говорить про возвращаемом значенні з printfяких в даному випадку є 9 , який потім отримує «підвищений» в якості коду виходу з mainпри використанні деяких версій GCC так чи інакше.
AH

вибачте. моя помилка. ти правий. я прочитав цю відповідь занадто швидко: /
Hicham

1
Зауважте, що це не гарантується. У C89 / C90 статус, що повертається в цьому випадку, не визначений ; це може бути що завгодно. Повертається 9, тому що компілятор не докладав зусиль, щоб повернути щось інше. Інші компілятори, ймовірно, поводяться інакше.
Кіт Томпсон

6

Значення, що повертається з функції, зазвичай зберігається в регістрі eax процесора, тому вислів "return 4;" зазвичай збирається в

mov eax, 4;
ret;

і повернути x (залежно від вашого компілятора) було б щось на зразок:

mov eax, [ebp + 4];
ret;

якщо не вказати значення повернення, компілятор все одно випхне "ret", але не змінить значення eax. Таким чином, абонент подумає, що те, що коли-небудь було залишене в реєстрі eax, - це повернене значення. У цьому прикладі, як правило, це буде returnf printf, але різні компілятори генерують різні машинні коди та використовують різні регістри по-різному.

Це спрощене пояснення, різні конвенції про виклики та цільові платформи відіграватимуть життєво важливу роль, однак у вашому прикладі має бути достатньо інформації для пояснення того, що відбувається «поза кадром».

Якщо ви маєте основне розуміння асемблера, варто порівняти розбирання різних компіляторів. Ви можете виявити, що деякі компілятори очищають реєстр eax як гарантію.


11
Компілятор може це зробити. Це не визначено. Натомість він може вирішити застрелити вас у голову картриджем для принтера.
Гонки легкості на орбіті

@LightnessRacesinOrbit undefined - це не те саме, що непередбачувано, тобто з "якщо компілятор зробить X, він буде відповідати стандарту", це не логічно випливає "компілятор може зробити X"
Owen

@Owen: Цитуйте стандартний уривок, що визначає це.
Гонки легкості по орбіті

2
@LightnessRacesinOrbit Вибачте, я не дуже дотримуюся. Я думаю, що я дійсно хочу сказати, що я вважаю, що уявлення про те, що є під капотом, наприклад, noggin182, наведені у цій відповіді, неймовірно корисні для налагодження. Коли ваша програма дає неочікувані результати, багато разів ви не знаєте, звідки вони беруться, або навіть звідки в коді шукати, і розуміння деталей реалізації може ввести вас у правильному напрямку.
Оуен

@Owen Я ніколи не стверджував інакше
гонки легкості в
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.