#c #reference
#c #ссылка
Вопрос:
Я пытаюсь понять, что происходит внутри с этим (странным?) поведением g .
#include <iostream>
using namespace std;
intamp; f(void) {
int a = 9;
intamp; b = a;
return a;
}
int main(void) {
intamp; l = f();
cout << l << 'n' << l << 'n';
}
При возврате a
самого себя и привязке к нему l
я получаю предупреждение (ссылку на локальную переменную) и seg.fault, если я обращаюсь к нему из l, но при возврате b
самого себя я не только не получаю seg.fault, но и могу получить к нему доступ один раз из l (UB, я предполагаю) передзначение l изменяется случайным образом. Но что именно здесь происходит?
Разве два возврата не идентичны? Автоматически ли g помечает a's
область как непригодную для использования после возврата, следовательно, seg.fault, в то время как по какой-то причине позволяет b жить дольше?
Комментарии:
1. Это UB. Либо проверьте точную сгенерированную сборку, либо не пытайтесь угадать. Общего объяснения нет.
Ответ №1:
Ваш главный вопрос заключается в том, почему gcc не выдает предупреждение ни в одной из альтернатив. Обе альтернативы являются неопределенным поведением, и единственное отличие состоит в том, что в одном случае компилятор может обнаружить его и предупредить вас об этом.
Стандарт C не требует диагностики неопределенного поведения. Любая диагностика на этот счет от вашего компилятора — это просто дополнительный бонус; и хотя современные компиляторы C очень умны, они не всегда могут понять, что скомпилированный код приведет к тому, что демоны вылетят из вашего носа.
PS gcc 10.2 выдает предупреждение с -O3
опцией для return b;
альтернативы. С -Wall
помощью only gcc также выдает 2-е предупреждение за неопределенное поведение, вы можете узнать, что это такое самостоятельно.
Комментарии:
1. Спасибо. Я также обнаружил
clang
предупреждения, что интересно!
Ответ №2:
Нет, компиляторы C не помечают области как непригодные для использования.
Segfaults — это всего лишь один из многих способов проявления неопределенного поведения. На самом деле это один из более удобных способов, поскольку он заставляет вас заметить это раньше.
Вы отвечаете за время жизни. Если вы ошибаетесь, результатом будет неопределенное поведение. Не исключение. Не ошибка сегмента. Буквально что угодно.
Одним из возможных симптомов является «похоже, что это работает». Другой — segfault . Другие включают буквальное перемещение во времени (где UB позже в программе заставляет более простой код вести себя по-другому), жесткий диск вашего компьютера становится непригодным для использования, кто-то получает информацию о вашей кредитной карте, история вашего браузера отправляется по электронной почте в ваш список контактов и т.д.
Некоторые компиляторы в режиме отладки помечают освобожденную память битовым шаблоном, чтобы облегчить отладку.
Комментарии:
1. Однако два оператора return идентичны, верно? Итак, я предполагаю, что возврат ссылки сам по себе не выдает предупреждение / seg.fault, потому что он также может указывать на что-то с жизненным циклом, превышающим функцию?
2. @pol Один легко диагностируется компилятором, другой немного сложнее. Но они оба являются UB (как только вы используете возвращаемую ссылку). Предупреждения компилятора — это просто предупреждения, они не гарантированы и не охватывают все проблемы.
Ответ №3:
Но что именно здесь происходит?
Неопределенное поведение.
Разве два возврата не идентичны?
Исходный код явно синтаксически отличается. Программы не имеют одинакового семантического значения, потому что ни одна из программ не имеет никакого семантического значения, поскольку значение обеих программ не определено. Таким образом, поведение программ не гарантируется одинаковым.
Автоматически ли g помечает область a как непригодную для использования после возврата
Возможно. Я бы не стал предполагать, что это так, основываясь на этом одном наблюдении, но это может быть правдой. Смотрите исходный код GCC для подтверждения.