LINUX.ORG.RU

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

 


0

2

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

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

★★★★★

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

Ответ на: комментарий от AptGet

Молодец, уже настрочил пока я писал. Только вот 2проблемы - возвращать надо не buf, а чуть дальше, чтобы дебильчик не получил анальную боль дебагая даблфри в твоей функции, заюзав где-то фри на возврат.

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

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

конст в сишке пишут только низкоквалифицированные специалисты

Сразу видно "лалку анскильную", которая ни разу мелкоконтроллеры не погромировала!

Eddy_Em ☆☆☆☆☆
()

А нынче стало модно возвращать указатель на локальный буфер? Он же не валидный и вообще UB.

Cactus64k
()
Ответ на: комментарий от post-factum

Имею право. Ибо опенсорс! И вообще, я — не погромист.

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

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

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

За шкафом! Если ты не поставишь константе модификатор const, она вместо флеша будет торчать в оперативке, засирая ее! И ладно, если у тебя десяток-другой переменных. А если это структура на сто килобайт?

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

Ниужели после всего, что между нами было

нука нука нука...Он трогал твой указатель и переполнял твои кучи?

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

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

И да, в гцц есть атрибут который позволяет куда угодно тебе тебе и что угодно записать. Это в 10раз более удобно чем через говноконст с тонной говна в родате.

А если это структура на сто килобайт?

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

У меня таких проблем нет, я не юзаю кастыльное и протухшеубогое с89 и не жру говна с кастылей убогих.

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

И не решается проблема с многопоточностью.

$ ./test
239308800, *****
239845376, ******
240381952, *******
240918528, ********
241455104, *********
241991680, **********
242528256, ***********
243064832, ************
243601408, *************
244137984, **************
244674560, ***************
245211136, ****************
246284288, *****************
245747712, ******************
246820864, *******************
247357440, ********************
239845376, ******
239308800, *****
240381952, *******
240918528, ********
241455104, *********
241991680, **********
242528256, ***********
243064832, ************
243601408, *************
244137984, **************
244674560, ***************
245211136, ****************
246284288, *****************
245747712, ******************
246820864, *******************
247357440, ********************
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

const char* foo()
{
	static _Thread_local char *buf;
	static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
	static size_t dynamic_len = 5;

	pthread_mutex_lock(&mutex);

	dynamic_len++;
	size_t len = dynamic_len;
	
	pthread_mutex_unlock(&mutex);
	
	free(buf);
	buf = malloc(len);
	for (size_t i = 0; i < (len - 1); i++)
		buf[i] = '*';

	buf[len-1] = '\0';
	
	return buf;
}

static void* worker(void *p)
{
	const char *s = foo();
	printf("%d, %s\n", (int)pthread_self(), s);
	sleep(1);
	printf("%d, %s\n", (int)pthread_self(), s);
	return NULL;
}

#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))

int main()
{
	pthread_t threads[16];
	for (size_t i = 0; i < ARRAY_SIZE(threads); i++)
		pthread_create(&threads[i], NULL, &worker, NULL);


	for (size_t i = 0; i < ARRAY_SIZE(threads); i++)
		pthread_join(threads[i], NULL);
}

Как по мне, нормально сработало.

Вообще хоть какой-то смысл есть в этом варианте?

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

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

И да, я тебя не обижаю, просто есть более удобные способы, но ты можешь делать по старинке. Я тебя нивчем не обвиняю, не пичалься - ты хороший, а хорошим надо дружить не обижаться на меня. ^_^

TrueTsar1C
()
Ответ на: комментарий от post-factum

Нет. Все то. Допустим: шрифты для экранчика + конфиги + всякие таблицы (для тригонометрии и прочей шняги)...

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

А почему бы и нет? Когда размер страницы флеша ==1кБ, надо выравнивать на этот размер. И удобней делать структуры.

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

конст в сишке пишут только низкоквалифицированные специалисты

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

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

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

posix требует thread-safe malloc, см: http://man7.org/linux/man-pages/man7/pthreads.7.html

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

