LINUX.ORG.RU

C — возврат буфера из функции

 


0

2

Привет. Сегодня проходил собеседование, задали вопрос, как при сохранении API сделать «правильно»:

char const *foo(void){
    char buf[10];
    //fill buf
    return buf;
}
На мой взгляд, наилучший вариант — возвращать статический буфер. Минусы: в клиенском коде зачастую придется копировать эту строчку, плюс в многопоточном варианте скорее всего будет ой-ой. Варианты возврата динамически выделенного буфера и модификации API отметаем. Говорят, есть вариант получе. Найти не смог, сами ответ мне не дали, сославшись на то, что не комментируют корректность ответов. Кроме этих трех вариантов ничего в голову не приходит, да и не гуглится. Ваши варианты?

★★★★★

Последнее исправление: staseg (всего исправлений: 1)

Правильного ответа нет.

К тому же вот это

()

намекает, что у них какой-то странный Си.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Я просто (неверно) упростил код.

Ок, а какие еще есть варианты? Сохраняем API, за пределами функции никакихдополнительных телодвижений не производим (освобождение или копирование).

staseg ★★★★★
() автор топика
Ответ на: комментарий от i-rinat

у них просто странные составлятели вопросов. Такие вопросы обычно задают люди, которые сами далеки от знания чего бы то ни было.

anonymous
()
Ответ на: комментарий от anonymous

у них просто странные составлятели вопросов. Такие вопросы обычно задают люди, которые сами далеки от знания чего бы то ни было.

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

staseg ★★★★★
() автор топика
Ответ на: комментарий от anonymous

static __thread

Вот я удивился, когда компилятор из Android NDK ничего не сказал, но оставил переменную общей для всех потоков.

i-rinat ★★★★★
()
Ответ на: комментарий от staseg

ну тут только царя звать.

Яб вообще поинтересовался что за задача такая. const char * как бы намекает, что менять буфер не нужно извне. Может там захардкоженная строка вообще? :D

anonymous
()
Ответ на: комментарий от anonymous

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

Кто тут ныне царь?

staseg ★★★★★
() автор топика

Говорят, есть вариант получше

Есть: выделять буфер и передавать его в аргументе.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от anonymous

Добавить еще вызов функции setbuf(*buf)

А вообще, можно и сюда вовнутрь запихнуть malloc. Только пердупердить в документации, что таперча надо free юзать.

Больше потокобезопасных вариантов в голову не приходит.

Eddy_Em ☆☆☆☆☆
()

Сейчас пришла в голову идея: использовать достаточно большой статический буфер, записывать результат, выставлять \0, возвращать результат на начало, откуда мы заполняли. При следующем вызове начинать записывать после этого \0 и т.д., пока в буфер помещается результат, иначе переходить к его началу. Если размер возвращаемой строк примерно известен, можно оценить размер буфера на, скажем, десять результатов. Это не спасет от проблем с многопоточностью, зато всякие printf(«%s,%s\n», foo(), foo()) пройдут гладко.

staseg ★★★★★
() автор топика
Ответ на: комментарий от Eddy_Em

Наркоман, ну читай условия же, ну! Я твои варианты описал в первом же сообщении.

staseg ★★★★★
() автор топика

Лучше бы, конечно, пример более полный. Можно как-то вообще задачу-то сформулировать?

anonymous
()
Ответ на: комментарий от staseg

Ага, но как только будет переполнение, ты загадишь буфер, который еще кому-то нужен(возможно). Авторы вопроса дураки, скорее всего. А правильного ответа мы не подберем.

anonymous
()

Варианты возврата динамически выделенного буфера .. отметаем

это почему?

Harald ★★★★★
()

Варианты(часть тобой уже перечислена): - статический буффер - статический буффер локальный для потока - динамический буффер - заранее выделенный большой буффер, аллокация в нем и возврат туда(пишем свой вариант free)

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

anonymous
()

Ежели всё так, как ты расписал, то вариантов больше нет. Может, опять что-то упустил, типа void? Про многопоточный вариант ты сам додумал, или это было в условиях?

eagleivg ★★★★★
()
Ответ на: комментарий от anonymous

версии 4.7 чтоль?

Не помню. Какой там был текущий в NDK летом 2013-го? Вот он.

Вроде бы, дело было не в компиляторе, а в libc.

i-rinat ★★★★★
()
Ответ на: комментарий от staseg

зато всякие printf(«%s,%s\n», foo(), foo()) пройдут гладко

О, я такое делал. Но только для отладочной печати. Для постоянной работы такое не годится.

static
const char *
rect2string(VdpRect const *rect)
{
    // use buffer pool to enable printing many rects in one printf expression
    static char bufs[8][100];
    static int i_ptr = 0;
    i_ptr = (i_ptr + 1) % 8;
    char *buf = &bufs[i_ptr][0];
    if (NULL == rect) {
        snprintf(buf, 100, "NULL");
    } else {
        snprintf(buf, 100, "(%d,%d,%d,%d)", rect->x0, rect->y0, rect->x1, rect->y1);
    }
    return buf;
}
i-rinat ★★★★★
()

