LINUX.ORG.RU

А потом у вас плазма течет!

Zhbert ★★★★★ ()

Какие могут быть причины НЕ выделения?

Память виртуальная кончилась.

По уму - надо. Для наколенной поделки пофик, тут весь остальной код должен быть дефенсивный и продуманный.

А ещё в си не делают каст аллокации, это пререгатива c++. и на единицу умножать смысла не особо.

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

Очевидно, нет памяти. Другой вопрос, зачем тебе выделять 1 байт? Определи локальную переменную - место под неё выделится в стеке... Или используй alloca

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

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

PPP328 ★★★ ()

Замени все свои малоки на кастомные и проверяй в нем. Не нужны будут миллионы проверок, все пацаны на районе так делают, инфа 100%

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

Влом думать правильный перевод defensive в данном контексе.

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

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

Очевидно, нет памяти

А как может не быть одного байта? Этож чёрт знает что...

зачем тебе выделять 1 байт

Там дальше реалок будет, это пример из libcurl - https://curl.haxx.se/libcurl/c/postinmemory.html

...
mem->memory = realloc(mem->memory, mem->size + realsize + 1);
if(mem->memory == NULL) {
printf("not enough memory (realloc returned NULL)\n");
return 0;
}
...
...
chunk.memory = malloc(1);  
chunk.size = 0; 
...
stD ()
Ответ на: комментарий от stD

Реаллок умеет перевыделять из NULL, так что мимо кассы.

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

Реаллок умеет перевыделять из NULL

То есть ошибки не будет?

...

И всё-таки, как может не быть одного байта?

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

А как может не быть одного байта? Этож чёрт знает что...

Запихаю маллок в цикл и буду аллоцировать террабайты памяти по байтику. Гениально.

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

И всё-таки, как может не быть одного байта?

Память кончилась. Или не кончилась, но её принудительно ограничили каким-нибудь докером. А ещё можно её самому ограничить, man setrlimit.

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

Я только учусь, скажите, что такое - каст аллокации?

stD ()

Ну не проверяй, может дальше пофиг, если на входе 0.

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

Каст это приведение к типу. То есть, по уму твоя аллокация должна выглядеть скорее не как есть, а как-то так:

char *str = malloc(sizeof(char));

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

Я только учусь, скажите, что такое - каст аллокации?

Кигда программа падает из-за одного байта на пользователей кастуется бешенство. В героев не играл штоле?

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

По уму - надо.

Надо, но в онтопике бесполезно, ибо «linux overcommit». Память (вернее, диапазон виртуальных адресов) выделяется (почти) всегда, но при попытке её использовать приложение может внезапно получить сюрприз.

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

Там дальше реалок будет

реаллок и с нулевым указателем хорошо работает, равно как и маллок выделяет блок памяти нулевого размера, поэтому выделять именно 1 байт смысла нет. А вообще, man malloc, там всё написано, даже про оверкоммит.

debugger ★★ ()

тебе null вернут либо если лимиты кончились, либо если у тебя ОС без overcommit.

в остальном, тебе вернут указатель, но при попытке написать в него - может произойти OOM.

Такие дела.

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

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

Надо, но в онтопике бесполезно

Расстрелять тапочком.

anonymous ()

Нет, не надо.

Это совершенно нормально для приложения падать, когда фатальный писец.

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

Тогда позволю себе вольный перевод:упреждающий :-)

Twissel ★★★★★ ()

sizeof(char) == 1. Всегда.

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

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

В оригинале так и было - defensive programming, а не defensive code.

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

Нет, не надо.

И тебя расстрелять тапочком.

anonymous ()
Ответ на: комментарий от fornlr
char *s = malloc(N);
s[N - 1] = 0;

При больших N и доле везения может и не упасть.

А даже то что падает может упасть в совершенно неподходящий момент.

Переопределить malloc, чтобы делал abort при NULL звучит гораздо разумнее.

Падение при NULL это как ремень безопасности. Спасает, но с оговорками. И уж точно врезаться в столб с мыслью «я же пристегнут» не стоит.

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

