//////////////////
Вопрос решился иным способом.
Возвращаем из функции количество символов в релультирующей строке (как признак для дальнейшего поведения), а результирующую строку выделили отдельным членом класса.
Спасибо за комментарии.
Здравствуйте, 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)
Вариант 1 — все работает! Благодарю!
Получить значение нужно всего 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?
return &szTmp[0];
Ибо, раз два плюса — то по умолчанию соглашение вызовов cdecl, то бишь освобождает стек вызывающая функция.
Соответственно, где-то в вызывающей функции строка вида будет валидна
const char* const pszResult=…Sum();
Т.к. после const char* const pszResult=… сам массив szTmp в вызываемой функции все еще будет существовать. В том смысле, что вызываемая уже отработала конечно, но вот добро ее на стеке все еще в полном порядочке лежит.
1) Эффективно!?! О да-а-а!
2) Опасно? Нисколько. Ну разве что иногда выстреливая в ногу может случайно снести и голову такой вызов.
3) Поддерживаемо? О-да! Еще как причем — один раз заложитесь на такое документированное, но неочевидное поведение и будете просто обязаны постоянно держать в голове это, и поддерживать завсегда. Иначе таки снесет и всю голову приложению.
4) Переносимо! Да запросто. Но есть нюанззз (ц). Не дай бог какая "пьянь" с какого-нить перепугу сменит тоже соглашение вызовов. И тут начнется.
5) Отлаживаемо! Легко и непринужденно. Только если хоть что-нибудь из вышеперечисленного случится, то полный привет. Докопаетесь вы до причины такой баги году так эдак 2многонулей_какая_цифра. Т.е не_приведи_оспади
Я где-то увидел такое решение, и чуть не припух. Ибо проект был солидный, и парни там грамотные. Очень долго въезжал, что к чему. Потом стало ясно.
В общем, такие решения использовать можно. Но нужно четко понимать себе их последствия и ограничения их использования.
В противном случае, все стандартные правила вроде "кто буфер выделял, тот и освобождает". Или возвращать какой-то прокси-объект вроде std::array ну или свой "лесапет" прикрутить. Тут уж кто во что горазд.
Здравствуйте, Carc, Вы писали:
C>Т.к. после const char* const pszResult=… сам массив szTmp в вызываемой функции все еще будет существовать. В том смысле, что вызываемая уже отработала конечно, но вот добро ее на стеке все еще в полном порядочке лежит.
Добавь static и добро будет лежать где надо всю программу. Если оно считается лишь один раз за время жизни программы, то катастрофы не случится.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
TB>Добавь static и добро будет лежать где надо всю программу. Если оно считается лишь один раз за время жизни программы, то катастрофы не случится.
Смотря куда-добавить… + нужно понимать, что можно словить очень неочевидные проблемы уже в release, в определенных случаях. Ну, например, когда какой-нить кодо-липосакцией занимаемся и отцепляем си-рантайм.
Опять же порядок инициализации static-member недокументирован.
Хотя здесь, в конкретном этом примере, можно конечно и сделать в вызываемой функции что-нить вида static char szTemp[20]={0}.
В общем, готового рецепта на все случаи все равно нет. В каждом есть свои нюансы.
А так, static дело архиполезное, и архинужное. Сам пользуюсь частенько.
Здравствуйте, Carc, Вы писали:
C>Ибо, раз два плюса — то по умолчанию соглашение вызовов cdecl, то бишь освобождает стек вызывающая функция. C>Соответственно, где-то в вызывающей функции строка вида будет валидна
Вызывающая функция освобождает стек только от того, что она сама туда напихала. Но не от того, что туда напихала вызываемая функция.
Здравствуйте, Pzz, Вы писали:
Pzz>Вызывающая функция освобождает стек только от того, что она сама туда напихала. Но не от того, что туда напихала вызываемая функция.
Согласен, но в том и "фишка" что указатель на локальную переменную после выхода из вызываемой должен остаться валидным в вызывающей функции... Ну, понятно, это все равно "хак".
C>Согласен, но в том и "фишка" что указатель на локальную переменную после выхода из вызываемой должен остаться валидным в вызывающей функции... Ну, понятно, это все равно "хак".
А чего бы ему оказаться не валидным, если это указатель на стек. Вот данные по этому указателю могут угробиться при первой же вызванной функции. Это не хак, это русская рулетка.
SD>А чего бы ему оказаться не валидным, если это указатель на стек. Вот данные по этому указателю могут угробиться при первой же вызванной функции. Это не хак, это русская рулетка.
Я про данные и говорил. Но то что "рулетка", я полностью согласен.
Здравствуйте, Carc, Вы писали:
Pzz>>Вызывающая функция освобождает стек только от того, что она сама туда напихала. Но не от того, что туда напихала вызываемая функция. C>Согласен, но в том и "фишка" что указатель на локальную переменную после выхода из вызываемой должен остаться валидным в вызывающей функции... Ну, понятно, это все равно "хак".
Здравствуйте, Pzz, Вы писали:
Pzz>Здравствуйте, Carc, Вы писали:
Pzz>>>Вызывающая функция освобождает стек только от того, что она сама туда напихала. Но не от того, что туда напихала вызываемая функция. C>>Согласен, но в том и "фишка" что указатель на локальную переменную после выхода из вызываемой дол
жен остаться валидным в вызывающей функции... Ну, понятно, это все равно "хак".
Pzz>Никому он ничего не должен.
Ну-у-у, началось словоблудие знакомое… Кто спорил то!?! Попытки вернуть адрес локальной переменной из функции и так понятно к чему приводит. Я о другом вообще говорил! Что в данном конкретном примере, при таком соглашении вызовов cdecl, по вернутому таким некошерным способом указателе будут запросто тем не менее лежать валидные данные.
И только всего! Это все, что я хотел сказать. Что с ним делать, с таким "некошерно" вёрнутым указателем, решать вызывающей функции. Ежу же понятно, что нужно сразу данные по нему куда-то "скопистить".
Такой же вариант бы сработал
char* const p=Sum();//вот тут мы получили этот абсолютно неверный указатель
//но его сразу нужно забрать, а не потом когда-нибудьif (NULL(nullptr) == p)
throw"Идите в *опу"; //т.е. если указатель не валидный, дальше вообще никаких переговоров
//или сразу его забираем себеconst std::string wowPtr(p);//все - забрали указатель
Если разговор в стиле топик-стартера, то ответ простой. Никак. Так нельзя. Нельзя использовать адрес локальной переменной после выхода из функции.
Если разговор как надо? Опять же ответ простой. Поиск по КЫВТ. 100 раз обсуждали. Подходов тыщи. А по сути их всего два. Остальных нет. Я не спроста написал, в первом же посте, что тот пример, который я показал, работать-то будет. Но так никогдане стоит делать.
C>//или сразу его забираем себе
C>const std::string wowPtr(p);//все - забрали указатель
C>
Это не "все забрали", а "все, приплыли". Указатель-то забрали, а сама переменная wowPtr вполне вероятно на месте данных и окажется, и перетрет строку своей инициализацией.
Здравствуйте, Pzz, Вы писали:
Pzz>2) Можно объявить szTmp, как static. Проблема в том, что следующий вызов функции подменит предыдущее содержимое этого буфера, и если кто-то где-то все еще использует старое значение, этот кто-то сильно удивится.
По-моему такое не стоит советовать даже в шутку. Имхо, но это эталонный выстрел в ногу.
Здравствуйте, Carc, Вы писали:
Pzz>>Никому он ничего не должен. C>Ну-у-у, началось словоблудие знакомое… Кто спорил то!?! Попытки вернуть адрес локальной переменной из функции и так понятно к чему приводит. Я о другом вообще говорил! Что в данном конкретном примере, при таком соглашении вызовов cdecl, по вернутому таким некошерным способом указателе будут запросто тем не менее лежать валидные данные.
Сразу видно, что ты никогда не программировал в среде, которая использует твой текущий стек для обработки прерываний.
Это совершенно не нарушает сишную calling convention, но данные, которые "над стеком" могут быть испорчены в любой момент.
Кстати, тоже самое может произойти и в привычной тебе среде, если компилятор вставляет служебные вызовы фукнций для проверки границ массивов, для профилировки и т.п.
Здравствуйте, Somescout, Вы писали:
Pzz>>2) Можно объявить szTmp, как static. Проблема в том, что следующий вызов функции подменит предыдущее содержимое этого буфера, и если кто-то где-то все еще использует старое значение, этот кто-то сильно удивится.
S>По-моему такое не стоит советовать даже в шутку. Имхо, но это эталонный выстрел в ногу.
Совершенно не обязательно. Такое решение плохо, так сказать, масштабируется, но масштабирование не всегда бывает нужно. Так можно делать, если хорошо понимать, какие это накладывает ограничения, и как на эти ограничения случайно не наступить.
Здравствуйте, Pzz, Вы писали:
Pzz>Здравствуйте, Carc, Вы писали:
Pzz>Сразу видно, что ты никогда не программировал в среде, которая использует твой текущий стек для обработки прерываний.
Pzz>Это совершенно не нарушает сишную calling convention, но данные, которые "над стеком" могут быть испорчены в любой момент.
Pzz>Кстати, тоже самое может произойти и в привычной тебе среде, если компилятор вставляет служебные вызовы фукнций для проверки границ массивов, для профилировки и т.п.
Pzz>Не умничай, короче, целее будешь
Ты о себе?
Именно выше перечисленные варианты событий я и имел ввиду (служебные вызовы).
Цитата из моего первого поста
Я где-то увидел такое решение, и чуть не припух. Ибо проект был солидный, и парни там грамотные. Очень долго въезжал, что к чему. Потом стало ясно.
Я про тот случай и говорил. Что очень удивился вообще такому решению. И что оно вообще может работать. Это только информация к размышлению топик-стартеру. И там же первом посте написал к чему приводит использование таких "оптимизаций"
Здравствуйте, Pzz, Вы писали:
Pzz>Совершенно не обязательно. Такое решение плохо, так сказать, масштабируется, но масштабирование не всегда бывает нужно. Так можно делать, если хорошо понимать, какие это накладывает ограничения, и как на эти ограничения случайно не наступить.
Именно для целей возврата изменяемого значения? Если речь о единожды вычисляемой константе — вопросов нет, а вот возвращать результат, который может быть изменён в процессе работы программы — закладывать мину в код.
ЗЫ. Хотя наверно для устройств с ограниченными ресурсами это нормальный вариант.