Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Указатель на локальную переменную

436
08 июля 2007 года
Fevzi
87 / / 02.04.2005
В своей книге Бьярн Страуструп пишет:
"Не возвращайте указатели или ссылки на локальные переменные!".
Но ведь иногда бывает так, что результат формируется внутри функции и приходится там заводить какую-то переменную result, а потом возвращать её, например:

char* GetString(int i)
{
char *result=new char[255];
switch(i)
{
case 1:result="My name is Vasya!";break;
case 2:result="dasddfasdf";break;
}
return result;
}
или что-то вроде такого,
но ведь этот код некоректен.
Как лучше оформлять подобный код?
361
08 июля 2007 года
Odissey_
661 / / 19.09.2006
Под локальными переменными в данном случае понимаются стековые переменные, время жизни которых оперделено пространством имени (namespace). Ты же создаешь здесь переменную в динамической памяти. Создание таких переменных и доступ к ним немного медленнее чем к стековым, но зато время жизни таких данных определяеться пользователем. В твоем примере локальной переменной можно назвать переменную i.
3
08 июля 2007 года
Green
4.8K / / 20.01.2000
Цитата: Fevzi

Как лучше оформлять подобный код?


Твой код сожержит ошибку, которая приводит к memory leak и потенциально к access violation.

Правильно будет так:

Код:
const char* GetString(int i)
{
    switch(i)
    {
    case 1:
        return "My name is Vasya!";

    case 2:
        return "dasddfasdf";
    }
}

или так:
Код:
std::string GetString(int i)
{
    switch(i)
    {
    case 1:
        return "My name is Vasya!";

    case 2:
        return "dasddfasdf";
    }
}
602
08 июля 2007 года
KPI Student
265 / / 16.12.2006
Цитата: Green
Твой код сожержит ошибку, которая приводит к memory leak и потенциально к access violation.


Маленикое уточнение:
... потому, что ты вызываешь operator= (char *, char *), т.е. присваиваешь один указатель другому, поэтому тот, который содержал адрес выделеной тобой памяти будет утерян.

 
Код:
result=new char[255];
case 1: result=...; // первоначаьное значение утеряно

Нужно либо копировать строки ( strcpy(), strncpy() )
Либо пользоваться классами, тогда за тебя это сделает STL
19K
08 июля 2007 года
Некромант
23 / / 05.12.2006
Вообще-то Вы изучаете С++, поэтому посоветую использовать функции именно из стандартной библиотеки С++, а не из С. По поводу приведенного в начале кода: в данном случае Вы создаете локальную переменную result, являющуюся указателем на что-то. Возвращаете тоже указатель, т.е. значение result копируется (копируется адрес, на который она указывает), поэтому в данном случае Вы не возвращаете указателей на локальные (стековые, которые кстати не быстрее в доступе, как это говорили) переменные.
260
09 июля 2007 года
Ramon
1.1K / / 16.08.2003
А строковый массив не катит?
320
09 июля 2007 года
m_Valery
1.0K / / 08.01.2007
А почему не использовать auto_ptr ?
361
09 июля 2007 года
Odissey_
661 / / 19.09.2006
Цитата:
Нужно либо копировать строки ( strcpy(), strncpy() )
Либо пользоваться классами, тогда за тебя это сделает STL


Угу. Так и есть.

Цитата:
стековые, которые кстати не быстрее в доступе, как это говорили


если можно поподробнее, прокоментируй

19K
09 июля 2007 года
Некромант
23 / / 05.12.2006
дизассемблируй любую процедуру в компиляторе, получишь нечто вроде такого (если не ошибаюсь):
pr proc
push ebp
mov ebp, esp
;и тут вот локальная переменная, например типа int со значением 10
push 10
;и вот ее использование:
mov eax, [ebp-4];примерно так, насчет 4 не уверен

Это к тому, что стек - обыкновенная область памяти, и доступ к нему идет также долго, как и при обращении к памяти (если не принимать во внимание существование кэш-памяти).
361
09 июля 2007 года
Odissey_
661 / / 19.09.2006
Ну хорошо. Вот пример.
 
Код:
int _tmain(int argc, _TCHAR* argv[])
{
    int * dinamic = new int[1000];
    dinamic[25] = 3;
    return 0;
}

Дизасемблируем
 
Код:
//int * dinamic = new int[1000];
004113AE  push        0FA0h
004113B3  call        operator new (411177h)
004113B8  add         esp,4
004113BB  mov         dword ptr [ebp-0D4h],eax
004113C1  mov         eax,dword ptr [ebp-0D4h]
004113C7  mov         dword ptr [dinamic],eax
//  dinamic[25] = 3;
[COLOR="Red"]004113CA  mov         eax,dword ptr [dinamic]
004113CD  mov         dword ptr [eax+64h],3 [/COLOR]

Далее
 
Код:
int _tmain(int argc, _TCHAR* argv[])
{
    int stek[1000];
    stek[25] = 3;
    return 0;
}

Дизассемблируем
 
Код:
//int stek[1000];
//stek[25] = 3;
[COLOR="#ff0000"]004113B2  mov         dword ptr [ebp-0F40h],3 [/COLOR]