умиляют такие условия задачи. говнокодеры блин. о рефакторинге что-нибудь слышали?

вариантов нет. Это говнокод. Либо ты его меняешь, либо - продолжаешь черпать полной ложкой дальше.

Deleted
()
Ответ на: комментарий от Deleted

щаща, погодите, придет царь!! вот придет царь и всем расскажет, что все вокруг бобуины и нихера не знают...

anonymous
()
Ответ на: комментарий от i-rinat

static char bufs[8][100];

Не экономно. Такой вариант будет экономнее:

const size_t size = 8 * 100;
static char buffer[size];
static size_t pos = 0;
do
{
   size_t maxSize = size - pos;
   int res = snprintf(buffer[pos], maxSize, "(%d,%d,%d,%d)", rect->x0, rect->y0, rect->x1, rect->y1);
   if(res < maxSize)
   {
      pos += res;
      break;
   }
   pos = 0;
} while(1);

Тут нужно еще добавить проверку на строку, которая длиннее

andreyu ★★★★★
()
Ответ на: комментарий от andreyu

Тут нужно еще добавить

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

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Вот это вот ломает всю радость от экономии.

Можно просто добавить еще один if внутри второго. В вашем случае длинна строки вообще ограничена размером элемента. В моем случае длина строки ограничена размером всего буфера.

andreyu ★★★★★
()

есть еще вариант, кроме вышеперечисленных:

char const *foo(void) {
    static thread_local char *buf;
    free(buf);
    buf = malloc();
    //fill buf
    return buf;
}
AptGet ★★★
()
Последнее исправление: AptGet (всего исправлений: 1)
Ответ на: комментарий от staseg

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

Если два раза вызывать эту функцию с одинаковым аргументом, она должна ту же строку вернуть?

А если вызвать сначала с одним, потом с другим, присвоив результат разным указателям, указатели будут указывать на разные буфера?

Если на оба вопроса ответ «да» то надо использовать какой-нибудь malloc. Хотя еще можно выделить достаточно большой статический массив и в него уже писать байтики, при этом можно запоминать, какие передавались аргументы в функцию, и при вызове функции с каким-то аргументом, который ранее уже был, можно возвратить указатель на нужный кусок в статическом массиве. Понятно, что никакого освобождения памяти тут не предусмотрено

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

А необходимость копировать строчку разве не нарушает API? Самый лучший вариант без требования сохранять API это просто передавать указатель на массив, который надо заполнить.

SZT ★★★★★
()
Ответ на: комментарий от anonymous

man free

The free() function deallocates the memory allocation pointed to by ptr. If ptr is a NULL pointer, no operation is performed.

mix_mix ★★★★★
()
Ответ на: комментарий от AptGet

есть еще вариант, кроме вышеперечисленных:

printf("%s%s\n", foo(), foo());

И не решается проблема с многопоточностью. Вообще хоть какой-то смысл есть в этом варианте есть?

staseg ★★★★★
() автор топика
Последнее исправление: staseg (всего исправлений: 1)
Ответ на: комментарий от SZT

Если два раза вызывать эту функцию с одинаковым аргументом, она должна ту же строку вернуть?

Содержимое строки то же.

А если вызвать сначала с одним, потом с другим, присвоив результат разным указателям, указатели будут указывать на разные буфера?

Я уже плохо соображаю.

А необходимость копировать строчку разве не нарушает API? Самый лучший вариант без требования сохранять API это просто передавать указатель на массив, который надо заполнить.

Да тоже ломает. Но копировать ее нужно далеко не всегда. Такой метод иногда используется в POSIX.

staseg ★★★★★
() автор топика
Ответ на: комментарий от AptGet

Офигеть!

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

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от anonymous
static __thread buf[buflen];

Вуаля! =D

// но это не избавит от проблем вида printf("%s%s\n", foo(), foo());

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от staseg

Варианты возврата динамически выделенного буфера и модификации API отметаем.

Скорее всего ты что-то недопонял, либо у тебя там тотальные плахие люди.

Мы берём за api - функцию с возвращаемой строкой, с условием, что следующий вызов функции уничтожает строку из предыдущего вызова.

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

char * fun(void) {
  static void * ptr = NULL;//сюда хреначить кастыли, либо как уже выше говорили пацаны треадлокал, но тут штука ещё в том,
  //что поидее у норм аллокатора на кадждый тред свой хипец, но говнари это не учитывают, поэтому треадлокала тут не хватит - хреначить надо синх.
  free(ptr);
  char kokoko[] = "конст в сишке пишут только низкоквалифицированные специалисты, не юзают структуры в сишке только низкоквалифицированные специалисты";// заменил аутисты на низкоквалифицированные специалисты, а то вдруг забанят.
  return memcpy((ptr = malloc(sizeof(kokoko) + 16)) + 16, kokoko, sizeof(kokoko));//это даблфри защита от низкоквалифицированные специалиста.
}

А так задача не имеет смысла, такой апи устарел лет на 20. Хочешь возвращать строковое представление какого-то говна не через кучу - вперёд, возвращай строку в структуре.

TrueTsar1C
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.