return char*
От: Jumangee Россия  
Дата: 11.03.17 11:35
Оценка:
Добрый день!
Вопрос:
char * CFooDlg::Sum()
{
char szTmp[200];
strcpy(szTmp, m_szVal1);
strcat(szTmp, m_szVal2);

return значение_szTmp;
}

как вернуть значение локальной переменной szTmp?

//////////////////
Вопрос решился иным способом.
Возвращаем из функции количество символов в релультирующей строке (как признак для дальнейшего поведения), а результирующую строку выделили отдельным членом класса.
Спасибо за комментарии.
Отредактировано 13.03.2017 15:00 Jumangee . Предыдущая версия .
Re: return char*
От: night beast СССР  
Дата: 11.03.17 11:49
Оценка:
Здравствуйте, Jumangee, Вы писали:

J>как вернуть значение локальной переменной szTmp?


std::array<char, 200>
Re: return char*
От: LaptevVV Россия  
Дата: 11.03.17 11:49
Оценка: -1
Сделай не локальный массив, а динамический.
Этот указатель и вернешь.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re: return char*
От: Pzz Россия https://github.com/alexpevzner
Дата: 11.03.17 11:58
Оценка: +1
Здравствуйте, Jumangee, Вы писали:

J>Добрый день!

J>Вопрос:
J>char * CFooDlg::Sum()
J>{
J> char szTmp[200];
J> strcpy(szTmp, m_szVal1);
J> strcat(szTmp, m_szVal2);

J> return значение_szTmp;

J>}

J>как вернуть значение локальной переменной szTmp?


1) Можно вернуть std::string, раз уж программа написана на C++
2) Можно объявить szTmp, как static. Проблема в том, что следующий вызов функции подменит предыдущее содержимое этого буфера, и если кто-то где-то все еще использует старое значение, этот кто-то сильно удивится.
3) Можно при каждом вызове выделять новый буфер с помощью оператора new или функции malloc. Тот, кто вызывает функцию, возвращающую данные таким образом, должен не забывать их освобождать
4) Буфер может предоставлять вызывающая сторона.

P.S. Как бы то ни было, при использованнии strcpy, strcat и т.п. надо быть уверенным, что результат поместится в выходной буфер (или проверять это заранее, или в самой функции, или использовать более безопасные strncpy/strncar)
Re[2]: return char*
От: Jumangee Россия  
Дата: 11.03.17 12:24
Оценка: +1 :))
Вариант 1 — все работает! Благодарю!
Получить значение нужно всего 1 раз, т.к. следующего раза, возможно, уже не будет, а держать лишнюю переменную под промежуточное значение нет желания.
Re: return char*
От: Carc Россия https://vk.com/gosha_mazov
Дата: 11.03.17 12:32
Оценка: -3
Здравствуйте, Jumangee, Вы писали:

J>Добрый день!

J>Вопрос:
J>char * CFooDlg::Sum()
J>{
J> char szTmp[200];
J> strcpy(szTmp, m_szVal1);
J> strcat(szTmp, m_szVal2);

J> return значение_szTmp;

J>}

J>как вернуть значение локальной переменной szTmp?

return &szTmp[0];

Ибо, раз два плюса — то по умолчанию соглашение вызовов cdecl, то бишь освобождает стек вызывающая функция.
Соответственно, где-то в вызывающей функции строка вида будет валидна
const char* const pszResult=…Sum();

Т.к. после const char* const pszResult=… сам массив szTmp в вызываемой функции все еще будет существовать. В том смысле, что вызываемая уже отработала конечно, но вот добро ее на стеке все еще в полном порядочке лежит.

1) Эффективно!?! О да-а-а!

2) Опасно? Нисколько. Ну разве что иногда выстреливая в ногу может случайно снести и голову такой вызов.

3) Поддерживаемо? О-да! Еще как причем — один раз заложитесь на такое документированное, но неочевидное поведение и будете просто обязаны постоянно держать в голове это, и поддерживать завсегда. Иначе таки снесет и всю голову приложению.

4) Переносимо! Да запросто. Но есть нюанззз (ц). Не дай бог какая "пьянь" с какого-нить перепугу сменит тоже соглашение вызовов. И тут начнется.

5) Отлаживаемо! Легко и непринужденно. Только если хоть что-нибудь из вышеперечисленного случится, то полный привет. Докопаетесь вы до причины такой баги году так эдак 2многонулей_какая_цифра. Т.е не_приведи_оспади

Я где-то увидел такое решение, и чуть не припух. Ибо проект был солидный, и парни там грамотные. Очень долго въезжал, что к чему. Потом стало ясно.
В общем, такие решения использовать можно. Но нужно четко понимать себе их последствия и ограничения их использования.

В противном случае, все стандартные правила вроде "кто буфер выделял, тот и освобождает". Или возвращать какой-то прокси-объект вроде std::array ну или свой "лесапет" прикрутить. Тут уж кто во что горазд.
Aml Pages Home
Re[2]: return char*
От: T4r4sB Россия  
Дата: 11.03.17 12:47
Оценка: 1 (1)
Здравствуйте, Carc, Вы писали:

