Возврат строки из функции без malloc
Можно ли вернуть строку из функции без вызова malloc
?
У меня есть функция, как показано ниже:
char* getString(){
char tempStr[20]
// open file, read char by char and assign to tempStr
// ....
char* str = (char*)malloc(sizeof(char)*20);
strcpy(str, tempStr); // assume this copy the null terminating char as well
return str;
}
И затем, когда я вызываю getString()
, я присваиваю возвращаемое значение char*
, а затем освобожу его, когда закончите, как показано ниже:
void randomFunction(){
char* line = NULL;
int i = 0;
while (i < 1000) {
line = getString();
// do stuff with line
free (line);
line = NULL;
}
}
Однако мне интересно, есть ли способ сделать это без malloc
? И, это правильный способ вернуть строку из функции C?
Я попытался сделать некоторые исследования относительно того, как вернуться без malloc
, но не нашел четких ответов. Я новичок в C и до сих пор учился.
Ответы
Ответ 1
Вы не можете возвращать временные функции, и если вы не используете malloc, ваш массив символов , определенный в функции, будет временным. Альтернативным решением является передача символьного массива в качестве параметра функции и использование его в качестве выходного параметра.
Ответ 2
Существует три способа возврата строки из функции. (Ну, на самом деле их нет, но есть три общих способа вернуть указатель на строку, которую вызывающий может затем использовать для доступа к строке.)
-
Выделите пространство для строки, используя malloc()
внутри функции. Это самый гибкий метод, но он заставляет вызывающего пользователя отвечать за free()
выделенного массива. Он также может налагать некоторые накладные расходы.
-
Требовать от вызывающего пользователя выделить пространство для строки и передать указатель на это пространство. Это накладывает некоторые неудобства на вызывающего абонента. В частности, вызывающий должен решить, насколько большой может быть строка.
-
Возвращает указатель на (первый элемент) a static
массив, определенный внутри функции. Массив будет продолжать существовать после возвращения функции, но есть только одна копия, что означает, что последовательные вызовы будут сжимать результат, возвращаемый предыдущими вызовами. Это также означает, что массив должен иметь некоторый фиксированный размер, выбранный при написании кода.
Ответ 3
Это зависит.
Вы можете решить и документировать, что возвращаемая строка является указателем на некоторый статический внутренний буфер. Тогда ваша рутина не является повторной (нитобезопасной). Например, ctime
или getpwent
делает это.
Лучше всего было бы передать строку результата и размер в качестве аргументов и заполнить эту строку и, возможно, вернуть ее. getcwd
(или snprintf
или strftime
, который возвращает размер, а не указатель) работает таким образом.
Но обычно вы решаете и документируете, что возвращаемая строка является выделенной кучей, и это ответчик вызывающего абонента на free
. В этом случае вы можете использовать strdup
или asprintf
.
И вы можете использовать в своей программе консервативный сборщик мусора Boehm (например, используйте его GC_STRDUP
или GC_MALLOC_ATOMIC
для строк, и GC_MALLOC
для значений кучи, содержащих некоторые указатели.)
Если вы считаете, что стандартные malloc
или strdup
слишком медленные (но, пожалуйста, измерьте это в первую очередь), у вас могут быть собственные распределители пула и т.д.
У вас также могут быть альтернативные схемы (но важно их документировать). Например, вы можете вернуть некоторую интернированную строку или даже каноническую интернированную строку (иногда называемую "кварк" или "символ" ) - тогда способный использовать равенство указателя вместо равенства строк. Вы также можете использовать схему reference counter. Посмотрите, например, на что Glib (из GTK, но можно использовать за пределами GUI-программ!): GString-s, GQuark-s, строковые утилиты
Однако важно решить, является ли результат выделенной кучей или нет, и четко определить, кто несет ответственность за освобождение (и как он должен быть освобожден) от результата, выделенного кучей.
Вы можете использовать valgrind для преследования утечек памяти. Не забудьте передать -Wall -g
вашему компилятору gcc
!
PS. Я бы подумал об использовании Boehm GC. И я не думаю, что malloc
(или strdup
, asprintf
....) следует отклонить по соображениям производительности (вы могли бы выбрать другую и более быструю реализацию malloc
или использовать свои собственные пулы памяти), Однако утечка памяти может быть проблемой.
Ответ 4
Обычный способ переместить выделение памяти из функции - это передать указатели. Хотя на практике вам нужно будет убедиться, что вы тоже не переходите через границы буфера.
char* getString(char* tempStr){
// open file, read char by char and assign to tempStr
// ....
return tempStr;
}
void randomFunction(){
char line[20];
int i = 0;
while (i < 1000) {
getString(line);
// do stuff with line
}
}
Ответ 5
Поскольку ваша строка (по-видимому) всегда содержит 20 символов, вы можете просто сделать это:
void getString( char *outputString ) {
// do stuff to outputString instead of mallocing, or use local memory and copy it at the end
}
char line[20];
for( ... ) {
getString( line );
// do things with line
}
Поскольку это позволяет избежать многих мелких маллоков, это происходит быстрее.
Ответ 6
Общим способом сделать это в C является передача строки в качестве аргумента функции.
char *getString(char *str, int size){
// open file, read char by char and assign to tempStr
// ....
strncpy(str, tempStr, size); // assume this copies the null terminator
return str;
}
В качестве альтернативы вы можете указать строку, статичную или в файл или глобальную область, и скопировать в нее. Однако убедитесь, что если вы сохранили указатель на буфер, выделенный в кучу, в этом символе, который вы освободите его перед повторным назначением.
Ответ 7
Я не C-гуру, но я думаю, что это возможно в C. (Это основано на решении C++.) Конечно, вам нужно знать границу для размера строки (99 здесь), но вы можете вернуть строка без выделения, нет необходимости в выходных параметрах.
https://onlinegdb.com/r1akUBiHB
#include <stdio.h>
#include <string.h>
typedef struct str_{
char c[99];
} str;
str fun(int i){
str ret;
if(i > 5) strcpy(ret.c, "big");
else strcpy(ret.c, "small");
return ret;
}
int main(int argc, char* argv[]){
str ret = fun(argc);
printf("%s", ret.c);
return 0;
}
Я не уверен, зависит ли это от того, что C принудительно применяет что-то под названием "Оптимизация возвращаемого значения".
Кроме того, я не знаю, хотите ли вы никаких распределений, потому что вы не можете вообще или только для выступлений.
Если это второе, вы можете реализовать структуру, которая условно выделяет, если строка не помещается в предопределенный размер (99 здесь).
Это в основном то, что std::string
делает в C++, для коротких строк он не будет выделяться на практике.
Обратите внимание, что если это сработает, оно будет также поточно-ориентированным. (Другие решения, основанные на глобальных переменных, не являются потокобезопасными.)
Ответ 8
Просто объявите строку как статическую в функции и верните ее.