А причём тут треадсейв? Тут дело не в нём. Тут дело в реализации этого самого треадсейва. Это может быть либо у каждого треда своя хипа, либо общий хип.

Давай я тебе объясню в чем проблема, скорее всего тут:

  static _Thread_local char *buf;//будет уникальна всегда, с каждым созданием треда.

Т.е. если мы заспавни твой воркер 100500 раз - у нас будет 100500 маллоков, которые никто не удалит. Возможное решение - это в случае с уникальной куче - это уничтожение кучи при завершении нити.

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

Поэтому это будет течь. Т.е. значение переменной buf из завершеного потока не наследуется следующему созданному, а должно.

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

Просто уже про thread local storage.

anonymous
()

Как вариант - возвращай указатель на массив и длину строки в структуре, без лишних аргументов вообще.

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

Полагаю, что нет, раз не на все вопросы ответил.

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

Если есть возможность сказать компилятору,

Конпелятору насрать на твои рассказы.

Тут штука в том, что мои утверждения относятся только к реальным пацанам, а старообрядцев я ввиду не имею. У пацанов там 100500 *.c файлов, у пацанов там до сих пор линковка, у пацанов там инлайн ещё не случился. Если ты относишься к ним - мои утверждения к тебе не относятся.

(например, передача массива в функцию только для чтения)

И что это даст? Это ничего не даст.

то это нужно делать.

Кому это нужно и зачем? Мне не нужно. А объяснить почему это нужно тебе - ты не объяснил. Т.е. это блажь, а блажь и нужно это совершенно разные вещи.

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

И что это даст?

В случае

void foo (const int* const buf);
Компилятор не позволит ни изменить сам указатель внутри функции, ни изменить значение массива.

Также программист, когда видит такое, понимает, что в функции ничего из переданного не изменяется.

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

Компилятор не позволит ни изменить сам указатель внутри функции, ни изменить значение массива.

И что это даст? Почему вы не можете отвечать на вопросы прямо, а продолжаете пастить мне свои бесполезные старообрядческие куллстори из мира жабки?

Ну и да, эти эксперты, такие эксперты:

void foo (const char* const buf) {
  memmove((char *)buf, buf + 5, strlen(buf));
}


int main(void) {
  char str[] = "Не позволит, инфа сотка - пацан на лоре так сказал";
  foo(str);
  fprintf(stderr, "%s\n", str);
}

Также программист, когда видит такое, понимает, что в функции ничего из переданного не изменяется.

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

И да, это относиться только ко внешнему апи и там ты можешь писать как хочешь/по уставу/талмуду, либо как-то ещё. Это всё твои субъективные заморочки.

Если же во внутреннем коде пацаны не знаю что делают и как работают их функции, ну это кастыль и никакой объективной причинности не имеет. Т.е. это ваши субъективные заморочки.

Ещё раз, я тебе не запрещаю жить в своём мире эльфов и мистических конпеляторов, верить в свою религию и талмуд. Только научитесь не приравнивать свой мир ко миру всему, хорошо?

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

в приличном обществе за такое канделябром бьют без разговоров.

anonymous
()
Ответ на: комментарий от TrueTsar1C
void foo (const char* const buf) {
  memmove((char *)buf, buf + 5, strlen(buf));
}

А без приведения типов? Приведение типов сразу говорит о том, что с кодом что-то не так, поэтому пример крайне некорректен. Приводи корректные примеры, пожалуйста.

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

Анонимус неодобряет

lol.c: In function ‘foo’:
lol.c:2:3: error: implicit declaration of function ‘memmove’ [-Werror=implicit-function-declaration]
   memmove((char *)buf, buf + 5, strlen(buf));
   ^
lol.c:2:3: error: incompatible implicit declaration of built-in function ‘memmove’ [-Werror]
lol.c:2:3: error: implicit declaration of function ‘strlen’ [-Werror=implicit-function-declaration]
lol.c:2:33: error: incompatible implicit declaration of built-in function ‘strlen’ [-Werror]
   memmove((char *)buf, buf + 5, strlen(buf));
                                 ^
