Предупреждение при передаче адреса локальной переменной
Столкнулся с теоретической проблемой: нужно реализовать функцию, возвращающую часть строки до символа-разделителя (префикс). Решил проблему так:
{
if(!su_strLen(sStr) || !su_strLen(sSeparators))
return (str)NULL;
uchar acPart[su_strLen(sStr)];
su_word usStrIterator = 0;
su_word usSepIterator = 0;
while(sStr[usStrIterator])
{
while(sSeparators[usSepIterator])
if(sStr[usStrIterator] == sSeparators[usSepIterator])
{
acPart[usStrIterator] = '';
return (str)acPart;
}else
usSepIterator++;
acPart[usStrIterator] = sStr[usStrIterator];
usStrIterator++;
usSepIterator = 0;
}
return (str)NULL;
}
Примечание 2: функция su_strLen(sStr) возвращает длину sStr.
Не могу понять, почему? Ведь соглашение __cdecl указывает, что стек очищает вызывающая сторона. Определять глобальную переменную нельзя потому, что функция станет рекурсивно не безопасной. Возвращать результат, как измененный аргумент тоже нельзя: функция должна поддерживать конвейерную обработку.
Кто что думает? Спасибо за внимание.
Красивого решения без изменения исходной строки для вашего примера пока не вижу.
Как заглушку вставил
{
if(!su_strLen(sStr))
return (su_str)NULL;
su_word wStrIterator = 0;
while(sStr[wStrIterator])
aucCopyStr[wStrIterator++] = sStr[wStrIterator];
aucCopyStr[wStrIterator] = '�';
return (su_uchar)TRUE;
}
Так ли это? Сомневаюсь, что это допустимо стандартом. Думаю, это расширение языка от GCC. Это не переносимо на другие компиляторы, лучше избегать. По-моему это ничем не отличается от выделить память new в ф-ции и просто delete[] в ней-же (уверенности нет). Тут ничего опасного нет. Опасно выделять в одном месте, освобождать - в другом. У вас же это приведет к delete[] массива прямо в ф-ции и его потере.
Да, это так, можете не сомневаться. Стандарту уже 14 лет, а K&R до сих пор жива (не без причин, конечно). О new / delete речи нет. Код пишется на стандартном Си.
Warning объясним вот чем: возвращается указатель, а не содержимое массива и компилятор опасается, что в программе будет висеть указатель (сохраненный) на не существующий участок памяти. К слову сказать: функция g_get_application_name из glib возвращает const g_char*, т.е. указатель на неизменяемую строку, хотя в параметрах у нее void. Строго говоря, сброс стека обычно происходит как
Нет, абсолютно. Warning связан с тем, что я возвращаю указатель на локальный динамический массив acPart (об этом можно судить по номеру строки. Простите, не привел).
Смысл вопроса-то вот в чем: да, локальный массив (хоть и динамический) находится в стеке, который будет сброшен после возврата из функции. НО! Ведь "ответ" функции возвращается тоже через стек. Следовательно, так как "ответ" возвращается через стек, и по правилам __cdecl стек очищает вызывающая сторона, логично предположить, что вызывающая сторона не станет этого делать до того, как использует возвращенное значение. Следовательно, массив в стеке должен остаться целым до момента использования "ответа", как я понимаю.
Да, в случае __stdcall возвращать указатель на локальную переменную не верно, так как стек сбросит вызываемая функция, и гарантии, что значение в стеке уцелеет нет (его может переписать следующий вызов). Но здесь я ведь явно задал тип вызова __cdecl. Что же еще сделать?..
Лучше вернуть адрес относительно sStr (она не разрушается).
Вот пример законного возврата адреса:
//string_head должна быть достаточной для хранения результата
char* AppendString(char* string_head, char* string_tail) {
int length;
length = strlen(string_head);
strcpy(string_head + length, string_tail);
return string_head;
}
Лучше вернуть адрес относительно sStr (она не разрушается).
Что Вы имеете ввиду под "вернуть адрес относительно sStr?" Прошу раскрыть тему, если можно. Если Вы имеете ввиду переписать строку sStr, то нет: исходная строка не должна измениться.
Насчет предположений, позволю себе с Вами не согласиться. Порядок cdecl определен стандартами C89 и C99. Это совсем не предположения. Если не учитывать порядок работы со стеком, создавать библиотеки и делать линковку из объектных файлов разных разработчиков станет сложно. ИМХО.
Красивого решения без изменения исходной строки для вашего примера пока не вижу.
Так ли это? Сомневаюсь, что это допустимо стандартом. Думаю, это расширение языка от GCC. Это не переносимо на другие компиляторы, лучше избегать. По-моему это ничем не отличается от выделить память new в ф-ции и просто delete[] в ней-же (уверенности нет). Тут ничего опасного нет. Опасно выделять в одном месте, освобождать - в другом. У вас же это приведет к delete[] массива прямо в ф-ции и его потере.
В С++ тоже не понятно, что делать с массивами переменной длины. Может, кто знает появились они в стандарте и когда?
В С++ тоже не понятно, что делать с массивами переменной длины. Может, кто знает появились они в стандарте и когда?
Полагаю, поддержка динамических массивов реализована в C RTL с помощью того же malloc() / free(). Просто теперь их вызов стал не явным для программиста. Я даже насчет free() не уверен, т.к. есть штатный способ увеличить длуну массива, но нет способа ее уменьшить (массив изменяется только вверх).
Последний известный мне стандарт C++ - C++11. И, насколько мне известно, штатных способов управления памятью, кроме new() / free() в нем нет.
C++11, Википедия
К слову, у C тоже есть стандарт от 2011 года:
C11, Википедия