Skip to content

Latest commit

 

History

History
67 lines (50 loc) · 3.81 KB

missing_return.md

File metadata and controls

67 lines (50 loc) · 3.81 KB

Забытый return

Про C/C++ иногда говорят, что это языки, в которых есть специальный синтаксис для написания невалидных программ.

В C/C++ в функции, возвращающей что-то, отличное от void, необязательно должен быть return что-то.

int add(int x, int y) {
    x + y;
}

Это синтаксически корректная функция, которая приведет к неопределенному поведению. Может быть мусор, может быть провал в код следующей далее по коду функции, а может быть и «все нормально».

А с современными версиями компиляторов, например, Clang 18, можно получить совершенно безумный по своей красоте результат сравнимый с работой популярных ИИ-ассистентов

__attribute__((noinline))
bool is_even(int x) {
    switch (x) {
    case 0: return true;
    case 1: return false;
    case 2: return true;
    case 3: return false;
    // Undefined behaviour, do your job!
    }
}

int main() {
    std::cout << is_even(19) << "\n";
    std::cout << is_even(200) << "\n";
}
# Сгенерированный код совершенно точно делает то, что мы и подразумевали!
is_even(int):                            # @is_even(int)
        test    dil, 1
        sete    al
        ret

Особенную боль недоразумение с забытым return может доставить тем, кто пришел в C++ после какого-нибудь ориентированного на выражения языка, в котором похожий код абсолютно нормален:

fn add(x: i32, y: i32) -> i32 {
    x + y
}

Обоснования, почему не обязательно писать в конце функции return, следующие:

  1. В функции может быть ветвление логики. В одной из веток может вызываться код, который не предполагает возврата: бесконечный цикл, исключение, std::exit, std::longjmp или что-то иное, помеченное аттрибутом [[noreturn]]. Проверить на наличие такого кода не всегда возможно.
  2. Функция может содержать ассемблерную вставку со специальным кодом финализации и инструкцией ret.

Проверить наличие формального return, конечно, можно. Но нам разрешили не писать иногда (очень иногда!) чисто формальную строчку, а компиляторам разрешили не считать это ошибкой.


С флагом -Wreturn-type GCC и clang во многих случаях сообщают о проблеме.


Единственным исключением, начиная с C++11, является функция main. В ней отсутствующий return к неопределенному поведению не приводит и трактуется как возврат 0.

Полезные ссылки

  1. https://stackoverflow.com/questions/1610030/why-does-flowing-off-the-end-of-a-non-void-function-without-returning-a-value-no
  2. https://en.cppreference.com/w/cpp/language/attributes/noreturn