Недавно я начал использовать CLang для компиляции встроенных программ ARM на C ++.

До этого я использовал GCC и C почти исключительно для встраиваемых систем.

Я заметил, что когда у меня есть метод, возвращающий значение, и я забываю оператор return, ядро ​​программы дамп. Не печатается никакая ошибка, кроме "msleep error -1" от одного из моих драйверов устройства. Это на FreeBSD.

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

Я заметил, что когда у меня есть метод, возвращающий значение, и я забываю оператор return, ядро ​​программы дамп. Не печатается никакая ошибка, кроме "msleep error -1" от одного из моих драйверов устройства. Это на FreeBSD.…

Что здесь происходит?

НАПРИМЕР.:

bool MyClass::DummyFunc() {
  <do some stuff and forget the return value>     
}

В другом месте :

if(pMyObj->DummyFunc()) {
  print ("Hey, it's true!\n");
} else {
  print ("Darn, it's false!\n");
}

Этот код не должен давать сбой, независимо от возвращаемого значения.

-6
NXT 4 Сен 2016 в 03:08

2 ответа

Лучший ответ

Забывание возвращаемого значения приводит к тому, что поток управления достигает конца функции. Оба стандарта C и C ++ описывают этот случай. Обратите внимание, что main является исключением из этого случая и описывается отдельно.

Выход за пределы функции эквивалентен возврату без значения; это приводит к неопределенному поведению в функции возврата значения C ++ 11 Draft стр. 136

Мой опыт работы с clang показал, что компилятор будет обрабатывать экземпляры «неопределенного поведения», поскольку не произойдет и оптимизирует пути. Это законно, поскольку поведение не определено. Часто clang вместо этого выдает недопустимую инструкцию по пропущенному пути, так что код выйдет из строя, если произойдет «невозможное», что, вероятно, в вашем случае здесь. Фактически, компилятор может затем определить, что вызов DummyFunc () приводит к неопределенному поведению и, следовательно, не может произойти, и вместо этого начать оптимизацию вызывающих тел.

Gcc гораздо «дружелюбнее» и пытается создать что-нибудь приятное, например, вернуть 0.

Обратите внимание, что оба компилятора верны и выдают действительный код в соответствии со стандартом.

4
Brian 4 Сен 2016 в 02:14

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

Что здесь происходит?

Ваше ожидание неверно, вот что происходит.

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

Тогда вы нарушили это обещание.

Вот почему мы не будем делать предположений о деталях реализации, особенно при написании программ, поведение которых не определено. Вы не можете провести прямое сравнение между кодом C ++ и «некоторые данные поступают в этот регистр, и есть стек вызовов, который делает именно это».

Есть почти бесконечное количество способов, как это может сломаться, потому что в процессе преобразования вашего кода C ++ в хорошо оптимизированную программу, читаемую вашим компьютером, компилятор выполняет серию чрезвычайно сложных оптимизаций, и любую из них можно зафиксировать. половина из-за вашего нарушения правил стандарта. Попытка точно определить, что произошло при каждом конкретном запуске вашей программы, потребует следующих знаний:

  • Производитель, модель и версия процессора
  • марка, модель и версия операционной системы
  • производитель, модель и версия компилятора
  • все флаги компилятора
  • 10-летний опыт работы с исходным кодом компилятора
  • машина времени для проверки состояния каждого бита памяти на вашем компьютере во время выполнения вашей программы.

Это действительно того не стоит.

К счастью, в некоторых случаях мы можем дополнительно рационализировать этот случай, не изменяя абстракции: например, подумайте, что происходит, когда вы обещали вернуть std::string, но не сделали этого, а затем это несуществующее { {X1}} выходит за рамки. Деструктор вызывается при случайной ерунде, и это не пойдет хорошо.

4
Lightness Races in Orbit 4 Сен 2016 в 00:54