Как работает временное хранилище в C, когда функция возвращается? - PullRequest
2 голосов
/ 27 июля 2010

Я довольно хорошо знаю C, однако я не понимаю, как работает временное хранилище.

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

Например:

void f() {
    int a = 5;
} // a's value doesn't exist anymore 

Однако мы можем использовать ключевое слово return для передачи некоторых данных во внешний мир:

int f() {
    int a = 5;
    return a;
} // a's value exists because it's transfered to the outside world

Пожалуйста, остановите меня, если что-то из этого не так.

Теперь вот странная вещь, когда вы делаете это с arrays, это не работает.

int []f() {
    int a[1] = {5};
    return a;
} // a's value doesn't exist. WHY?

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

Я знаю, что мог бы использовать динамическое распределение, чтобы сохранить данные для внешнего мира, но мой вопрос касается временного распределения.

Спасибо!

Ответы [ 6 ]

10 голосов
/ 27 июля 2010

Когда вы возвращаете что-то, его значение копируется. a существует ли не вне функции во втором примере; это значение делает. (Существует как значение.)

В последнем примере вы неявно преобразуете массив a в int*, и эта копия возвращается. a срок службы заканчивается, и вы указываете на мусор.

Ни одна переменная не живет вне своей области видимости.

1 голос
/ 27 июля 2010

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

0 голосов
/ 27 июля 2010
int []f() {
    int a[1] = {5};
    return a;
} // a's value doesn't exist. WHY?

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

typedef int ia[1];
ia h(void) {
    ia a = 5;
    return a;
}

Во-вторых, ты все равно не сможешь этого сделать. Вы также не можете сделать

int a[1] = {4};
int b[1];
b = a; // Here both a and b are interpreted as pointer literals or pointer arithmatic

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

Кроме того, вне объявлений и операторов sizeof (и, возможно, операций typeof, если у компилятора есть такое расширение) всякий раз, когда имя массива появляется в коде, оно рассматривается компилятором как литерал указателя или как часть арифметики указателя это приводит к указателю. Это означает, что оператор return будет выглядеть так, как будто вы возвращаете неправильный тип - указатель, а не массив.

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

0 голосов
/ 27 июля 2010

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

0 голосов
/ 27 июля 2010

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

0 голосов
/ 27 июля 2010

В реализациях C I (в основном для встроенных 8/16-битных микроконтроллеров) пространство выделяется для возвращаемого значения в стеке при вызове функции.

Перед вызовом функции предположим, что стек такой (строки могут представлять различную длину, но все они фиксированы):

[whatever]
...

Когда вызывается подпрограмма (например, sometype myFunc(arg1,arg2)), C выбрасывает в стек параметры функции (аргументы и пробел для возвращаемого значения, которые имеют размер фиксированной длины ), а затем по обратному адресу для продолжения выполнения кода и, возможно, для резервного копирования некоторых регистров процессора.

[myFunc local variables...]
[return address after myFunc is done]
[myFunc argument 1]
[myFunc argument 2]
[myFunc return value]
[whatever]
...

К тому времени, когда функция полностью завершится и вернется к коду, из которого она была вызвана, все ее переменные были освобождены из стека (они могут еще быть в теории, но есть нет гарантия)

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

Некоторые компиляторы хранят возвращаемые значения во временных регистрах процессора, а не используют стек, но это редко (это встречается только на некоторых AVR-компиляторах).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...