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

Почему этот код по-прежнему работает?

Какой-то старый код, с которым я только что столкнулся:

MLIST * new_mlist_link()
{
    MLIST *new_link = (MLIST * ) malloc(sizeof(MLIST));
    new_link->next  = NULL;
    new_link->mapi  = NULL;
    new_link->result = 0;
}

Это вызывалось для создания связанного списка, однако я заметил, что нет инструкции:

return new_link;

Даже без возвращаемого утверждения список по-прежнему создается правильно. Почему это произошло?

Изменить: Платформа: Mandriva 2009 64bit Linux 2.6.24.7-сервер GCC 4.2.3-6mnb1

Edit: Funny... этот код также успешно работал примерно на 5 различных установках Linux, всех разных версиях/ароматах, а также на Mac.

4b9b3361

Ответ 1

В 32-разрядной Windows большую часть времени возвращаемое значение из функции остается в регистре EAX. Подобные установки используются в других операционных системах, хотя, конечно, они специфичны для компилятора. Эта конкретная функция предположительно сохранила новую переменную new_link в том же месте, поэтому, когда вы вернулись без возврата, переменная в этом местоположении была обработана вызывающим абонентом как возвращаемое значение.

Это не переносится и очень опасно для этого, но также является одной из мелочей, которые делают программирование на C таким большим удовольствием.

Ответ 2

Возможно, он просто использовал регистр EAX, который обычно сохраняет возвращаемое значение последней вызванной функции. Это совсем не хорошая практика! Поведение для такого рода вещей - undefined.. Но здорово видеть работу; -)

Ответ 3

Это в основном удача; по-видимому, компилятор, похоже, придерживается new_link в том же месте, в котором он будет придерживаться возвращаемого значения.

Ответ 4

Чтобы избежать этой проблемы, используйте:

-Wreturn-type:

Предупреждать, когда функция определена с типом возвращаемого значения, которое по умолчанию соответствует int. Также предупреждайте о любом возвращаемом выражении без возвращаемого значения в функции, тип возвращаемого которой не является недействительным (падение с конца тела функции считается возвратом без значения), а также оператор return с выражением в функции, return-type недействителен.

-Werror=return-type, чтобы превратить это в ошибку:

Вывести указанное предупреждение в сообщение об ошибке. Спецификатор для предупреждения добавляется, например -Werror = переключает предупреждения, управляемые -Wswitch, на ошибки. Этот переключатель принимает отрицательную форму, которая должна использоваться для отрицания -Werror для определенных предупреждений, например -Wno-error = switch делает -Wswitch предупреждениями не являются ошибки, даже когда -Werror действует. Вы можете использовать опцию -fdiagnostics-show-option, чтобы каждое контролируемое предупреждение было изменено с помощью опции, которая ее контролирует, чтобы определить, что использовать с этой опцией.

(from Параметры предупреждения GCC)

Ответ 5

Это работает по совпадению. Вы не должны полагаться на это.

Ответ 6

Скорее всего, это будет очень сложно найти ошибку. Я не знаю, где я его читаю, но я помню, что если вы забудете включить оператор return, большинство компиляторов по умолчанию вернут void.

Вот короткий пример:

#include <iostream>

using namespace std;

int* getVal();

int main()
{
        int *v = getVal();
        cout << "Value is: " << *v << endl;
        return 0;
}

int* getVal()
{
        // return nothing here
}

Для меня это тоже работает. Однако, когда я запускаю его, я получаю ошибку сегмента. Так что это действительно undefined. Просто потому, что он компилируется, не означает, что он сработает.

Ответ 7

Это работает, потому что в 1940 году, когда был создан язык C, ключевого слова return не было. Если вы посмотрите в разделе "функции" стандарта C43 MINSI, он должен это сказать по этому вопросу (между прочим):

16.4.3b For backwards compatibility the EAX register MUST be used to return
        the address of the first chunk of memory allocated by malloc.

</humour>

Ответ 8

Вероятно, совпадение:

Пространство для возвращаемого значения функции назначается заранее. Поскольку это значение не инициализировано, оно могло бы указывать на то же пространство в куче, что и память, выделенная для структуры.