lol.c: In function ‘main’:
lol.c:9:3: error: implicit declaration of function ‘fprintf’ [-Werror=implicit-function-declaration]
   fprintf(stderr, "%s\n", str);
   ^
lol.c:9:3: error: incompatible implicit declaration of built-in function ‘fprintf’ [-Werror]
lol.c:9:11: error: ‘stderr’ undeclared (first use in this function)
   fprintf(stderr, "%s\n", str);
           ^
lol.c:9:11: note: each undeclared identifier is reported only once for each function it appears in
cc1: all warnings being treated as errors

Кто пустил этого ламера в мои интернеты?

anonymous
()
Ответ на: Анонимус неодобряет от anonymous

Кто пустил этого ламера в мои интернеты?

#include <stdio.h>
#include <string.h>
Deleted
()
Ответ на: комментарий от Deleted

А без приведения типов?

Обделался и в кусты? Что значит без приведения типов? Приведение типов валидная и основная операция. Ну и да, автокаст из const во что угодно валидная операция, и максимум, что можешь сделать конпелятор - высрать тебе ворнинг.

Приведение типов сразу говорит о том, что с кодом что-то не так, поэтому пример крайне некорректен.

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

Приводи корректные примеры, пожалуйста.

Я вон не понимаю, зачем при обсёре ты начинаешь юлить? Тыж кукарекнул конпелятор не позволит, а тут вдруг не позволить я должен себе сам.

И да:

typedef union {
  const char * cp;
  char * p;
} ctonc_t;

void foo (const char* const buf) {
  ctonc_t p;
  p.cp = buf;
  memmove(p.p, p.p + 5, strlen(buf));
}


char * proxy(void * p) {
  void ** ptr = p;
  return *ptr;
}

typedef struct {
  const char * ptr;
} ptr_t;

void foo1(const char * const buf) {
  ptr_t t = {buf};
  memmove(proxy(&t), buf + 5, strlen(buf));
}

Структуры, воиды и т.п. по мнению ламерка тоже нельзя юзать?

TrueTsar1C
()
typedef char * string;

const string func() {
    const string buf = malloc(10);
    return buf;
}

/thread

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

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

Если будет warning, значит нужно использовать.

Пример по прежнему некорректен.

mikhail@lens ~/dev $ gcc -Wall -Wpedantic -Werror test3.c
test3.c: В функции «foo1»:
test3.c:27:3: ошибка: инициализирующий элемент не может быть вычислен во время компоновки [-Werror]
   ptr_t t = {buf};
   ^
cc1: all warnings being treated as errors

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

Если будет warning, значит нужно использовать.

И что же нужно использовать?

Пример по прежнему некорректен.

Там два примера, а не собирается один. Со штанин уже ручьи текут - продолжай.

Давай я тебя научу собирать - gcc -Wall -Wpedantic -Werror 123.c -std=c11 -c

Как я понял структуры уже непоюзать? и касты тоже? Всё некорректно, когда обосрался в штанишки?

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

Там два примера, а не собирается один.

Хорошо, посмотрел на тот, который собирается. Об отсутствии необходимости использования const он не говорит. По типу функции видно, что программист не планирует изменять buf, следовательно, сразу видна ошибка.

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

Хорошо, посмотрел на тот, который собирается.

Ты посмотрел на оба, и сделал вид, что типа второй не работает, хотя это твой файл.

Об отсутствии необходимости использования const он не говорит.

Причём тут «отсутствии необходимости использования» - он говорит о фейле в твоём балабольстве,а именно:

Компилятор не позволит ни изменить сам указатель внутри функции, ни изменить значение массива.

Меня абсолютно не интересует что там у школьников необходимо, а что нет.

По типу функции видно, что программист не планирует изменять buf, следовательно, сразу видна ошибка.

Какая нахрен ошибка? Причём тут ошибка? Чтож ты сливаешься и юлишь как первоклассник?

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

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

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

ты сказал, что конпелятор чего-то там не позволит

Он и не позволил. Указатель остался целым, данные по указателю изменены заметным образом, а значит const выполняет свое предназначение.

Deleted
()

boehm-gc. зависимость — не апи, так что условиям удовлетворяет.

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