Возвращаемое значение функций
От: Аноним  
Дата: 14.10.05 11:34
Оценка:
Как устроен механизм возврата значений?

1.

int f(){
int y = 5;
return 5;
}

a = f(); //вот тут вычислится значение f — выделится память на int и после присвоения — освободится

Вроде так.


2.

char* f(){
return "qwe";
}

char *str = f(); // Что вот тут будет? Какая память выделяется и что с ней дальше происходит?

3.

char* f(){
cgar qw[] = "qwe";
return qw;
}

char *str = f(); // Что вот тут будет? Какая память выделяется и что с ней дальше происходит?
Re: Возвращаемое значение функций
От: LuciferMoscow Россия  
Дата: 14.10.05 11:42
Оценка: -1
Здравствуйте, Аноним, Вы писали:

<scipped>
Во втором и третьем случаях "будет выделена память" под указатель на char. После выхода из функции оно будет указывать в "никуда"
Re: Возвращаемое значение функций
От: _Winnie Россия C++.freerun
Дата: 14.10.05 11:50
Оценка: 3 (1)
Здравствуйте, Аноним, Вы писали:

А>Как устроен механизм возврата значений?


А>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* f(){
А> cgar qw[] = "qwe";
А> return qw;
А>}



А>char *str = f(); // Что вот тут будет? Какая память выделяется и что с ней дальше происходит?


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

PS. Пользуйся тегами выделения кода.
Правильно работающая программа — просто частный случай Undefined Behavior
Re[2]: Возвращаемое значение функций
От: LuciferMoscow Россия  
Дата: 14.10.05 11:58
Оценка:
Здравствуйте, _Winnie, Вы писали:

_W>Здравствуйте, Аноним, Вы писали:

Я в принципе пытался сказть тоже. Настолько непонятно сказал?
Re: Возвращаемое значение функций
От: Erop Россия  
Дата: 14.10.05 11:59
Оценка: 2 (2)
Здравствуйте, Аноним, Вы писали:

А>Как устроен механизм возврата значений?


А>1.


int f(){
  int y = 5;
  return 5;
}

a  = f(); //вот тут вычислится значение f - выделится память на int и после присвоения - освободится


большинство реализаций скомпилируют это так, что f вернёт значение в каком-нибудь предназначенном для этого месте проца, например в специальном регистра. На Intel это обычно eax. оттуда это значение и будет сохранено в переменную a.


А>2.



const char* f(){
 return "qwe";
}

const char *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;
   //  тут как-то инициализируют поля переменной result
   return result;
}

IntData data = f(); // Что вот тут будет? Какая память выделяется и что с ней дальше происходит?


Произойдёт интересно.
Так как структура IntData довольно большая, то она уже скорее всего не сможет быть передана через "волшебное место" в процессоре, так что она будет передаваться через память. Обычно это происходит так:
1) Тот кто вызывает, заводит у себя место под возвращаемое значние функции. Если компилятор не совсем того, то в данном контексте это будет непосредственно место под переменную data.
2) у функции f() на самом деле есть тайный параметр, куда передаётся указатель на это место
3) в операторе return будет вызван конструктор IntData, при этом объект будет сконструирован как раз в том самом месте. В этом варинте функции позовут конструктор копирования.
4) Вызывающая сторона сама решает как распорядиться полученным временным объектом. В жанном варианте (если компилятор не совсем того), то временного объекта вообще не будет и мы просто получим инициализированную переменную data и программа продолжит выполнение.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Возвращаемое значение функций
От: Bell Россия  
Дата: 14.10.05 12:06
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Как устроен механизм возврата значений?


Вот псевдокод для примера

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*.


А>3.

А>char* f(){
А> cgar qw[] = "qwe";//Локальная копия строкового литерала
А> return qw;//возвращение адреса локальной переменной - большой бумц
А>}



А>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% выделяется, а под структуру непонятно...
Re[3]: Возвращаемое значение функций
От: SWW Россия  
Дата: 14.10.05 12:50
Оценка:
_W>>Здравствуйте, Аноним, Вы писали:
LM>Я в принципе пытался сказть тоже. Настолько непонятно сказал?

Ошибка будет только в третьем случае, а ты написал: во втором и третьем.
Re[3]: Возвращаемое значение функций
От: Bell Россия  
Дата: 14.10.05 13:06
Оценка: 1 (1)
Здравствуйте, Аноним, Вы писали:

А>Просто не очень понятно почему под int память 100% выделяется, а под структуру непонятно...

Про 100% никто не говорил. Тут все зависит от оптимизатора — а подробности поищи по словам RVO (Return Value Optimization), и "Оптимизация возвращаемого значения".
Любите книгу — источник знаний (с) М.Горький
Re[3]: Возвращаемое значение функций
От: Erop Россия  
Дата: 14.10.05 13:40
Оценка: 2 (2)
Здравствуйте, Аноним, Вы писали:


А> А можно вот тут поподробней... Т.е. всё зависит от компилятора???...

А>Я обывно в таких функциях делаю что-то типа

А>
А>void f(A* a){
А>//и  уже вот тут меняю a
А>}
А>


А>Просто не очень понятно почему под int память 100% выделяется, а под структуру непонятно...


От компилятора вообще много зависит
Но тут выбор простой. Либо в аржитектуре есть какой-то общепринятый и эффективный способ передавать из функции возвращаемое значение (это же понятие далеко за рамки C\C++) выходит. Либо тебе приходится передавать в функцию адрес буфера, куда лить результат.
Обычно в архитектуре есть возможность возвращать из функции что-то простое. Число, адрес, редко строку или счётчик. Короче какие-то атомарные или близкие к ним типы.
Ну и тогда компиляторы C++ польхуются халявой и возвращают из функций занчения через такой механизм, если значение не противоречит механизму.
Ну а если значение слишком большое, то возвращать приходится через буфер.

Отличие от кода с f(A*) есть и оно очень существенно. В случае, когда A -- возвращаемое значение, его ненадо конструировать до вызова функции.
Объект создаётся только на момент срабатывания оператора return. Согласись, что часто это таки важно.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Возвращаемое значение функций
От: Dmi_3 Россия  
Дата: 14.10.05 15:21
Оценка:
Здравствуйте, LuciferMoscow, Вы писали:

LM>Во втором и третьем случаях "будет выделена память" под указатель на char. После выхода из функции оно будет указывать в "никуда"



А разве "C-строки" не размещены в статической read-only памяти?

char* f(){
  return "qwe";
}

равносильно с точностью до константности
static char text[]="qwe";
char* f(){
  return text;
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.