Это Visual C++ 2005. Возможно в другом компиляторе будут отличия, но суть останется той же. Стековая память имеет фиксированный размер и указатель на стек, специально предназначенный для быстрого доступа к стековым данным. С динамической памятью все подругому.
309
09 июля 2007 года
el scorpio
1.1K / / 19.09.2006
Некромант
Особенно быстро производится работа со стековыми массивами, так как адрес начала массива в стеке постоянный. Особенно если индекс задан константой - в этом случае адрес элемента определяется как "ebp-X", где смещение вычисляется ещё на этапе компиляции.
Для массива в куче ВСЕГДА требуется сначала получить адрес начала массива из элемента стека, а потом прибавить к нему смещение.

Вот только символьные массивы часто приводят к ошибкам :(. То access violation, то переполнение буфера... Используйте лучше строковые объекты.
260
09 июля 2007 года
Ramon
1.1K / / 16.08.2003
Цитата:
Вот только символьные массивы часто приводят к ошибкам . То access violation, то переполнение буфера... Используйте лучше строковые объекты.



Из-за дури писавшего :-D

Еще юзайте stl, tr1, boost, не забывая шаблоны проектирования в программах уровня хеллоу ворлд - это же так интересно. :-D

PS: Особенно интересно использовать объекты с константными строками, обернутые в stl::map с ключем-индексом :-D

361
09 июля 2007 года
Odissey_
661 / / 19.09.2006
Цитата:
Для массива в куче ВСЕГДА требуется сначала получить адрес начала массива из элемента стека


Из элемента стека? Заполни мой пробел пожалуйста, мне казалось стёкавая память отличается свойством thread - safe, то есть при вызове функцией другой нитью (потоком) стековые данные пушаться в стек, и создается новый набор локальных данных. (по сути при вызове любой рекурсивной функции, просто как пример)
А если у нас указатель на динамическую область памяти объявлен вне функции, то указатель хранится где-то в статической памяти (вот здесь я не совсем понимаю где это?).
Просвети =)

436
09 июля 2007 года
Fevzi
87 / / 02.04.2005
Пусть тогда будет так:

const char* GetString(int i)
{
char* result=new char[100];
switch(i)
{
case 1:
strcpy(result,"какая-то строка");

case 2:
strcpy(result,"какая-то строка");
}
return result;
}
но ведь нужно освободить память, занятую result.В каком месте это сделать?
361
09 июля 2007 года
Odissey_
661 / / 19.09.2006
Как надо тебе написал Green. Аж 2 варианта.
Тот на котором ты остановился не самый то что надо =), ты это видишь уже потому что сразу наступаешь на еще одни грабли.
Касательно именно этого вопроса могу посоветовать передовать внешний указатель в функцию а память выделять и освобождать по месту использования. void GetString(int i, char* t).
Но!
Лучше используй один из вариантов Green`а.
260
09 июля 2007 года
Ramon
1.1K / / 16.08.2003
Код:
const char* g_aString[] =
            {
                "Suxx1",
                "Suxx2"
            };

const char* GetString(unsigned int i)
{
    const char* pRet = NULL;

    if (i < 2)
        pRet = g_aString;

    return pRet;
}


Код:
const char* GetString(unsigned int i)
{
    const char*     pRet = NULL;
    static const char*  aString[] =
                {
                    "Suxx1",
                    "Suxx2"
                };

    if (i < 2)
        pRet = aString;

    return pRet;
}
3
09 июля 2007 года
Green
4.8K / / 20.01.2000
Обращение, что к куче, что к стеку, занимает одинаковое время.
Но вспомните эпопею с EuroDiff, где выяснилось, что держать массивные данные на стеке накладно для производительности. Особенно яркр это проявилось на Celeron.

http://forum.codenet.ru/showthread.php?t=17339&page=13
260
09 июля 2007 года
Ramon
1.1K / / 16.08.2003
Он то как раз не в стеке, смотрим на "static", он в секции данных.

PS: Спасибо за единственный умный комментарий.
PS2: А если объявить его так "static const char* const aString[] = ...", то он еще будет в секции данных только для чтения.
309
10 июля 2007 года
el scorpio
1.1K / / 19.09.2006
[quote=Odissey] Для массива в куче ВСЕГДА требуется сначала получить адрес начала массива из элемента стека

Из элемента стека?
[/quote]
А откуда же ещё? :) Обращение к объектам (массивам), которые расположены в областях памяти, отличных от сегмента стека текущей функции, производится через адреса, хранимые в локальных указателях или ссылках. Или по цепочке (объект1->объект2->объект3), начало которой всё равно расположено в стеке функции. Иначе - утечка памяти

Исключение составляют только глобальные и статические переменные (константы), адреса которых известны на момент компиляции.

[quote=Green]
Обращение, что к куче, что к стеку, занимает одинаковое время.
[/quote]
Это в циклах, когда адрес начала массива заранее считан из стека в регистр CPU.
А при единичном доступе по заранее определённым индексам вызываются ДВЕ команды вместо одной. Правда, подобный доступ используется ОЧЕНЬ редко.

Цитата:
Но вспомните эпопею с EuroDiff, где выяснилось, что держать массивные данные на стеке накладно для производительности. Особенно яркр это проявилось на Celeron.


А у него кэш маленький - весь стековыми массивами заполнялся :D

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог