2014-01-03 7 views
4

Давайте представим себе, что у нас есть функция, которая должна возвращать два возвращаемых значения. Например, у нас есть функция, которая возвращает 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 

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

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

+1

Зачем нужны более умные варианты, чем вы предоставили? При этом вы просто запутываете свой код. – meagar

+0

@meagar. Зачем? Мне любопытно, каковы другие способы сделать это. Могут быть некоторые действительно интересные причины этого. Более того, у меня есть сильное чувство, что в C разные способы делать то же самое могут иметь разные эффекты и соображения. – Sigurd

ответ

3

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

struct string function(); 

Если кодовая не использует один стандартный-структуру везде для строк я бы посоветовал использовать структуру. Сложность конвертирования назад и вперед на самом деле не стоит того.

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

char* function(int* length); 
+0

Не могли бы вы объяснить, почему 'char * f (int *)' vs 'int f (char **)' – Sigurd

+0

@Sigurd: По крайней мере, в большинстве функций кода и библиотеки, которые я видел, 'char **' is используется, когда вызывающий абонент отвечает за выделение памяти и уже определил длину или обеспечивает максимальную длину. Например, в системе Linux вызывают 'read (2)', вы предоставляете предварительно выделенный буфер и максимальную длину данных для этого буфера. – Linuxios

+0

«В противном случае соглашение должно вернуть символ * и иметь длину в качестве параметра указателя:« - утверждая, что соглашение ** является **, это немного жесткое. Существуют и другие, равноправные соглашения. –

1

Другой способ:

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

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

struct char_and_len foobar(); 

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

1

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

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 -correct.

+0

Возможно, в моем вопросе я не слишком поняла. Мне нужно вернуть int и char *. Представим себе, что char не заканчивается с \ 0, поэтому нам нужно вернуть его длину. – Sigurd

+0

@Sigurd Затем вы можете изменить свою 'struct', чтобы она содержала' char * 'и' int'. И вы можете изменить одно из возвращаемых значений в третьем примере на 'int'. И забудьте о втором подходе. –

+0

Я знаю, как вернуть несколько значений :) Мой вопрос был тем, что является самым эффективным способом и почему. – Sigurd

0

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

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

+0

Строки в C всегда являются указателями, независимо от того, передаете ли вы сам указатель по ссылке или значению. – Linuxios

+0

Да, но у него тоже есть int. – Abend

+0

Интенсивность обычно составляет четыре байта. В 32-битной системе, так же как и указатель. В 64-битной системе указатель (8 байт) будет на самом деле * больше *, чем 'int'. – Linuxios

1

Мой любимое бы

void foobar(struct char_and_len*); 

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

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

Если у вас есть структура, созданная для этой цели, почему бы не вернуть всю структуру по значению. – this

+0

Хотя я бы с вами согласился, потому что мы имеем дело со строками здесь, вы не думаете, что проблема заключается в определении строки типа, которая должна быть преобразована в и из? – Linuxios

0

В С ней данным не является «нормальным» для возврата двух значений. Это означает, что, как вы уже сделали, вы можете создать структуру для возврата результата. Это формально правильный способ сделать это, но не самый простой, и он важен. Поэтому я бы полностью забыл это решение.

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

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

символ * бар (Int * lenP); //

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

, как если бы он был определен как псевдо синтаксиса (S, L) = бар();

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

недействительным бар (символ * s, Int * lenP); // этот случай одинаково относится к аргументам слишком.

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

1

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

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

В общем ...

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

Смежные вопросы