Куча может полностью израсходоваться, поэтому проверять всегда надо. А вот выделять по одному байту не следует. Операция выделения памяти достаточно дорогая. Кроме того, реально выделяется намного больше байта. Для одного байта проще создать локальную (лучше) или глобальную (хуже) переменную. Это и по расходу памяти будет лучше, и по производительности. И небольшими блоками в несколько байт тоже лучше не выделять. Память может фрагментироваться и её потом может не хватить для чего-то большего просто потому, что не будет одного непрерывного участка памяти достаточной длины, даже если пустых дырок и будет много.

И зачем тема в General, а не в Development?

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

При больших N и доле везения может и не упасть.

А может и при маленьких не упасть. Потому что ub и целевую платформу с компилятором никто не указывал.

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

Операция выделения памяти достаточно дорогая

Это так, но для ядра. malloc запрашивает память блоками и помещает их в пул, из которого уже и отдаёт приложениям. Это далеко не так медленно, как brk(2).

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

Цимес в том, что падение не гарантируется

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

В линаксе же тупо вся система вешаться может с большой вероятностью.

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

Операция выделения памяти достаточно дорогая

Это так, но для ядра. malloc запрашивает память блоками и помещает их в пул, из которого уже и отдаёт приложениям.

Операция динамического выделения произвольного куска памяти в любом случае дорогая. Для ядра она ещё дороже, потому что надо переключать контексты и мэпить память. Для malloc в сравнении с ядром — дёшево. Но не в сравнении со статическим или автоматическим (стековым) буфером. Потому что надо найти свободный блок достаточной длины, пометить его занятым и перестроить список свободных блоков. А при освобождении снова пометить его свободным и опять обновить список. Т. е. операций довольно много в сравнении с фиксированным адресом, где их вообще нет.

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

Каст это приведение к типу. То есть, по уму твоя аллокация должна выглядеть скорее не как есть, а как-то так:

Спасибо.То есть можно упростить до того, как сделано в примере libcurl:

chunk.memory = malloc(1); 

Скажите, почему в си не делают каст аллокации, в плюсах делают?

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

И небольшими блоками в несколько байт тоже лучше не выделять.

Спасибо.

У меня есть программа, в которой несколько функций, в которых (в каждой) происходит выделение (малок) нескольких буферов по 30-100 байт, скажите, как будет лучше, оставить как есть или заменить на статистические?

else if((strstr(buffer, "iwinfoscan")) != NULL) 
     {
       FILE *prog_iwinfoscan, *datasend;
       datasend = fopen("datscan.file", "w");
       if(datasend == NULL) error_log("open datscan.file!");
       prog_iwinfoscan = popen("iwinfo wlan0 scan", "r");
       if(prog_iwinfoscan == NULL) error_log("open prog_iwinfoscan!");

       char *buff = (char*)calloc(80, sizeof(char));
       if(buff == NULL) error_log("memory buff allocated!");
       char *text_buff = (char*)malloc(128 * sizeof(char));
       if(text_buff == NULL) error_log("memory text_buff allocated!");
       char *res_buff = (char*)malloc(64 * sizeof(char));
       if(res_buff == NULL) error_log("memory res_buff allocated!");
stD ()
Ответ на: комментарий от stD

У меня есть программа, в которой несколько функций, в которых (в каждой) происходит выделение (малок) нескольких буферов по 30-100 байт, скажите, как будет лучше, оставить как есть или заменить на статистические?

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

char buf[256];
if(eth0)
{
  char* eth_buf = buf;
  // bla-bla-bla
}
else if(wlan0)
{
  char* wlan_buf = buf;
  // bla-bla-bla
}
else
{
  char* other_buf = buf;
  // bla-bla-bla
}

Если в разных ветках число и/или размеры буферов сильно отличаются, то подход должен быть другим. Но тут тоже могут быть варианты. Например, если время жизни buf, text_buf и res_buf одинаково, то проще выделить и удалить память один раз:

if((buf=malloc(80+128+64))!=0)
{
  text_buf = buf+80;
  res_buf = text_buf+128;
  // bla-bla-bla
  free(buf);
}

Ну а если время жизни сильно разное, то как у тебя.

if(datasend == NULL) error_log(«open datscan.file!»);

В error_log вызывается exit()? Потому что если нет, а в случае ошибок программа продолжает выполняться по той же схеме, что и без них, не упадёт ли она?

malloc(128 * sizeof(char));

Вот на sizeof(char) умножать совсем не нужно, потому что он всегда равен 1. Можно, конечно, но это лишнее. :-)

