Эффективные множественные возвращаемые значения

Давайте представим, что у нас есть функция, которая должна возвращать два возвращаемых значения. Например, у нас есть функция, которая возвращает char* и его длина. Char назначается внутри этой конкретной функции.

Я могу представить следующие способы сделать это:

int foo(char **result);       // Passing pointer to char*, returning int
char* bar(int *len);          // Passing pointer to int, returning char*
struct char_and_len foobar(); // Returning struct that contains both values

Существуют ли другие способы реализации нескольких значений и каков наиболее эффективный способ сделать это?

Я был бы очень признателен за подробное объяснение, учитывая производительность, выравнивание памяти или любую другую скрытую функцию Си.

7 ответов

Решение

Здесь есть два случая. Если вы работаете в базе кода / фреймворке (например, Glib), у которого есть стандартная строковая структура, используемая во всем приложении, используйте третий вариант:

struct string function();

Если ваша кодовая база не использует везде одну стандартную структуру для строк, я бы посоветовал не использовать структуру. Затруднения преобразования туда и обратно не стоят того.

В противном случае, соглашение (по крайней мере, что я видел) состоит в том, чтобы вернуть char* и иметь длину в качестве параметра указателя:

char* function(int* length);

По-другому:

void foo(char **result, int *len);

Худшее на мой взгляд это:

struct char_and_len foobar();

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

Мой любимый будет

void foobar(struct char_and_len*);

По следующим причинам:

  • Необходимо передать только один параметр
  • возвращаемое значение / выходной параметр не смешиваются
  • возвращаемые значения можно игнорировать, особенно когда возвращаемое значение необходимо снова освободить, это может стать серьезным источником ошибок программирования. Выходные параметры не могут быть проигнорированы.
  • Наличие указателя на структуру позволяет избежать слишком большого количества операций копирования. Только один указатель должен быть предоставлен для функции.
  • Таким образом, вызывающая функция может решить, где struct char_and_len хранится (в куче, стеке), в то время как при использовании возвращаемых значений данные должны быть помещены в стек хотя бы временно

Вы можете просто вернуть массив и обернуть его в структуру:

typedef struct {
    char *strings[2];
} RetType;

RetType func()
{
    return (RetType){ { "foo", "bar" } };
}

Другое идиоматическое решение - это C для передачи массива в вашу функцию и его заполнения:

void func(char *strings[2])
{
    strings[0] = "foo";
    strings[1] = "bar";
}

Третье решение - вернуть одно значение и передать другое по указателю (хотя это более важно, если у вас есть значения разных типов):

char *func(char **outparm)
{
    *outparm = "foo";
    return "bar";
}

Кроме того, извините за то, что я не const-правильный.

Используйте строку.

Для приведенного вами конкретного примера помните, что длина неявно или явно является свойством любого типа строки. Например, строки в стиле C заканчиваются нулем, так что даже если нет явной длины, вызывающая сторона может определить длину строки. Строки в стиле Pascal включают длину в качестве первого байта. char* это не обязательно строка, это может быть обычный старый текстовый буфер, где вам нужна длина. Но смысл строковых типов состоит в том, чтобы избежать необходимости передавать данные и длину отдельно.

В более общем смысле...

Функция может возвращать только одно значение, поэтому, если вам нужно вернуть больше, вам нужно либо упаковать все в одно значение, используя структуру, либо передать указатель (или указатели) в местоположение для получения результатов. Какой метод вы используете, зависит от обстоятельств. У вас уже есть структура, определенная для данных, которые должны быть возвращены? Может ли вызывающий объект иметь существующий объект, который может получить результаты? Нет лучшего метода, только подходящий метод для ситуации.

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

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

В C не нормально возвращать два значения. Это означает, что, как вы уже сделали, вы можете создать структуру для возврата результата. Это формально правильный способ сделать это, но не самый простой, и он неуклюжий. Так что я бы полностью забыл это решение.

Как правило, другой способ возврата результатов - передача аргументов по ссылке (в Паскале VB и т. Д.). В C нет возможности передавать их по ссылке, вместо этого передается указатель. Но в C++ есть возможность передать переменную по ссылке (это просто означает передачу указателя, но с использованием переменной).

Поэтому я считаю, что самый простой способ сделать то, что вам нужно, это определить:

char * bar (int * lenP); //

теперь у вас есть результат функции, которая может вернуть два результата:

например, если он был определен как псевдосинтаксис (s,l)=bar();

Кроме того, вы также можете использовать:

void bar (char * * s, int * lenP); // этот случай одинаково относится и к аргументам.

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

Другие вопросы по тегам