LINUX.ORG.RU

Си: прилично ли передавать в функцию указатели, заполненные мусором после malloc?

 ,


0

1

Прилично ли делать так, что указатель выделяется с помощью malloc в одной функции, передаётся без заполнения выделенной памяти осмысленными значениями, а заполняется полезными данными - в другой функции? Принято ли так делать, или это противоречит каким-нибудь там правилам хорошего тона?

★★★★★

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

Принято ли так делать

Нет, не принято. На примере изображений: создание по размерам «нулевого» изображения, только потом его передача.

anonymous
()

Да. Читаю сейчас книгу Extreme C. Там именно так и делается

person_t* person = person_new();

person = person_ctor(age, birth_year);

Функция person_new возвращает указатель от malloc, а конструктор (person_ctor) заполняет объект.

anonymous
()

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

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

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

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

Вполне нормально, почему нет?

Например:

ArrayInt arr;

arrIntInit(&arr, 10); выделили память под 10 элементов

arrIntFill(&arr, 0xffff); // заполняем значениями

// ...

Или я не так понял вопрос?

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

Эта другая функция сама память выделить не может?

А зачем функции знать, какая память ей передана - стек ли, статика ли, либо куча?

vodz ★★★★★
()

Тебе - можно.

anonymous
()

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

XMs ★★★★★
()

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

Хорошим тоном будет иметь не 1 а 3 таких функции.

  • Превая просто выделяет память (аля malloc)
  • Вторая выделяет память с дефолтным значением заполения (аля calloc)
  • Третья выделяет и заполняет значением пользователя (аля memset)

Имена должны быть одинаковы с пре или постфиксами для второй и третьей.


Но это базово. Можно придумать по разному. Да и порой таки надо принудительно очищать память тем или иным значением. Хз. Короче.

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от xaizek

Вопрос чисто про культуру.

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

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

den73 ★★★★★
() автор топика
Ответ на: комментарий от LINUX-ORG-RU

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

den73 ★★★★★
() автор топика
Ответ на: комментарий от LINUX-ORG-RU

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

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

Коротко говоря, если я допустим пишу свой стандарт MISRA, могу ли я туда вписать запрет на такую передачу

#define malloc(x) calloc(1, x)

хватит всем

anonymous
()

Зависит от контракта. Так-то можно делать всё, что стандартом не запрещено, просто надо явно это указывать и проверять.

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

Зависит от того, что эта функция с ней делает. Может и не надо.

xaizek ★★★★★
()

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

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

А зачем функции знать, какая память ей передана - стек ли, статика ли, либо куча?

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

Другое дело, что не всегда можно заранее определить, сколько выделять. Если выделяется только базовая структура, в которой ещё куча указателей на дополнительные объекты, выделяемые дочерней функцией — выгода такого решения становится неочевидной. Всё равно для освобождения придётся отдельную функцию писать…

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

hobbit ★★★★★
()
Последнее исправление: hobbit (всего исправлений: 2)
Ответ на: комментарий от sparks

Спасибо! Собственно, на этом тема вроде как исчерпана.

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 2)

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

Lrrr ★★★★★
()

А как по другому инициализировать объекты? Запретить кастомные аллокаторы? Или городить для них отдельно какой-нибудь интерфейс?

Kuzy ★★★
()

В большинстве случаев сам чистишь, дальше передаешь в функцию указатель. Как минимум так сделано в OpenSSL, больше Сишных библиотек не помню.

xpahos ★★★★★
()

ИМХО всё хорошо пока ты выделяешь память и высвобожаешь в одном и том же блоке, а касательно вопроса - это вопрос соглашения и прилично такое или нет будет зависеть от проекта - главное придерживаться одного стиля. Также в библиотеках желательно оборачивать malloc и free в что-то типа libname_malloc и libname_free, дабы в любое время свободно менять способ выделения памяти во всей библиотеке.

Моё мнение что это очень даже нормально, если к примеру формируется единый интерфейс работы с объектом:

void matrix_alloc(int n, int m, double **mat);
void matrix_free(double *mat);
void matrix_ones(int n, int m, double *mat);
void matrix_identity(int n, int m, double *mat);
...
int  main()
{
    ...
    int m = 224;
    int n = 224;
    double *A;
    matrix_alloc(n, m, &A);
    matrix_ones(n, m, A);
    ...
    matrix_free(A);
    ...
}


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

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

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

Какие еще аллокаторы? Память если и должна выделятся, то только один раз в начале! А с матрицами это на фортран.

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

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

Legioner ★★★★★
()

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

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

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

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

Это ясно только для единовременно написанного кода, который никогда никто не меняет/расширяет. Обнуление структуры сразу после создания позволит в будущем спокойно эту структуру расширять без риска забыть какие-то поля доинициализировать, или написать другую функцию, которая будет инициализировать по-другому и частично – опять же без риска получить не до конца инициализированный объект. Совершенно неимоверное количество человекочасов потрачено многими людьми на отлов таких багов, которых можно было избежать при использовании при использовании calloc() или malloc() + memset() вместо простого malloc(). Плюс, надо учесть, что с одним и тем же кодом работают разные люди – всем заморачиваться вопросом, как именно выделили память под структуру? Это будет непродуктивный расход времени.

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

Обнуление структуры сразу после создания позволит в будущем спокойно эту структуру расширять без риска забыть какие-то поля доинициализировать,

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

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

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

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

Поклонники rust же. Мало того что calloc не вернёт область памяти где ключи лежали, так ещё и корректнее отработает ситуацию когда результат перемножения размера типа данных на количество в size_t не пролазит.

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

Мало того что calloc не вернёт область памяти где ключи лежали

malloc тоже не вернёт ничего такого.

Есть два случая:

  1. malloc возвращает новую память: mmap возвращает обнулённую память

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

Правда говорят, что во встраиваемых линуксах ситуация может быть другой

On embedded Linux, malloc could mmap(MAP_UNINITIALIZED|MAP_ANONYMOUS), which is only enabled for some embedded kernels because it’s insecure on a multi-user system.

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