C>Т.к. после const char* const pszResult=… сам массив szTmp в вызываемой функции все еще будет существовать. В том смысле, что вызываемая уже отработала конечно, но вот добро ее на стеке все еще в полном порядочке лежит.


Добавь static и добро будет лежать где надо всю программу. Если оно считается лишь один раз за время жизни программы, то катастрофы не случится.
Re[3]: return char*
От: Carc Россия https://vk.com/gosha_mazov
Дата: 11.03.17 13:11
Оценка:
Здравствуйте, T4r4sB, Вы писали:


TB>Добавь static и добро будет лежать где надо всю программу. Если оно считается лишь один раз за время жизни программы, то катастрофы не случится.

Смотря куда-добавить… + нужно понимать, что можно словить очень неочевидные проблемы уже в release, в определенных случаях. Ну, например, когда какой-нить кодо-липосакцией занимаемся и отцепляем си-рантайм.

Опять же порядок инициализации static-member недокументирован.

Хотя здесь, в конкретном этом примере, можно конечно и сделать в вызываемой функции что-нить вида static char szTemp[20]={0}.

В общем, готового рецепта на все случаи все равно нет. В каждом есть свои нюансы.
А так, static дело архиполезное, и архинужное. Сам пользуюсь частенько.
Aml Pages Home
Re[2]: return char*
От: Pzz Россия https://github.com/alexpevzner
Дата: 11.03.17 14:30
Оценка: +1
Здравствуйте, Carc, Вы писали:

C>Ибо, раз два плюса — то по умолчанию соглашение вызовов cdecl, то бишь освобождает стек вызывающая функция.

C>Соответственно, где-то в вызывающей функции строка вида будет валидна

Вызывающая функция освобождает стек только от того, что она сама туда напихала. Но не от того, что туда напихала вызываемая функция.
Re[3]: return char*
От: Carc Россия https://vk.com/gosha_mazov
Дата: 11.03.17 14:37
Оценка: -1
Здравствуйте, Pzz, Вы писали:

Pzz>Вызывающая функция освобождает стек только от того, что она сама туда напихала. Но не от того, что туда напихала вызываемая функция.

Согласен, но в том и "фишка" что указатель на локальную переменную после выхода из вызываемой должен остаться валидным в вызывающей функции... Ну, понятно, это все равно "хак".
Aml Pages Home
Re[4]: return char*
От: std.denis Россия  
Дата: 11.03.17 14:44
Оценка: 1 (1)
C>Согласен, но в том и "фишка" что указатель на локальную переменную после выхода из вызываемой должен остаться валидным в вызывающей функции... Ну, понятно, это все равно "хак".

А чего бы ему оказаться не валидным, если это указатель на стек. Вот данные по этому указателю могут угробиться при первой же вызванной функции. Это не хак, это русская рулетка.
Re[5]: return char*
От: Carc Россия https://vk.com/gosha_mazov
Дата: 11.03.17 15:06
Оценка:
Здравствуйте, std.denis, Вы писали:


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

Я про данные и говорил. Но то что "рулетка", я полностью согласен.
Aml Pages Home
Re[4]: return char*
От: Pzz Россия https://github.com/alexpevzner
Дата: 11.03.17 16:15
Оценка: -1
Здравствуйте, Carc, Вы писали:

Pzz>>Вызывающая функция освобождает стек только от того, что она сама туда напихала. Но не от того, что туда напихала вызываемая функция.

C>Согласен, но в том и "фишка" что указатель на локальную переменную после выхода из вызываемой должен остаться валидным в вызывающей функции... Ну, понятно, это все равно "хак".

Никому он ничего не должен.
Re[5]: return char*
От: Carc Россия https://vk.com/gosha_mazov
Дата: 11.03.17 18:30
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Здравствуйте, Carc, Вы писали:


Pzz>>>Вызывающая функция освобождает стек только от того, что она сама туда напихала. Но не от того, что туда напихала вызываемая функция.

C>>Согласен, но в том и "фишка" что указатель на локальную переменную после выхода из вызываемой дол
жен остаться валидным в вызывающей функции... Ну, понятно, это все равно "хак".

Pzz>Никому он ничего не должен.

Ну-у-у, началось словоблудие знакомое… Кто спорил то!?! Попытки вернуть адрес локальной переменной из функции и так понятно к чему приводит. Я о другом вообще говорил! Что в данном конкретном примере, при таком соглашении вызовов cdecl, по вернутому таким некошерным способом указателе будут запросто тем не менее лежать валидные данные.

И только всего! Это все, что я хотел сказать. Что с ним делать, с таким "некошерно" вёрнутым указателем, решать вызывающей функции. Ежу же понятно, что нужно сразу данные по нему куда-то "скопистить".

Такой же вариант бы сработал

char* const p=Sum();//вот тут мы получили этот абсолютно неверный указатель

//но его сразу нужно забрать, а не потом когда-нибудь
if (NULL(nullptr) == p)
    throw "Идите в *опу"; //т.е. если указатель не валидный, дальше вообще никаких переговоров

