Безопасно передавать указатель на автоматическую переменную? - программирование

Безопасно передавать указатель на автоматическую переменную?

Предположим, что у меня есть функция, которая объявляет и инициализирует две локальные переменные, которые по умолчанию имеют продолжительность хранения auto. Затем эта функция вызывает вторую функцию, которой она передает адреса этих двух локальных переменных. Может ли эта вторая функция безопасно использовать эти указатели?

Тривиальный программный пример, чтобы дополнить это описание:

#include <stdio.h>

int adder(int *a, int *b)
{
    return *a + *b;
}

int main()
{
    auto int a = 5;    // `auto' is redundant; included for clarity
    auto int b = 3;

    // adder() gets the addresses of two auto variables! is this an issue?
    int result = adder(&a, &b);
    printf("5 + 3 = %d\n", result);

    return 0;
}

Эта программа работает, как ожидалось, печать 5 + 3 = 8.

Обычно, когда у меня возникают вопросы о C, я перехожу к стандарту, и это не было исключением. В частности, я проверил ISO/IEC 9899, §6.2.4. Там сказано, в частности:

4 Объект, идентификатор которого объявлен без связи и без спецификатор класса хранения static имеет автоматическую продолжительность хранения.

5 Для такого объекта, который не имеет тип массива переменной длины, его время жизни продолжается от входа в блок, с которым он связанный до тех пор, пока выполнение этого блока не закончится каким-либо образом. (Ввод закрытый блок или вызов функции приостанавливает, но не заканчивается, выполнение текущего блока.) Если блок введен рекурсивно, каждый раз создается новый экземпляр объекта. Начальное значение объекта неопределенно. Если инициализация указана для объект, он выполняется каждый раз, когда декларация достигается в выполнение блока; в противном случае значение становится неопределенным каждый раз, когда декларация будет достигнута.

Читая это, я рассуждаю о следующих моментах:

  • Переменные a и b имеют продолжительность хранения auto, которую я сделал явным, используя ключевое слово auto.

  • Вызов функции adder() соответствует скобке в разделе 5 в частичной цитате выше. То есть, ввод функции adder() "приостанавливает, но не заканчивается", выполнение текущего блока (который является main()).

  • Так как блок main() не является "end [ed] каким-либо образом," хранение для a и b гарантировано. Таким образом, доступ к ним с использованием адресов &a и &b, даже внутри adder(), должен быть безопасным.

Итак, мой вопрос: я прав в этом? Или я просто получаю "повезло" и получаю доступ к ячейкам памяти, которые, по случайности, не были перезаписаны?


P.S. Я не смог найти точный ответ на этот вопрос через поиск Google или SO. Если вы можете, отметьте это как дубликат, и я удалю его.

4b9b3361

Ответ 1

Да, это безопасно, и в основном ваши предположения верны. Время жизни автоматического объекта происходит от записи в блоке, где она была объявлена ​​до тех пор, пока блок не завершится.

(C99, 6.2.4p5) "Для такого объекта [...] его время жизни продолжается от входа в блок, с которым он связан, до тех пор, пока выполнение этого блока не закончится каким-либо образом.

Ответ 2

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

Если вам нужно быть осторожным, если функция сохраняет значения указателя в структуре, срок службы которой больше, чем ее собственный вызов. Рассмотрим две функции: foo() и bar():

int *g_ptr;

void bar (int *p) {
    g_ptr = p;
}

void foo () {
    int x = 10;
    bar(&x);
}

int main () {
    foo ();
    /* ...do something with g_ptr? */
    return 0;
}

В этом случае переменная x lifetime заканчивается на foo(). Однако указатель на x был сохранен в g_ptr на bar(). В этом случае для foo() было ошибкой передать указатель на свою локальную переменную x на bar().

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

Ответ 3

Эти переменные выделяются в стеке. Пока вы не вернетесь от функции, которая их объявила, они остаются действительными.

Ответ 4

Поскольку мне еще не разрешено комментировать, я бы предпочел написать другой ответ в качестве поправки к jxh answer above:

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

Несмотря на то, что это законно на C-языке, я считаю вредным передавать указатели на автоматические переменные во время вызова функции. Вы никогда не знаете (и часто не хотите знать), что именно вызываемая функция выполняет с переданными значениями. Когда вызываемая функция устанавливает псевдоним, у вас возникают большие проблемы.