2011-12-22 4 views
14

У меня есть функция, которая задает два целых числа и возвращает строку. Сейчас у меня есть это:Правильный способ вернуть строку из функции

char* myfunc(int a, int b, int* len) 
{ 
    int retLen = ... 
    char* ret = malloc(retLen + 1); 

    if (len != NULL) 
    { 
     *len = retLen; 
    } 

    return ret; 
} 

Однако, большинство функций в библиотеке C, как правило, сделать что-то подобное:

int myfunc(char* ret, int a, int b) 
{ 
    ... 

    return retLen; 
} 

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

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

Что считается хорошей практикой при возвращении строки, длина которой динамически найдена с учетом входных данных?

ответ

7

Узор я видел в программах режима ядра является:

  1. Вы вызываете функцию один раз, с некоторой выделенной памяти, если вам посчастливилось иметь некоторые доступны, или нулевой, как парам, если вам случится нет у них
  2. Если у вас выделена память, и функция обнаружила ее достаточно, она помещает результат в эту память и возвращает OK
  3. Если у вас не было памяти для передачи, или слишком мало памяти, функция возвращает ERROR_NOT_ENOUGH_MEMORY и помещает в выходном параметре необходимая память.
    • Вы затем выделить эту память, необходимую и повторный вызов функции

Пример:

int myfunc(
    __out char* output, 
    __in size_t given, 
    __out size_t needed_or_resulted, 
    extra params ... 
){ 
    ... implementation 
} 

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

Используется как:

int result = myfunc(output, given, needed_or_resulted, extra params ...); 
if(result == OK) { 
    // all ok, do what you need done with result of size "needed_or_resulted" on "output" 
} else if(result == ERROR_NOT_ENOUGH_MEMORY) { 
    output = malloc(needed ... 
    result = myfunc(output, given, needed_or_resulted, extra params ...); 
    if(result == OK) { 
     // all ok, do what you need done with result of size "needed_or_resulted" on "output" 
    } else if(result == ERROR_OTHER) { 
     // handle other possible errors 
    } else { 
     // handle unknown error 
    } 
} else if(result == ERROR_OTHER) { 
    // handle other possible errors 
} else { 
    // handle unknown error 
} 
+0

Конечно, с этим рисунком вы передали бы длину вместе с указателем. Это может быть даже параметр ввода/вывода (передан как 'size_t *'). – cHao

+0

Да, действительно, я отредактировал подпись. – clyfe

+0

Очень хороший ответ, который очень помогает, поэтому спасибо. Пара вопросов: почему для этой функции предпочтительнее функция 'size_t myfunc (char * output, size_t given, ...)', которая эффективно возвращает 'need_or_resulted' для пользователя, чтобы сравнить его собственное значение для успеха? Кроме того, почему это предпочтительнее второй функции, такой как 'myfuncLength', которая выполняет необходимые вычисления длины? – Matt

2

Вы вправе указать причину, по которой предпочтительна подпись int myfunc(char* ret, int a, int b). На самом деле это объясняет другое: почему нужно вернуть длину (размер буфера равен MAX, поэтому нам обычно нужно сообщить вызывающему, сколько из них мы действительно использовали).

Когда вы выделяете строки внутри функции, вы обычно не возвращаете размер строки, потому что strlen может быть использован для определения этого. Посмотрите на strdup на пример функции, которая динамически выделяет строки. Поэтому я хотел бы изменить подпись вашей функции

char* myfunc(int a, int b) { 
    ... 
} 
+0

Если я читаю этот вопрос правильно, нет 'MAX'. Или, если он есть, он слишком велик, чтобы создавать буферы. – cHao

+0

@cHao Правильно, это мнимая «МАКС», которую OP говорит, что он не хочет вводить. В первой части ответа я объясняю, почему нам нужно возвращать длину, когда есть 'MAX', не предполагая, что OP должен иметь один. Вторая часть больше касается ситуации. – dasblinkenlight

+0

Кроме того, этот вид превращает правило о 'free'ing, что вы' malloc' на его ухе. Вам лучше понять, кто владеет памятью в документации для этой функции. – cHao

3

Последнее лучше, потому что ключи в вызывающем о том, кто несет ответственность за освобождение памяти. Первый вызывает большие проблемы, если вызывающий и вызываемый используют разные реализации malloc (например, в Windows, отладка и выпуск часто используют несовместимые модели памяти).

1

Я бы передать char * по ссылке.Если функция выполняется успешно, выделите строку и присвойте ей ссылку на указатель и верните длину строки. Если ошибка встречается, установите errno и return -1.

int myfunc(int a, int b, char ** str) 
{ 
    int retLen; 

    /* code to calculate string length required */ 

    if (!(str)) 
    { 
     errno = EINVAL; 
     return(-1); 
    }; 
    if (!(*str = malloc(retLen))) 
     return(-1); 

    /* calculate new value and store to string */ 

    return(retLen); 
} 
2

Следуйте интерфейс snprintf, стандартную функцию с точно такой же вопрос:

size_t myfunc(char *s, size_t n, int a, int b); 

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

После успешного завершения функция snprintf() должна вернуть количество байтов , которое было бы записано в s, если бы n было достаточно большим, за исключением завершающего нулевого байта.

Если значение п равно нуль на вызов к snprintf(), ничего не должно быть написано, количество байт, которые были написаны уже н было достаточно большой за исключением завершающего нуля должны быть возвращено, и s может быть нулевым указателем.

Типичное использование:

size_t needed = myfunc(0, 0, a, b) + 1; 
char *buf = malloc(needed); 
if (buf) { 
    myfunc(buf, needed, a, b); 
} 

Вы можете включать нулевые байты в счете вернулся - это делает вызывающий код проще, хотя и немного менее знакомы людям, привыкших к стандартным snprintf.

Если вычисление retLen является удивительно дорогостоящим, может существовать аргумент для функции, которая вычисляет его по мере генерации строки и возвращает выделенный буфер нужного размера (возможно, на пути есть). Но я обычно не думал об этом. Для удобства пользователей, которые хотят выделить, просто поместите вышеуказанный код в функцию myfunc_alloc и неважно, что он дублирует немного работы. Пользователи, которые уже имеют буфер можно назвать myfunc непосредственно следующим образом:

if (myfunc(buf, bufsize, a, b) >= bufsize) { 
    printf("buffer too small, string (%s) has been truncated\n", buf); 
} 
Смежные вопросы