Здравствуйте, Аноним, Вы писали:
А>Как устроен механизм возврата значений?
А>1.
А>int f(){
А> int y = 5;
А> return 5;
А>}
А>a = f(); //вот тут вычислится значение f — выделится память на int и после присвоения — освободится
Да, формально f создаст временный объект, который и будет инициализирован возвращенным объектом из f.
А>Вроде так.
А>2.
А>char* f(){
А> return"qwe";
А>}
А>char *str = f(); // Что вот тут будет? Какая память выделяется и что с ней дальше происходит?
Память под строку "qwe" будет выделена до запуска проиложения, строка будет находиться в ней вечно до конца работы. Функция вернет указатель, точно так же как int. Память под сам этот указатель и память, на которую он указывает — это суть две совершенно разные штуки. То, на что он указывает будет, как я уже сказал, будет валидно и после вызова функции.
А>3.
А>char *str = f(); // Что вот тут будет? Какая память выделяется и что с ней дальше происходит?
Строка будет лежать во времменном массиве-переменной. После выхода из функции эта переменная умрёт, и возвращенный указатель будет невалидным, его использование — это неопределённое поведения.
PS. Пользуйся тегами выделения кода.
Правильно работающая программа — просто частный случай Undefined Behavior
Здравствуйте, Аноним, Вы писали:
А>Как устроен механизм возврата значений?
А>1.
int f(){
int y = 5;
return 5;
}
a = f(); //вот тут вычислится значение f - выделится память на int и после присвоения - освободится
большинство реализаций скомпилируют это так, что f вернёт значение в каком-нибудь предназначенном для этого месте проца, например в специальном регистра. На Intel это обычно eax. оттуда это значение и будет сохранено в переменную a.
А>2.
constchar* f(){
return"qwe";
}
constchar *str = f(); // Что вот тут будет? Какая память выделяется и что с ней дальше происходит?
Строчка "qwe" выделится в сегменте статических данных и будет там спокойно жить, когда функция затеет его возвращать, она вернёт просто сам указатель, то есть тоже как атомарное значение, то есть всё будет очень похоже на п 1, только значение будет передаваться другое. Оно же и сохранится в str.
Правда есть одно замечание. В C++ "qwe" имет тип const char [4], так что чтобы всё было по стандарту, нужно писать ещё и выделенное полужирным const
А>3.
char* f(){
char qw[] = "qwe";
return qw;
}
char *str = f(); // Что вот тут будет? Какая память выделяется и что с ней дальше происходит?
А вот тут будет плохо!
char qw[] = "qwe"; -- это объявление автоматического массива из 4-х char'ов, и инициализация его кодами букв q, w, e и 0.
После чего адрес этого массива (то есть адрес данных на стеке), будет возвращён из функции так же как был возвращён адрес во втором случае. Но, так как стековый фрейм f будет к этому моменту разрушен, то эти данные довольно скоро будет затёрты какими-нибудь другими пользователями стека, так что вы получити какой-то мусор.
Но есть ещё и
4
struct IntData {
int Size;
int Data[15];
};
IntData f() {
IntData result;
// тут как-то инициализируют поля переменной resultreturn result;
}
IntData data = f(); // Что вот тут будет? Какая память выделяется и что с ней дальше происходит?
Произойдёт интересно.
Так как структура IntData довольно большая, то она уже скорее всего не сможет быть передана через "волшебное место" в процессоре, так что она будет передаваться через память. Обычно это происходит так:
1) Тот кто вызывает, заводит у себя место под возвращаемое значние функции. Если компилятор не совсем того, то в данном контексте это будет непосредственно место под переменную data.
2) у функции f() на самом деле есть тайный параметр, куда передаётся указатель на это место
3) в операторе return будет вызван конструктор IntData, при этом объект будет сконструирован как раз в том самом месте. В этом варинте функции позовут конструктор копирования.
4) Вызывающая сторона сама решает как распорядиться полученным временным объектом. В жанном варианте (если компилятор не совсем того), то временного объекта вообще не будет и мы просто получим инициализированную переменную data и программа продолжит выполнение.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Аноним, Вы писали:
А>Как устроен механизм возврата значений?
Вот псевдокод для примера
int f(){
int y = 5;
return 5;
}
a = f();
...
//псевдокод для строки a = f():
{
//выделяем память под возвращаемое значениеint tmp;
//каким-то образом передаем в функцию адрес, по которому будет
//сконструирован результат
f(&tmp);
//присваивание результата
a = tmp;
}
//после присваивания память, занимаемая tmp, освобождается
//псевдокод для int f()void f(int* res)
{
//...
//вместо return 5;
*tmp = 5;//В общем случае создание объекта с использованием конструктораreturn;
}
А>2.
А>char* f(){
А> return"qwe";
А>}
А>char *str = f(); // Что вот тут будет? Какая память выделяется и что с ней дальше происходит?
В str будет адрес строкового литерала "qwe"; Механизм тот же, что и в случае с int, только временная переменная имеет тип char*.
А>char *str = f(); // Что вот тут будет? Какая память выделяется и что с ней дальше происходит?
в str будет мусор.
Любите книгу — источник знаний (с) М.Горький
Re[2]: Возвращаемое значение функций
От:
Аноним
Дата:
14.10.05 12:47
Оценка:
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, Аноним, Вы писали:
E>
E>struct IntData {
E> int Size;
E> int Data[15];
E>};
E>IntData f() {
E> IntData result;
E> // тут как-то инициализируют поля переменной result
E> return result;
E>}
E>IntData data = f(); // Что вот тут будет? Какая память выделяется и что с ней дальше происходит?
E>
E>Произойдёт интересно. E>Так как структура IntData довольно большая, то она уже скорее всего не сможет быть передана через "волшебное место" в процессоре, так что она будет передаваться через память. Обычно это происходит так: E>1) Тот кто вызывает, заводит у себя место под возвращаемое значние функции. Если компилятор не совсем того, то в данном контексте это будет непосредственно место под переменную data. E>2) у функции f() на самом деле есть тайный параметр, куда передаётся указатель на это место E>3) в операторе return будет вызван конструктор IntData, при этом объект будет сконструирован как раз в том самом месте. В этом варинте функции позовут конструктор копирования. E>4) Вызывающая сторона сама решает как распорядиться полученным временным объектом. В жанном варианте (если компилятор не совсем того), то временного объекта вообще не будет и мы просто получим инициализированную переменную data и программа продолжит выполнение.
А можно вот тут поподробней... Т.е. всё зависит от компилятора???...
Я обывно в таких функциях делаю что-то типа
void f(A* a){
//и уже вот тут меняю a
}
Просто не очень понятно почему под int память 100% выделяется, а под структуру непонятно...
Здравствуйте, Аноним, Вы писали:
А>Просто не очень понятно почему под int память 100% выделяется, а под структуру непонятно...
Про 100% никто не говорил. Тут все зависит от оптимизатора — а подробности поищи по словам RVO (Return Value Optimization), и "Оптимизация возвращаемого значения".
А> А можно вот тут поподробней... Т.е. всё зависит от компилятора???... А>Я обывно в таких функциях делаю что-то типа
А>
А>void f(A* a){
А>//и уже вот тут меняю a
А>}
А>
А>Просто не очень понятно почему под int память 100% выделяется, а под структуру непонятно...
От компилятора вообще много зависит
Но тут выбор простой. Либо в аржитектуре есть какой-то общепринятый и эффективный способ передавать из функции возвращаемое значение (это же понятие далеко за рамки C\C++) выходит. Либо тебе приходится передавать в функцию адрес буфера, куда лить результат.
Обычно в архитектуре есть возможность возвращать из функции что-то простое. Число, адрес, редко строку или счётчик. Короче какие-то атомарные или близкие к ним типы.
Ну и тогда компиляторы C++ польхуются халявой и возвращают из функций занчения через такой механизм, если значение не противоречит механизму.
Ну а если значение слишком большое, то возвращать приходится через буфер.
Отличие от кода с f(A*) есть и оно очень существенно. В случае, когда A -- возвращаемое значение, его ненадо конструировать до вызова функции.
Объект создаётся только на момент срабатывания оператора return. Согласись, что часто это таки важно.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, LuciferMoscow, Вы писали:
LM>Во втором и третьем случаях "будет выделена память" под указатель на char. После выхода из функции оно будет указывать в "никуда"
А разве "C-строки" не размещены в статической read-only памяти?