//или сразу его забираем себе
const std::string wowPtr(p);//все - забрали указатель


Если разговор в стиле топик-стартера, то ответ простой. Никак. Так нельзя. Нельзя использовать адрес локальной переменной после выхода из функции.

Если разговор как надо? Опять же ответ простой. Поиск по КЫВТ. 100 раз обсуждали. Подходов тыщи. А по сути их всего два. Остальных нет. Я не спроста написал, в первом же посте, что тот пример, который я показал, работать-то будет. Но так никогда не стоит делать.
Aml Pages Home
Отредактировано 11.03.2017 18:32 Carc . Предыдущая версия .
Re[6]: return char*
От: andrey.desman  
Дата: 11.03.17 18:42
Оценка:
Здравствуйте, Carc, Вы писали:

C>
C>//или сразу его забираем себе
C>const std::string wowPtr(p);//все - забрали указатель
C>


Это не "все забрали", а "все, приплыли". Указатель-то забрали, а сама переменная wowPtr вполне вероятно на месте данных и окажется, и перетрет строку своей инициализацией.
Re[2]: return char*
От: Somescout  
Дата: 11.03.17 18:47
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>2) Можно объявить szTmp, как static. Проблема в том, что следующий вызов функции подменит предыдущее содержимое этого буфера, и если кто-то где-то все еще использует старое значение, этот кто-то сильно удивится.


По-моему такое не стоит советовать даже в шутку. Имхо, но это эталонный выстрел в ногу.
ARI ARI ARI... Arrivederci!
Re[6]: return char*
От: Pzz Россия https://github.com/alexpevzner
Дата: 11.03.17 19:26
Оценка: 1 (1) +3
Здравствуйте, Carc, Вы писали:

Pzz>>Никому он ничего не должен.

C>Ну-у-у, началось словоблудие знакомое… Кто спорил то!?! Попытки вернуть адрес локальной переменной из функции и так понятно к чему приводит. Я о другом вообще говорил! Что в данном конкретном примере, при таком соглашении вызовов cdecl, по вернутому таким некошерным способом указателе будут запросто тем не менее лежать валидные данные.

Сразу видно, что ты никогда не программировал в среде, которая использует твой текущий стек для обработки прерываний.

Это совершенно не нарушает сишную calling convention, но данные, которые "над стеком" могут быть испорчены в любой момент.

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

Не умничай, короче, целее будешь
Re[3]: return char*
От: Pzz Россия https://github.com/alexpevzner
Дата: 11.03.17 19:28
Оценка: +1
Здравствуйте, Somescout, Вы писали:

Pzz>>2) Можно объявить szTmp, как static. Проблема в том, что следующий вызов функции подменит предыдущее содержимое этого буфера, и если кто-то где-то все еще использует старое значение, этот кто-то сильно удивится.


S>По-моему такое не стоит советовать даже в шутку. Имхо, но это эталонный выстрел в ногу.


Совершенно не обязательно. Такое решение плохо, так сказать, масштабируется, но масштабирование не всегда бывает нужно. Так можно делать, если хорошо понимать, какие это накладывает ограничения, и как на эти ограничения случайно не наступить.
Re[7]: return char*
От: Carc Россия https://vk.com/gosha_mazov
Дата: 11.03.17 19:42
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Здравствуйте, Carc, Вы писали:


Pzz>Сразу видно, что ты никогда не программировал в среде, которая использует твой текущий стек для обработки прерываний.


Pzz>Это совершенно не нарушает сишную calling convention, но данные, которые "над стеком" могут быть испорчены в любой момент.


Pzz>Кстати, тоже самое может произойти и в привычной тебе среде, если компилятор вставляет служебные вызовы фукнций для проверки границ массивов, для профилировки и т.п.


Pzz>Не умничай, короче, целее будешь

Ты о себе?

Именно выше перечисленные варианты событий я и имел ввиду (служебные вызовы).
Цитата из моего первого поста
Автор: Carc
Дата: 11.03.17

Я где-то увидел такое решение, и чуть не припух. Ибо проект был солидный, и парни там грамотные. Очень долго въезжал, что к чему. Потом стало ясно.


Я про тот случай и говорил. Что очень удивился вообще такому решению. И что оно вообще может работать. Это только информация к размышлению топик-стартеру. И там же первом посте написал к чему приводит использование таких "оптимизаций"
Aml Pages Home
Re[4]: return char*
От: Somescout  
Дата: 11.03.17 20:15
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Совершенно не обязательно. Такое решение плохо, так сказать, масштабируется, но масштабирование не всегда бывает нужно. Так можно делать, если хорошо понимать, какие это накладывает ограничения, и как на эти ограничения случайно не наступить.


Именно для целей возврата изменяемого значения? Если речь о единожды вычисляемой константе — вопросов нет, а вот возвращать результат, который может быть изменён в процессе работы программы — закладывать мину в код.

ЗЫ. Хотя наверно для устройств с ограниченными ресурсами это нормальный вариант.
ARI ARI ARI... Arrivederci!
Отредактировано 11.03.2017 22:59 Somescout . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.