Скажите, почему в си не делают каст аллокации, в плюсах делают?

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

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

if((buf=malloc(80+128+64))!=0) ...

Большое Вам спасибо. Такая конструкция даже в голову не приходила.

if(datasend == NULL) error_log(«open datscan.file!»);

У меня там хитро сделано, если в конце стоит восклицательный знак, тогда - exit(0);, если точка, то нет.

Ещё раз спасибо за развёрнутый ответ!

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

почему в си не делают каст аллокации, в плюсах делают?

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

В Си++ более строгая проверка типов, и там без каста компилятор ругнётся. Но кастовать тип в Си++ — плохая идея. Там есть new, который возвращает указатель нужного типа.

i-rinat ★★★★★ ()

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

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

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

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

В Си он не нужен, потому что void * неявно кастуется в указатель на любой другой тип

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

У меня «шаблон рвётся»...

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

void * неявно кастуется в указатель на любой другой тип.

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

int main(int argc, char** argv)
{
  int* i=argv[0];
  return *i;
}
$ gcc -o test_cast test_cast.c
test_cast.c: В функции «main»:
test_cast.c:4:10: предупреждение: несовместимый тип указателя в инициализации [-Wincompatible-pointer-types]
   int* i=argv[0];
          ^~~~
$ ./test_cast || echo $?
46

Но зачем так делать, если можно привести явно? Ну написать несколько лишних букв. Зато потом читающему будет понятно, что там за типы, и что это я не случайно по ошибке int-указателю char* присваиваю, а намеренно. Ну и с void* примерно то же самое.

Но кастовать тип в Си++ — плохая идея. Там есть new, который возвращает указатель нужного типа.

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

int* i = new int;
int* array = new int[10];
aureliano15 ()
Ответ на: комментарий от stD

У меня там хитро сделано, если в конце стоит восклицательный знак, тогда - exit(0);, если точка, то нет.

Я бы сделал разные уровни ошибок, типа

enum
{
  MSG_OK=0,
  MSG_WARNING,
  MSG_ERROR,
  MSG_FATAL
} err_level;

if(fignya==NULL)
  error_log(MSG_WARNING, "нехорошо, но ничего страшного");
if(other==NULL)
  error_log(NSG_ERROR, "плохо, устройство other отключено");
if(all=NULL)
  error_log(MSG_FATAL, "а это конец, выходим!");

А функцию error_log реализовал бы как-то так:

void error_log(int level, const char* msg)
{
  switch(level)
  {
    case MSG_OK:
      fprintf(stderr, "%s, Ok\n", msg);
      break;
    case MSG_WARNING:
      fprintf(stderr, "ПРЕДУПРЕЖДЕНИЕ: %s\n", msg);
      break;
    case MSG_ERROR:
      fprintf(stderr, "ОШИБКА: %s\n", msg);
      break;
    default:
      fprintf(stderr, "ФАТАЛЬНАЯ ОШИБКА: %s\nПрограмма завершилась с кодом возврата 1\n", msg);
      exit(1);
  }
}

Оно как бы то же самое, но, имхо, более читабельно получается. Кроме того, потом можно включить фильтрацию и выводить ошибки только начиная с определённого уровня. Например, по опции -q выводить только фатальные ошибки, без опций — фатальные и обычные, с опцией -v — ошибки и предупреждения, а с опцией -vv — вообще всё.

aureliano15 ()

Почему ты создаешь девелоперские темы в general?

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