Подтвердить что ты не робот

Что (на самом деле) происходит, когда вызывается функция с предупреждением "control достигает конца не-void функции"?

Я знаю, что это означает, я просто задаюсь вопросом, почему это сообщение не, а просто предупреждение?

Что происходит в этом случае? Например, предположим, что у меня есть функция

int f()
{
}

и что происходит, когда я это называю?
Компилятор добавляет возвращение "неинициализированного" int в этом случае?
Или недостающее возвращение может привести к повреждению стека?
Или это (абсолютно) undefined поведение?

Протестировано с помощью gcc 4.1.2 и 4.4.3


РЕДАКТИРОВАТЬ: Чтение ответов. Я понимаю одно, читаю комментарии - другое.

ОК, давайте суммируем: это поведение undefined. Тогда это означает, что можно привести к повреждению стека, не так ли? (это даже означает, что мой компьютер может начать бросать гнилые помидоры через меня через микрофонный домкрат, кричать - "что ты сделал???" ).

Но если это так, то почему в главном ответе здесь говорится, что коррупция стека не может произойти и, в то же время, что поведение undefined?

И undefined в отношении? Вызывающий, который пытается использовать "не возвращенное значение" или просто конец функции, является undefined, если он должен возвращать значение, но это не так?

Или это не undefined поведение, и только пользователь, который пытается использовать значение (которое не возвращается, d'oh!) будет "получать" undefined значение? Другими словами - просто какая-то ценность для мусора, и больше ничего не может произойти?

4b9b3361

Ответ 1

A: Нет, недостающее возвращение не приведет к повреждению стека

A: Да, поведение было бы "undefined", если вызывающий пользователь попытался прочитать и/или использовать возвращаемое значение (undefined!).

PS:

Здесь цитата для С++:

С++ 03 §6.6.3/2:

Оттекание конца функции эквивалентно возврату без стоимость; это приводит к поведению undefined при возврате значения функция.

Ответ 2

Вы спрашивали о C и С++. Правила на разных языках различны.

В C поведение undefined, только если вызывающий объект пытается использовать значение, возвращаемое функцией. Если у вас есть:

int func(void) {
    /* no return statement */
}

...

func();

то поведение корректно определено.

В С++ поведение undefined (если функция вообще вызывается), пытается ли вызывающий объект использовать результат или нет. (Это по историческим причинам: перед ANSI C не было ключевого слова void, а функции, которые не предназначались для возврата значения, обычно определялись (неявно) для возврата int.)

Ответ Джона Боде уже цитировал проект N1570 стандарта ISO C, 6.9.1p12:

Если достигается }, которая завершает функцию, и значение вызов функции используется вызывающим, поведение undefined.

И paulsm4 привел стандарт С++; цитируя последнюю версию 2011 года, 6.6.3p2:

Вытекание конца функции эквивалентно return без стоимость; это приводит к поведению undefined при возврате значения функция.

Исторические причины, приведшие к тому, что C разрешающая функция возврата значения не возвращает значение, если значение не используется вызывающим, не относится к С++, на дизайн которого не оказало сильное влияние необходимо избегать нарушения старого (pre-ANSI C) кода.

В обоих C (начиная с C99) и С++ функция main является частным случаем; достижение } main без выполнения возврата эквивалентно a return 0;. (C разрешает main возвращать определенный для реализации тип, отличный от int; в этом (редком) случае падение с конца возвращает неопределенный статус завершения в среду хоста.)

Конечно, исключение оператора return из функции возврата значения или наличие возможного пути выполнения, не достигающего оператора return, является плохой идеей. Это имеет смысл только в древнем устаревшем коде C, который использует int как stand-in для void, а в main (хотя даже для main мне лично нравится иметь явный return 0;).

Ответ 3

Стандарт рассматривает его undefined.

На практике будет считана память или регистр, зарезервированный для возвращаемого значения. Что бы там ни было.

Ответ 4

C 2011 проект N1570

6.9.1 Определения функций

...
12 Если достигнут }, который завершает функцию, а значение вызова функции используется вызывающий, поведение не определено.

"Undefined" просто означает, что компилятор не требуется стандартом языка для обработки этой ситуации каким-либо особым образом; любое действие считается "правильным". Компилятор может свободно выполнять диагностику и останавливать перевод, или выдавать диагностический и полный перевод (это то, что вы видите), или просто игнорировать проблему полностью.

Что касается реального поведения во время выполнения, это зависит от таких вещей, как:

  • как вызывающий абонент использует возвращаемое значение?
  • что используется вызывающее соглашение?
  • Как работает базовая архитектура?

и т.д..

Ответ 5

Компилятор не обязан диагностировать это, потому что в некоторых случаях он жесткий. Таким образом, правило состоит в том, что поведение undefined.

Ответ 6

Я провел простой тест на Linux 64 бит, GCC 4.63 Посмотрите на практике, как GCC собирает такую ​​вещь.

Я создал простой пример

Это test.c с нормальным возвратным значением

int main()
{
    return 0;
}

Это выход ассемблера GCC для test.c:

main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $0, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc

Это test2.c без возвращаемого значения

int main()
{
}

Это выход ассемблера GCC для test2.c:

main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc

В основном мы видим, что следующая строка отсутствует.

    movl    $0, %eax

Эта строка перемещает значение в регистр eax, который является возвращаемым значением функции. Если функция использовалась в реальной жизни, регистр eax, который, вероятно, содержит значение мусора, будет представлять возвращаемое значение main()...

Ответ 7

Я провел тест в g ​​++. Кажется, что вы получаете объект со случайными вещами в нем, т.е. Точка не вызывает никакого конструктора. Когда я имею дело только с целыми числами, это означает, что я получаю случайное число. Когда я разбираюсь со строками, я получаю нарушение сегментации. Я не знаю, что вы имели в виду под коррупцией, но это плохо.

Что касается вашего первого вопроса, я бы пошел еще дальше. Я хочу знать, почему это предупреждение, которое отключено по умолчанию! Мне это кажется очень важным.

#include <string>
#include <iostream>

class X
{
private:
  int _x;
public:
  X(int x) : _x(x) { } // No default construcor!                            
  int get() const { return _x; }
};

X test1(int x)
{
  if (x > 0)
    return X(x);
  // warning: control reaches end of non-void function                      
}

class Y
{
private:
  std::string _y;
public:
  Y(std::string y) : _y(y) { } // No default construcor!                    
  std::string get() const { return _y; }
};

Y test2(std::string y)
{
  if (y.length() > 3)
    return Y(y);
  // warning: control reaches end of non-void function                      
}

int main(int, char**)
{
  std::cout<<"4 -> "<<test1(4).get()<<std::endl;
  std::cout<<"-4 -> "<<test1(-4).get()<<std::endl;
  std::cout<<"stop -> "<<test2("stop").get()<<std::endl;
  std::cout<<"go -> "<<test2("go").get()<<std::endl;
}