Почему важно проверять, что вернула функция malloc

    malloc

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

    Примечание. В статье под функцией malloc часто будет подразумеваться, что речь идёт не только именно об этой функции, но и о calloc, realloc, _aligned_malloc, _recalloc, strdup и так далее. Не хочется загромождать текст статьи, постоянно повторяя названия всех этих функций. Общее у них то, что они могут вернуть нулевой указатель.

    malloc


    Если функция malloc не смогла выделить буфер памяти, то она возвращает NULL. Любая нормальная программа должна проверять указатели, которые возвращает функция malloc, и соответствующим образом обрабатывать ситуацию, когда память выделить не получилось.

    К сожалению, многие программисты небрежно относятся к проверке указателей, а иногда сознательно не проверяют, удалось ли выделить память или нет. Их логика следующая:
    Если функция malloc не смогла выделить память, то вряд ли моя программа продолжит функционировать должным образом. Скорее всего, памяти будет не хватать и для других операций, поэтому можно вообще не заморачиваться об ошибках выделения памяти. Первое же обращение к памяти по нулевому указателю приведёт к генерации Structured Exception в Windows, или процесс получит сигнал SIGSEGV, если речь идёт о Unix-подобных системах. В результате программа упадёт, что меня устраивает. Раз нет памяти, то и нечего мучаться. Как вариант, можно перехватить структурное исключение/сигнал и обрабатывать разыменовывания нулевого указателя более централизовано. Это удобнее, чем писать тысячи проверок.
    Я не придумываю, я не раз общался с людьми, которые считают такой подход уместным и сознательно никогда не проверяющих результат, который возвращает функция malloc.

    Кстати, существует ещё одно оправдание разработчиков, почему они не проверяют, что вернула функция malloc. Функция malloc только резервирует память, но вовсе нет гарантии, что хватит физической памяти, когда мы начнём использовать выделенный буфер памяти. Поэтому, раз всё равно гарантии нет, то и проверять не надо. Например, именно так Carsten Haitzler, являющийся одним из разработчиков библиотеки EFL Core, объяснял, почему я насчитал более 500 мест в коде библиотеки, где отсутствуют проверки. Вот его комментарий к статье:
    OK so this is a general acceptance that at least on Linux which was always our primary focus and for a long time was our only target, returns from malloc/calloc/realloc can't be trusted especially for small amounts. Linux overcommits memory by default. That means you get new memory but the kernel has not actually assigned real physical memory pages to it yet. Only virtual space. Not until you touch it. If the kernel cannot service this request your program crashes anyway trying to access memory in what looks like a valid pointer. So all in all the value of checking returns of allocs that are small at least on Linux is low. Sometimes we do it… sometimes not. But the returns cannot be trusted in general UNLESS its for very large amounts of memory and your alloc is never going to be serviced — e.g. your alloc cannot fit in virtual address space at all (happens sometimes on 32bit). Yes overcommit can be tuned but it comes at a cost that most people never want to pay or no one even knows they can tune. Secondly, fi an alloc fails for a small chunk of memory — e.g. a linked list node… realistically if NULL is returned… crashing is about as good as anything you can do. Your memory is so low that you can crash, call abort() like glib does with g_malloc because if you can't allocate 20-40 bytes… your system is going to fall over anyway as you have no working memory left anyway. I'm not talking about tiny embedded systems here, but large machines with virtual memory and a few megabytes of memory etc. which has been our target. I can see why PVS-Studio doesn't like this. Strictly it is actually correct, but in reality code spent on handling this stuff is kind of a waste of code given the reality of the situation. I'll get more into that later.
    Приведённые рассуждения программистов являются неправильными, и ниже я подробно объясню почему. Но прежде надо ответить на вопрос: «а причём здесь Chromium?».

    Chromium


    Chromium здесь при том, что в используемых в нём библиотеках имеется не менее 70 ошибок, связанных с отсутствием проверки после вызова таких функций, как malloc, calloc, realloc. Да, в самом Chromium эти функции почти нигде не используются. В Chromium применяются только контейнеры или operator new. Однако, раз ошибки есть в используемых библиотеках, то значит, можно сказать, что они есть и в Chromium. Конечно, какие-то части библиотек могут не использоваться при работе Chromium, но определять это сложно и ненужно. Всё равно надо править все ошибки.

    Я не буду приводить в статье множество фрагментов кода с ошибками, так как они однотипны. Приведу для примера только одну ошибку, обнаруженную в библиотеке Yasm:

    static SubStr *
    SubStr_new_u(unsigned char *s, unsigned int l)
    {
        SubStr *r = malloc(sizeof(SubStr));
        r->str = (char*)s;
        r->len = l;
        return r;
    }

    Предупреждение PVS-Studio: V522 CWE-690 There might be dereferencing of a potential null pointer 'r'. Check lines: 52, 51. substr.h 52

    В коде нет никакой защиты от нулевого указателя. Другие подобные ошибки из Chromium и используемых библиотек я собрал вместе в файл и выложил их здесь: chromium_malloc.txt. В файле упоминаются 72 ошибки, но на самом деле их может быть больше. Как я писал в вводной статье, я просматривал отчёт только поверхностно.

    Согласно Common Weakness Enumeration обнаруженные ошибки PVS-Studio классифицирует как:

    1. CWE-690: Unchecked Return Value to NULL Pointer Dereference.
    2. CWE-628: Function Call with Incorrectly Specified Arguments.
    3. CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer

    Как видите, даже в таком высококачественном проекте как Chromium, можно заметить массу дефектов, связанных с отсутствием проверок. Теперь я перехожу к самому интересному и расскажу, почему проверки обязательно нужны.

    Почему обязательно нужна проверка


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

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

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

    В разных операционных системах для этих целей резервируется разное количество памяти. При этом в некоторых ОС это значение можно настраивать. Поэтому нет смысла называть какое-то конкретное число зарезервированных байт памяти. Но чтобы как-то сориентировать читателя, скажу, что в Linux системах типовым значением является 64 Кбайт.

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

    Заваривайте кофе, мы начинаем!

    Разыменовывание нулевого указателя — это неопределённое поведение


    С точки зрения языка C и C++ разыменовывание нулевого указателя приводит к неопределенному поведению. Неопределённое поведение — это что угодно. Не думайте, что вы знаете, как будет вести себя программа, если произойдёт разыменовывание nullptr. Современные компиляторы занимаются серьезными оптимизациями, в результате чего бывает невозможно предсказать, как проявит себя та или иная ошибка в коде.

    Неопределённое поведение программы — это очень плохо. Вы не должны допускать его в своём коде.

    Не думайте, что сможете совладать с разыменовыванием нулевого указателя, используя обработчики структурных исключений (SEH в Windows) или сигналы (в UNIX-like системах). Раз разыменовывание нулевого указателя было, то работа программы уже нарушена, и может произойти что угодно. Давайте рассмотрим абстрактный пример, почему нельзя полагаться на SEH-обработчики и т.п.

    size_t *ptr = (size_t *)malloc(sizeof(size_t) * N * 2);
    for (size_t i = 0; i != N; ++i)
    {
      ptr[i] = i;
      ptr[N * 2 - i - 1] = i;
    }

    Этот код заполняет массив от краёв к центру. К центру значения элементов увеличиваются. Это придуманный за 1 минуту пример, поэтому не гадайте, зачем такой массив кому-то нужен. Я и сам не знаю. Мне было важно, чтобы в соседних строках программы происходила запись в начало массива и куда-то в его конец. Такое иногда бывает нужно и в практических задачах, и мы рассмотрим реальный код, когда доберёмся до 4-ой причины.

    Ещё раз внимательно посмотрим на эти две строки:

    ptr[i] = i;
    ptr[N * 2 - i - 1] = i;

    С точки зрения программиста, в начале цикла произойдёт запись в элемент ptr[0], и возникнет структурное исключение/сигнал. Оно будет обработано, и всё будет хорошо.

    Однако компилятор в каких-то целях оптимизации может переставить присваивания местами. Он имеет на это полное право. С точки зрения компилятора, если указатель разыменовывается, то он не может быть равен nullptr. Если указатель нулевой, то это неопределённое поведение, и компилятор не обязан думать о последствиях оптимизации.

    Так вот, компилятор может решить, что в целях оптимизации выгоднее выполнить присваивания так:

    ptr[N * 2 - i - 1] = i;
    ptr[i] = i;

    В результате в начале произойдет запись по адресу ((size_t *)nullptr)[N * 2 — 0 — 1]. Если значение N достаточно велико, то страница защиты в начале памяти будет «перепрыгнута» и значение переменной i может быть записано в какую-то ячейку, доступную для записи. В общем, произойдёт порча каких-то данных.

    И только после этого будет выполнено присваивание по адресу ((size_t *)nullptr)[0]. Операционная система заметит попытку записи в контролируемую ею область и сгенерирует сигнал/исключение.

    Программа может обработать это структурное исключение/сигнал. Но уже поздно. Где-то в памяти есть испорченные данные. Причем непонятно, какие данные испорчены и к каким последствиям это может привести!

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

    Вывод

    Исходите из аксиомы: любое разыменовывание нулевого указателя — это неопределённое поведение программы. Не бывает «безобидного» неопределённого поведения. Любое неопределённое поведение недопустимо.

    Не допускайте разыменовывания указателей, которые вернула функция malloc и её аналоги, без их предварительной проверки. Не полагайтесь на какие-то другие способы перехвата разыменовывания нулевого указателя. Следует использовать только старый добрый оператор if.

    Разыменовывание нулевого указателя — это уязвимость


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

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

    Другие считают, что разыменовывание нулевого указателя приводят к ошибке «отказ в обслуживании» и является уязвимостью. Вместо того, чтобы штатно обработать нехватку памяти, программа, или одна из нитей программы, завершает свою работу. Это может приводить к потере данных, нарушению целостности данных и так далее. Другими словами, CAD система тупо закроется, если не сможет выделить память для какой-то сложной операции, не предложив пользователю даже сохранить результат своей работы.

    Не буду голословным. Есть, такая программа как Ytnef, предназначенная для декодирования TNEF потоков, например, созданных в Outlook. Так вот, разработчики приложения считают отсутствие проверки после вызова calloc уязвимостью CVE-2017-6298.

    Все исправленные места, в которых могло произойти разыменовывание нулевого указателя, имели приблизительно один и тот же вид:

    vl->data = calloc(vl->size, sizeof(WORD));
    temp_word = SwapWord((BYTE*)d, sizeof(WORD));
    memcpy(vl->data, &temp_word, vl->size);

    Выводы

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

    Однако, если вы разрабатываете какую-то библиотеку, то отсутствие проверок недопустимо! Вашей библиотекой могут пользоваться не только ленивые программисты, создающие безответственные приложения, типа игры Тетрис. Надо заботиться и о нормальных программистах, и нормальных программах.

    Поэтому я идеологически не согласен, например, с Carsten Haitzler, что в библиотеке EFL Core нет проверок (подробности в статье). Это не позволяет построить на основе таких библиотек надёжные приложения.

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

    Где гарантии, что будет разыменовывание именно нулевого указателя?


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

    Сейчас я на практических примерах покажу, что я имею в виду. Возьмём, например, код из библиотеки LLVM-subzero, которая используется в Chromium. Если честно, я теряюсь в догадках, какая связь между проектом Chromium и LLVM, но она есть.

    void StringMapImpl::init(unsigned InitSize) {
      assert((InitSize & (InitSize-1)) == 0 &&
             "Init Size must be a power of 2 or zero!");
      NumBuckets = InitSize ? InitSize : 16;
      NumItems = 0;
      NumTombstones = 0;
      
      TheTable = (StringMapEntryBase **)
                 calloc(NumBuckets+1,
                        sizeof(StringMapEntryBase **) + 
                        sizeof(unsigned));
    
      // Allocate one extra bucket, set it to look filled
      // so the iterators stop at end.
      TheTable[NumBuckets] = (StringMapEntryBase*)2;
    }

    Предупреждение PVS-Studio: V522 CWE-690 There might be dereferencing of a potential null pointer 'TheTable'. Check lines: 65, 59. stringmap.cpp 65

    Сразу после выделения буфера памяти происходит запись в ячейку TheTable[NumBuckets]. Если значение переменной NumBuckets достаточно большое, то мы испортим какие-то данные с непредсказуемыми последствиями. После такой порчи вообще нет смысла рассуждать, как будет работать программа. Могут последовать самые неожиданнейшие последствия.

    Аналогичные опасные присваивания я вижу ещё в двух местах этого проекта:

    • V522 CWE-690 There might be dereferencing of a potential null pointer 'Buckets'. Check lines: 219, 217. foldingset.cpp 219
    • V769 CWE-119 The 'NewTableArray' pointer in the 'NewTableArray + NewSize' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. Check lines: 218, 216. stringmap.cpp 218

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

    Продолжу заочную дискуссию с Carsten Haitzler. Он утверждает, что они понимают, что делают, когда не проверяют результат вызова функции malloc. Нет, не понимают. Давайте взглянем, например, на вот такой фрагмент кода из библиотеки EFL:

    static void
    st_collections_group_parts_part_description_filter_data(void)
    {
      ....
       filter->data_count++;
       array = realloc(filter->data,
         sizeof(Edje_Part_Description_Spec_Filter_Data) *
         filter->data_count);
       array[filter->data_count - 1].name = name;
       array[filter->data_count - 1].value = value;
       filter->data = array;
    }

    Предупреждение PVS-Studio: V522 There might be dereferencing of a potential null pointer 'array'. edje_cc_handlers.c 14249

    Примечание. Я использую старые исходники EFL Core Libraries, которые остались у меня со времён написания статьи про эту библиотеку. Поэтому код или номера строк могут уже не соответствовать тому, что есть сейчас. Однако это не важно для повествования.

    Перед нами типовая ситуация: в буфере не хватает свободного места для хранения данных, и его следует увеличить. Для увеличения размера буфера используется функция realloc, которая может вернуть NULL.

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

    array[filter->data_count - 1].name = name;
    array[filter->data_count - 1].value = value;

    Если значение переменной filter->data_count достаточно большое, то значения будут записаны по какому-то непонятному адресу.

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

    Я внимательно не стал изучать старый отчёт, касающийся EFL Core Libraries, но это точно не единственная подобная ошибка. Я заметил как минимум ещё два похожих места, где после realloc данные дописываются по какому-то индексу.

    Вывод

    Я вновь задаю вопрос: «где гарантии, что будет разыменовывание именно нулевого указателя?». Нет такой гарантий. Невозможно, разрабатывая или модифицируя код, помнить про только что рассмотренный нюанс. Запросто можно что-то испортить в памяти, при этом программа продолжит выполняться как ни в чём не бывало.

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

    Где гарантии, что memset заполняет память в прямом порядке?


    Найдётся кто-то, кто скажет что-то подобное:
    Я отлично понимаю про realloc и всё остальное, что написано в статье. Но я профессионал и, выделяя память, сразу заполняю её нулями с помощью memset. Там, где действительно необходимо, я использую проверки. Но лишние проверки после каждого malloc я писать не буду.
    Вообще заполнять память сразу после выделения буфера достаточно странная идея. Странная потому, что есть функция calloc. Тем не менее, так поступают очень часто. Далеко за примером ходить не надо, вот код из библиотеки WebRTC, используемой в Chromium:

    int Resampler::Reset(int inFreq, int outFreq, size_t num_channels) {
      ....
      state1_ = malloc(8 * sizeof(int32_t));
      memset(state1_, 0, 8 * sizeof(int32_t));
      ....
    }

    Выделяется память, затем буфер заполняется нулями. Очень частая практика, хотя, на самом деле, две строчки можно сократить до одной, используя calloc. Но всё это не важно.

    Главное, что даже подобный код не безопасен! Функция memset не обязана начать заполнять память с начала и тем самым вызывать разыменовывание нулевого указателя.

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

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

    Соглашусь, что подобная реализация memset действительно экзотика, и я даже задавал вопрос на StackOverflow на эту тему. В ответе говорится:

    The Linux kernel's memset for the SuperH architecture has this property: link.

    К сожалению, это код на незнакомой мне разновидности ассемблера, поэтому я не берусь рассуждать о нём. Зато ещё есть вот такая интересная реализация на языке Си. Приведу начало это функции:

    void *memset(void *dest, int c, size_t n)
    {
      unsigned char *s = dest;
      size_t k;
      if (!n) return dest;
      s[0] = c;
      s[n-1] = c;
      ....
    }

    Обратите внимание на:

    s[0] = c;
    s[n-1] = c;

    Здесь мы возвращаемся к причине N1 «Разыменовывание нулевого указателя — это неопределённое поведение». Нет гарантии, что компилятор в целях оптимизации не поменяет присваивания местами. Если компилятор это сделает, и аргумент n будет иметь большое значение, то вначале будет испорчен какой-то байт памяти. И только потом произойдёт разыменовывание нулевого указателя.

    Опять не убедительно? Хорошо, а как вам вот такая реализация:

    void *memset(void *dest, int c, size_t n)
    {
      size_t k;
      if (!n) return dest;
      s[0] = s[n-1] = c;
      if (n <= 2) return dest;
      ....
    }

    Вывод

    Нельзя доверять даже функции memset. Да, это во многом искусственная и надуманная проблема. Я просто хотел показать, как много существует нюансов, если не проверять значение указателя. Просто невозможно всё это учесть. Поэтому не надо выпендриваться, а следует аккуратно проверять каждый указатель, который вернула функция malloc и аналогичные ей. И вот именно тогда вы станете профессионалом.

    Заключение


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

    Как видите, анализатор PVS-Studio совсем не зря предупреждает о том, что нет проверки указателя после вызова malloc. Невозможно написать надёжный код, не делая проверки. Особенно это важно и актуально для разработчиков библиотек.

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


    Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Andrey Karpov. Why it is important to check what the malloc function returned.
    PVS-Studio
    832,00
    Static Code Analysis for C, C++, C# and Java
    Поделиться публикацией

    Комментарии 272

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

      Распространенное конструктивное решение (за отсутствие упоминания которого я и ставлю минус) состоит в написании функций-врапперов для выделения памяти. Распространенное название одной из них — xmalloc(). Поведение: вызывает malloc() с тем же параметром, если вернулся NULL — вызывает abort() для явного аварийного завершения программы, если не NULL — возвращает указатель на выделенную память. За xmalloc() проверять не надо.

      Понимаю, что решение не универсальное, и что за него в некоторых случаях тоже ругают.
        +9
        А что мешает везде проверять и валиться с «Out of memory», вместо непонятных segmentation faults? Одно дело я вижу проблему, а другое — внезапный crash, да и ещё, как я понимаю, может мне затереть память в неизвестном месте.

        Поправьте меня, пожалуйста, если я не прав.
          +14
          вызывает malloc() с тем же параметром, если вернулся NULL — вызывает abort()
          Ради бога. Это как раз и есть та самая проверка, на которой я настаиваю и реакция на неё. Как решить проблему — это вторично. Главное начать её решать, а не надеяться, что если ничего не делать, то так всё само-собой хорошо будет ;).
            +2
            Но и обрабатывать по-умному ошибку, когда malloc вернул NULL, тоже нельзя, поскольку этот случай толком невозможно протестировать.

            Очень даже можно. На Linux'е есть LD_PRELOAD, на macOS DYLD_INSERT_LIBRARIES. Пишем библиотеку которая оборачивает системный malloc. В своей обёртке над malloc'ом вы вольны возвращать NULL когда вам захочется.


            Для Windows решение тоже имеется, но реализуется намного сложнее чем на NIX'ах.


            Правда вынужден сказать, что заваливание malloc'а достойно переживает только Linux'вый рантайм. На macOS даже printf падает при невозможности выделить память. Эти неприятные мелочи приходится учитывать при тестировании.

              +4
              Легко сказать «пишем библиотеку». Вопрос в том, как обвалить конкретно этот вызов malloc(), а не какой-то другой, чтобы протестировать код, обрабатывающий именно этот NULL, а не NULL где-то еще.
                +1
                В gcc можно смотреть на __builtin_return_address (https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html), а из него уже понять имя функции/строку (если собраны с дебагом)
                  +1
                  Иногда прерывать нельзя, но все же: поставить под отладчиком остановку по условию и поменять результат на NULL чем не нравится?
              –5
              Очень много воды и ни одного довода по существу. Если malloc/new вернула null, то лучше падать сразу, чем писать дикие ветвления кода и всё равно падать в конце концов, когда не смогли отследить не корректность данных, так как проверить как поведёт себя вся прога во всех случаях при new==null просто не возможно.
              P.S. Для надёжности в серьёзных проектах делают свой new/malloс/..., что бы он сразу валил прогу во внешний try/catch, и весь головняк этой проверки снимается. Странно если в хроме этого нет. Точно нет (не смотрел)?
                +11
                Вы неправы. Так делать неправильно.
                Простейший пример: из-за ошибки программиста программа просить выделить ОЧЕНЬ большой буфер, которого нет.
                При правильной обработке ошибок мы просто выйдем с ошибкой из функции, показав пользователю понятное сообщение и дав ему сохранить имеющуюся работу. А возможно и продолжить работу нормально, просто какая-то часть функционала будет недоступна.
                Пример простой, но если проверять КАЖДОЕ выделение памяти (и освоождать все выделенное даже при пробросе исключения), то программа станет гораздо надежнее.
                  0
                  Так для этого случая как раз и нужен try/catch. Если какое-то большое действие внутри завершается неудачей, программа работает дальше, если может. При правильной архитектуре в следующем try/catch даже память выделенную внутри этого действия пытается чистить за собой. А потом и выводит пользователю какое-то сообщение если может/надо.
                  Весь код надо тестировать. И если раздувать его подобными ветвлениями, которые ни когда не должны исполняться — это усложняет всё очень сильно там, где ни какого смысла усложнять нет.
                    +1
                    1. try/catch в С нет
                    2. использовать malloc в C++ довольно странная затея
                    3. стандартный new (насколько мне известно) никогда nullptr не возвращает.
                      0
                      В C есть для этого goto и setjmp/longjmp
                      2 Да странно, но ручки у malloc которая затронет все приложение есть (mallopt) В C++ такои случае нужно либо везде пихать свои аллокаторы либо дергать тот же mallopt надеясь что все юзают стандартные аллокаторы с new который свалится в тот же malloc. А так да malloc в C++ выглядит чужеродно
                      3) Возвращал но до офф версии стандарта 98 года
                        0
                        Есть new (std::nothrow), которая возвращает nullptr. А без дополнительных аргументов new бросит исключение std::bad_alloc.
                    +7
                    Очень много воды и ни одного довода по существу. Если malloc/new вернула null, то лучше падать сразу,


                    Так против этого автор статьи ничего и не говорит. Нужно падать сразу. Но явно, сделав проверку на Null.

                    во всех случаях при new==null

                    Здесь чистый косяк компилятора — new (без опции std::nothrow) обязан либо вернуть не null, либо выбросить исключение.
                      0
                      Это не косяк компилятора, просто в статье описывается именно поведение malloc. Насколько я понимаю, при выделении памяти оператором new такой проблемы не возникнет
                      0
                      Да, прекрасное решение, особенно для встраиваемых непрерывных систем. Хотели бы Вы полетать на самолете запрограммированном таким образом? или лифту хотя бы поездить?
                        +3
                        А не надо для встраиваемых систем динамически выделять память. Только преаллокация при компиляции.
                          0
                          Слышал интересное утверждение (там, кажется, ссылались конкретно на правила Сименса), что у самолетов в бортовом оборудовании вся память должна быть выделена до взлета.
                            0
                            Так и есть. То же самое относится и к космической технике. И вообще к любой встраиваемой.
                            0
                            До этой глубокой мысли авторы FreeRTOS дошли в позапрошлом году. До 9-й версии все объекты freertos'а (задачи, очереди, семафоры и т.д.) выделялись malloc'ом. Сейчас хотя бы выбор появился…

                            Правда, справедливости ради, можно спроектировать систему так, чтобы маллок вызывался только при старте, а дальше всё работало «в статике».
                              0

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

                            +2
                            поэтому для встраиваемых систем вообще malloc использовать не рекомендуется.
                            чего там лифту выделять динамически? можно эти 3 байта статически глобально объявить.
                              0
                              Подскажите стандарт на счет использовать не рекомендуется, мне на ум только Misra приходит. Тут возникает вопрос в другом, где взять программиста который будет делать без malloc в другом стиле мышления, когда все кругом привыкли писать уже и с автоматическим сборщиком мусора.
                                +1
                                я мисру и имел в виду
                                предполагается, что во встраиваемых системах не будут поступать неизвестные объемы данных, а место под известные объемы можно выделить вручную заранее.
                                для самолета подход вряд ли прокатит — там скорее всего используется куча готовых библиотек, а для лифта — запросто, и программист скорее всего даже не задумываясь не станет вызывать malloc для хранения массива нажатых кнопок итп, а просто объявит его с известным размером.
                                  0
                                  у нас в программе при старте используется malloc для создания обьектов (потому что удобнее это делать в унифицированном виде, к примеру, массива обьектов разнообразных фильтров разной длинны), но созданные обьекты НИКОГДА до перезагрузки не уничтожаются…
                                    0
                                    Поздравляю: вы изобрели JavaCard — великую версию Java без GC.

                                    У них, правда, круче: GC нет, память можно только выделить, но не освободить… и никакой перезагрузки тоже нет — если хотите память освободить извольте приложение (вернее «кардлет») снести, а потом поставить заново.

                                    Зато Java…
                                      0
                                      Такого редкого гуся в нашем процессоре не задействовать, ни одного компилятора стороннего нет, только проприетарный от производителя. Поэтому вертимся как можем.
                                  +3
                                  Тут возникает вопрос в другом, где взять программиста который будет делать без malloc в другом стиле мышления


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

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

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

                                  Если устройство имеет очень серьезную программную часть (насыщенный интерфейс пользователя, содержательные вычисления и т.п.), то обычно разработчик(и) железа оборачивают взаимодействие с аппаратурой в API, учитывающий все нюансы, и уже его отдают программистам.
                            +3
                            Получается причина одна, просто приведены разные сценарии ее проявления. Но в любом случае Вы правы.
                            Если честно, я теряюсь в догадках, какая связь между проектом Chromium и LLVM, но она есть

                            Наверное генерят нативный код для исполнения JavaScript виртуальной машиной.
                              +2
                              Для WebAssembly наверное используется. Да и шейдеры можно им компилировать.
                                +2
                                Subzero — Fast code generator for PNaCl bitcode
                                +1

                                Возможно я отстал от жизни и что-то изменилось (прошло уже лет 10, как я проверял это поведение, но поскольку на тот момент это была "не бага, а фича", то...), но чтобы получить NULL от malloc под Линуксом, нужно еще постараться — скорее система сама прибьёт процесс при попытке выделить память. Вроде можно повозиться с настройкой каких-то лимитов (я виндузятник, под линуксом только время от времени), но с настройками по умолчанию по исчерпанию памяти система просто прибивала процесс. Т.е. до проверки на NULL дело не доходило, хотя она и была.

                                  0
                                  … но чтобы получить NULL от malloc под Линуксом, нужно еще постараться — скорее система сама прибьёт процесс при попытке выделить память

                                  Сейчас, допустим, трудно «получить NULL от malloc под Линуксом». Где гарантия, что через год будет также? В статье как раз и говорится, что это неопределённое поведение и не нужно до него доводить.
                                    0

                                    Я про то, что можно городить хитрые проверки, загромождая и запутывая код, а это все будет совершенно без толку, потому что на клиентской машине настройки по умолчанию и проверки просто не сработают. Кстати, вспомнилось, что линукс процесс убьет даже не при выделении памяти, а только при попытке обращения к ней. Привет проверкам! :)


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


                                    Речь, конечно, не идет и микроконтроллерах и т.п. системах с жесткими ограничениями на размер памяти.

                                      +3
                                      Я про то, что можно городить хитрые проверки, загромождая и запутывая код,


                                      В статье речь про ровно одну проверку. Которую можно убрать в свою обёртку на malloc'ами.

                                      Без этой проверки — неприятное UB, за которое цепляется глаз при чтении кода.

                                      Кстати, вспомнилось, что линукс процесс убьет даже не при выделении памяти, а только при попытке обращения к ней. Привет проверкам! :)


                                      Убьёт — так убьёт. Нам в общем-то от проверки это и нужно — проверить на null и умереть. Не побив по дороге данные.

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

                                      В таких случаях сам факт исчерпания памяти — уже неопределенное поведение.


                                      На всякий случай — словосочетание «неопределённое поведение» в статье автора и словосочетание «неопределённое поведение» в вашем комментарии обозначают разные вещи.
                                      В частности, ваше «неопределённое поведение» не является UB. Оно вообще в рантайме происходит. В статье же — речь об UB, наличие которого влияет на то, какой код из вашего исходника выдаст компилятор.

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


                                      Premature optimization.
                                      К слову, допустим, эта самая оптимизация вам прямо-таки нужна.
                                      Ну то есть, давайте я возьму и поверю, что убирание одной проверочки на null в вызовах malloc'а в местах, где большие регионы памяти не выделяются, делает вам погоду. Что ж, время задуматься о применении (или даже написании) кастомного алокатора.

                                      Речь, конечно, не идет и микроконтроллерах и т.п. системах с жесткими ограничениями на размер памяти.


                                      Речь идёт о коде кроссплатформенных библиотек. Сегодня Вася её в код браузера впихивает под какой-нибудь десктопный линукс или винду, а завтра та же самая библиотека будет пересобрана под minix в Intel ME на ограниченном количестве памяти хрен знает как замапаном. Это нормальное использование Open Source библиотек. И это нормально такие проверочки делать.
                                        –3

                                        Под линуксом (без специальных настроек системы) malloc вернет не NULL, даже если память закончилась. И программа упадет уже при попытке обращения к этой памяти. Т.е. проверка на NULL не имеет смысла. Ну да не линуксом единым...


                                        Под виндой ситуация лучше — malloc вернет NULL, если память выделить не удалось. Но что с этим знанием делать? Если просто молча аварийно завершить выполнение, то разыменование нулевого указателя сделает это за вас (да, иногда адресная арифметика может преподнести сюрпризы, но когда речь идет о достаточно больших регионах памяти, проверка результата malloc даже под линуксом не повредит). Если же пытаться как-то обрабатывать эту ситуацию, то обработка ошибки выделения памяти тоже может потребовать немало памяти. Это в эпоху MSDOS можно было вывести строку на экран, вызвав прерывание BIOS, до байта понимая, сколько памяти потребуется и как ее зарезервировать. В современных системах это уже гораздо сложнее. А привести к отказу выделения памяти может запрос даже одного байта.


                                        С библиотеками ситуация немного проще — им обычно не требуется обработка ошибки. Достаточно просто установить как-то ее признак и завершить выполнение текущей библиотечной функции (тоже может оказаться нетривиально).


                                        Что касается обертки вокруг malloc… Если бы это был выход, библиотечный malloc вызывал бы exit вместо возвращения NULL. Если обертка вокруг malloc будет всегда убивать процесс при получении любого нулевого указателя, вы лишите программу возможности обрабатывать ситуации, когда есть шанс обработать эту ошибку более корректно (выдав пользователю сообщение, что, к сожалению, его гигабайтный файл не удалось загрузить в память). Если не всегда, то обертка теряет смысл.


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


                                        В общем, я полностью согласен с автором, что ошибка выделения памяти — очень серьезная ошибка. Одна из самых серьезных. Но вот только корректных способов ее обработки нет, за редким исключением.

                                          +2
                                          Если же пытаться как-то обрабатывать эту ситуацию, то обработка ошибки выделения памяти тоже может потребовать немало памяти.


                                          . Это в эпоху MSDOS можно было вывести строку на экран, вызвав прерывание BIOS, до байта понимая, сколько памяти потребуется и как ее зарезервировать. В современных системах это уже гораздо сложнее. А привести к отказу выделения памяти может запрос даже одного байта.


                                          Позвольте полюбопытствовать.

                                          Вот вы видите, что я написал следующее:
                                          Убьёт — так убьёт. Нам в общем-то от проверки это и нужно — проверить на null и умереть. Не побив по дороге данные.


                                          Так же вы видите слова автора статьи в соседней ветке:
                                          вызывает malloc() с тем же параметром, если вернулся NULL — вызывает abort()

                                          Ради бога. Это как раз и есть та самая проверка, на которой я настаиваю и реакция на неё.


                                          Зачем же вы (и другие комментаторы) продолжаете писать большие и длинные аргументы про то, что содержательную обработку такой ошибки сделать не получится, если очевидно, что абсолютно никто о ней не говорил ни в посте, ни в комментариях?

                                          Вода — мокрая.
                                          Содержательная обработка в такой ситуации — не нужна.

                                          Ни с этим, ни с другим вроде бы никто не спорит, не?

                                          Под линуксом (без специальных настроек системы) malloc вернет не NULL, даже если память закончилась. И программа упадет уже при попытке обращения к этой памяти. Т.е. проверка на NULL не имеет смысла. Ну да не линуксом единым...


                                          Да, под линуксом при нехватки памяти malloc скорее всего не вернёт null — это истинное утверждение.

                                          Под линуксом (без специальных настроек системы) malloc вернет не NULL


                                          Нет, ситуации, когда malloc вернёт null под линуксом возможны.

                                          Опять же, причём здесь линукс? Речь идёт о библиотеке вроде как.

                                          Если не всегда, то обертка теряет смысл.

                                          Сформулировали проблему — проверка на null раздражает вас тем, что часто приходится смотреть на аж целую строчку проверки.
                                          Обёртка malloc_or_die решает эту проблему — не придётся.

                                          По мне проблема — дурацкая.
                                            0

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


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

                                              +3
                                              Видимо, у меня есть сомнения в том, что прибивать процесс при любом неудачном вызове malloc является хорошей стратегией.


                                              Тогда не прибивайте.

                                              Суть статьи: «Смотрите, вот в этом месте люди надеются, что приложение прибьётся при обращении по нулевому указателю, а на самом деле здесь UB. Вот такое бывает, да. Оно скорее всего прибьётся, но по дороге могут побочные эффекты возникнуть. А если звёзды крайне удачно сложатся, то вот это UB в какой-нибудь левой библиотеке вдруг возьмёт и выстрелит как уязвимость в вашем приложении, но это маловероятно.»

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

                                              Вот вы сначала говорите «Ну так пусть приложение возьмёт и умрёт! Не надо обрабатывать ошибку! У вас и не получится её обработать нормально — памяти-то нет!»

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

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


                                              Теперь вы говорите нечто противоположное.

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

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


                                              Стоит читать хотя бы те комментарии, на которые вы отвечаете.
                                                +1
                                                пардон, отвлёкся и не успел поправить комментарий в установленный срок, чтобы это считалось редактированием.
                                                И в этом случае (когда вы всё-таки хотите уйти в умный обработчик)

                                                Имелось в виду:
                                                И в этом случае (когда вы всё-таки хотите уйти в умный обработчик), проверка на null вам тем более не будет лишней. Вам, более того, скорее всего даже захочется ещё дополнительно проверить и убедиться на месте, что система память сможет выделить. (При помощи memset_s'а, к примеру).
                                    +1
                                    Сейчас да — происходит page fault, памяти для страницы физической нет -> процесс умирает. А указатель malloc чаще всего выдаёт нормальный.

                                    Это всё при включённом overcommit. Если порог понизить или выключить совсем — можно получить NULL.

                                    Итого мораль простая — если хочется, чтобы программа корректно без сюрпризов работала и сегодня при любых настройках и в будущем — проверка нужна. Если целевая платформа это Linux с настройками по умолчанию: в принципе не то чтобы и нужна.
                                      +5
                                      Если целевая платформа это Linux с настройками по умолчанию: в принципе не то чтобы и нужна.
                                      Хочу заметить, что такое недопустимо для библиотек. Библиотека не знает, где и как её будут использовать. Например, тот-же Chromium, использующий кучу библиотек, работает в Windows, где malloc/realloc вполне может дать нулевой указатель, если запросили много или если память приложения сильно фрагментирована.
                                        +2
                                        Согласен. От библиотек всегда хочется стабильности и предсказуемости в самых разных ситуациях, часть из которых даже в голову разработчиками не могла прийти на момент написания кода.
                                      +2
                                      Нет ни чего сложного. У меня несколько раз получалось, когда из-за ошибки я просил выделить слишком много памяти, в результате malloc возвращал NULL. Я так как я проверяю, что вернула функция, то я просто получал в логе ошибку, что не удалось выделить память…
                                        0

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

                                          0
                                          Зачем? Я просто написал, что это сделать не так сложно… По моему опыту ошибки с выделением памяти были связаны только с ошибками в коде, мне ни когда не требовалось очень много памяти… А ошибку я вспомнил, она была простая: увеличиваем пул памяти в полтора раза когда не хватает, при этом забываем увеличить счётчик выделенной памяти и очень быстро получаем ошибку :)
                                        0
                                        На 32-битной системе (любой) получилить очень просто — размер адресного пространства 3 Gb, в которые еще и спроэцирована куча библиотек и которое еще и фрагментировано. 2-2.5 Gb выделили — и всё nullptr.
                                        +2
                                        Самый очевидный пример: базы данных. Неожиданное падение на транзакции, да еще с порчей неизвестных данных в разделяемой памяти. Пользователь будет рад, что в его базе появилась крутая библиотечка, которая круто парсит json или еще чего.
                                          0
                                          Кому-то очевидно, что падать при первом поводе это плохая идея, а кому-то нет. :) Вот на реддите, например, статью и заминусовали и закритиковали.
                                            +2
                                            FailFast — прекрасный паттерн. Но это не значит, что его нужно применять бездумно.
                                            Если падение в определенный момент приводит к порче данных — от этого нужно защищаться, это очевидно и это не противоречит Fail Fast.
                                              +4
                                              По опыту обсуждения подобной темы складывается ощущение, что в Интернетах есть две секты. Приверженцы первой свято уверены в том, что под Linux-ом malloc никогда не возвращает NULL. Приверженцы второй свято уверены в том, что если память в программе выделить не удалось, но ничего уже в принципе сделать нельзя, нужно только падать.

                                              Переубедить их никак нельзя. Особенно когда эти две секты пересекаются. Можно только принять это как данность. Причем не суть важно, reddit это, Хабр, LOR или еще какой профильный ресурс.
                                                –1
                                                Это бездумная попытка доказать свою правоту, при этом все попытки — суть подлоги. Я ведь знаю, кого ты цитируешь и что именно тот, кого ты цитируешь говорил. Ведь говорил это я. И никаких «никогда» не было. Был описан набор ограничений, в раках которых malloc() вернуть NULL не может. Это объективный факт.

                                                Ты можешь рассуждать о чём угодно, но это не изменит этого факты. Всё что ты можешь — это апеллировать к «на это нельзя рассчитывать», но тогда ты уже не расскажешь про секту.
                                                  –2
                                                  Ой, да это же LOR-овский Царь-Сишки-Балабол сюда приплыл.
                                                    +1

                                                    Эти ограничения находятся под контролем разработчика? Или это ограничения на среду исполнения, повлиять на которые разработчик может, максимум, проверяя среду при старте программы и в случае несоответствия умирая с сообщение типа "программа будет работать только с такими-то настройками ядра"? Если второе, то как часто вы видели такие проверки в реальном коде?

                                                      –2
                                                      Эти ограничения находятся под контролем разработчика?

                                                      Да.

                                                      Или это ограничения на среду исполнения

                                                      Это манёвры, а не конструктив. Проверяете ли вы среду на наличие необходимых ресурсов( той же памяти) — нет. Вас это так же интересует и в контексте динамический аллоцируемых массивов/строк в тех же крестах. Они разбрасываются ВМ( кстати, раз начали говорить о ВМ, типичный срыватель покровов и его представления о матчасти продемонстрировано тут) на право и налево, и при изменении той ручки — получаешь в среднем увеличение потребляемой векторами/строками памяти на 25% даже на больших векторах/строках. Это кого-то волнует?

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

                                                      Точно так же, на аллокациях меньше страницы шанс не получить NULL стремиться к 100%, как и шанс словить ООМ. С любыми ручками. Обходится это только лимитами, но в рамках вашей же логики — вы не можете рассчитывать на среду исполнения, т.е. это не работает.

                                                      В этом заключается проблемы. Мы рассуждаем об обработки NULL как о том, что даёт какие-то гарантии. Пример статьи выше. Нам там вещают о надёжных приложениях, хотя на самом деле это враньё. Авторы сих утверждение живут в своих фэнтезийных мирах, где проверка на NULL делает их программу надёжной — нет. В реальном мире всё не так. И авторы подобных утверждений наоборот, создают ненадёжные программы.

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

                                                      И всё было бы хорошо, если бы нам рассказали как именно добиться проверки «есть память, либо нет», но нам дали какую-то соломинку, которая что-то гарантирует автору/нам лишь по причине незнания, либо умалчивания деталей.

                                                      И именно поэтому, когда программист знает — что может вернуть NULL, а что не может. Он сможет проверить — есть ли у него память, либо нет( если это действительно важно для корректной работы приложения), а вот вот человек, уверенный в том, что NULL его спасёт — нет. И он повесит систему, либо словит sigkill.

                                                      Если второе, то как часто вы видели такие проверки в реальном коде?

                                                      Дело тут не совсем в проверках. Есть механизмы, которые 100% дадут NULL( на самом деле никакие NULL'ы там не используются) и это не malloc(). В частности mmap() + MAP_POPULATE.

                                                      Откуда взялись эти 64кб из статьи? Мне неведомо. Подобного я никогда не видел.

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

                                                      Это то же подлог. Мы мало того, что считаем оппонентов идиотами, да ещё к тому же — подбиваем результаты под желаемое. В большинстве случаев она не закроется, а словит sigkill, либо повесит систему и sigkill словит кто-то ещё. И произойти это можно в любой момент в процессе операции.

                                                      Третий пример вообще паста с первого. Зачем это?

                                                      Перед нами типовая ситуация: в буфере не хватает свободного места для хранения данных, и его следует увеличить. Для увеличения размера буфера используется функция realloc, которая может вернуть NULL.

                                                      Здесь явный подлог. С чего автор взял, что его рассуждения про «может вернуть NULL» имеют какое-то отношения к реальности? Он живёт в своём мире и натягивает его свойства на реальность.

                                                      Вообще заполнять память сразу после выделения буфера достаточно странная идея. Странная потому, что есть функция calloc.


                                                      Это так же говорит о том, что представления автора слабо коррелируют с реальностью. Я советую погуглить автору по ключевому слову «префолт», а потом уже о чём-то рассуждать.

                                                      Невозможно написать надёжный код, не делая проверки.

                                                      Чистый маркетинг и ничего более. Подобные заявления ортогональны понятию надёжности, т.к. внушают у читателей ложную уверенность — давай ложную ассоциацию «есть проверка — надёжно», что естественно реальности не соответствует.

                                                      В конечном итоге — что мы имеем? Все подобные вопросы являются манипуляциями, а именно — мы определяем ложное равенство «проверка NULL == безопасно», а далее на все ответы оппонентов апеллируем к тому, что «но у тебя же не всегда так( на самом деле всегда, ведь всегда это комплексный подход. И он не состоит только из игнорирования результата маллока)», но это не имеет смысла. Ведь на самом деле первое равенство ложно, а раз оно ложно, то и то работает «не всегда», а раз обе стороны работают «не всегда», то апелляция к «не всегда» попросту не имеет смысла.

                                                        +2
                                                        проверка NULL == безопасно

                                                        Не передергивайте. Проверка NULL закрывает лишь один вектор атаки или сбоя, она увеличивает безопасность, но не гарантирует её.


                                                        Если кто-то крутит ручки, которые крутить не надо — это его проблема.

                                                        Если ручки есть, то они сделаны, чтобы их крутить. В каких ситуациях и как крутить отдельный разговор.


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

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


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

                                                          –2
                                                          Не передергивайте. Проверка NULL закрывает лишь один вектор атаки или сбоя, она увеличивает безопасность, но не гарантирует её.

                                                          Это ваши фантазии, которые нигде в статье не определены. В статье используются явные противопоставления «небезопасного кода»(без проверок) коду с проверками, т.е. безопасному. Это первое.

                                                          Второе — автор не просто не упоминает «закрывает какой-то там неведомый вектор», а попросту не знает ничего в данной теме, но пытается обвинять в этом других. Банальный пример с мемсетом. Автор явно не знаком с базовыми понятиями.

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

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

                                                          Дак вот, проверяя — вы теряете «я знаю что делаю», а значит теряете хоть какую-то возможность создать хоть что-то безопасное. Вы не получаете преимуществ — вы получаете дыры.

                                                          Если ещё проще. Вы противопоставляете «проверке» импорт гарантий снизу, а значит — вы никогда и никаких гарантий не получите, а вот ваш оппонент получит.

                                                          Если ручки есть, то они сделаны, чтобы их крутить. В каких ситуациях и как крутить отдельный разговор.

                                                          Очень глупо. Stack limit нигде не определён, а такая ручка есть. Почему ваша программа не работает, ведь ручки созданы для того, чтобы их крутить?

                                                          И да, я вам открою тайну. В ядре нет ручек. В ядре есть кастыль, который специально сделан для того, чтобы эмулировать подобное поведение. Т.е. это именно ваша проверка работает только тогда, когда включен специальный флаг в окружении( и то на самом деле нет).

                                                          Это ваш подход напрямую зависит от ручек.

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

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

                                                          Если я могу гарантировать, что в случае отсутствия проверки — NULL у меня не будет, то и неопределённого поведения у меня не будет.
                                                    0
                                                    Есть третья группа, но они грамотно прячутся.
                                                +1
                                                Нарисовали бы единорога с циркуляркой на КДПВ, выбивается из фирменного стиля )

                                                Можно не минусить, проверки на NULL я делаю
                                                  +1
                                                  Если не ошибаюсь, то обход массива от последнего к первому чуточку эффективнее (лет 10 назад пытался изучать ассемблер, декремент указателя эффективнее работает, кажется), так что проблема весьма реальна. Не мемсетом едины.)
                                                    0

                                                    Основная проблема в скорости памяти.
                                                    Когда процессоры были маленькими, выигрыш от Loop был заметен.
                                                    Сейчас потеряется больше времени на загрузке данных.
                                                    А при прямом чтении она выполнится спекулятивно.

                                                      0
                                                      Она и при обратном чтении спекулятивно загружаться будет. Никакой разницы.
                                                    +2

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

                                                      0
                                                      О том же подумал, когда статью читал. Всё надо либо проверять, либо заранее «договариваться» о том, чего быть «не должно», потому что это уже было обязательно проверено выше.
                                                        0
                                                        С другой стороны представьте себе объект, который выделяет память. Тут можно проверить результат — а выделилась ли память на самом деле. Действительный указатель на выделенную память запоминает в переменной, члене класса объекта. Далее объект вызывает другие функции других классов и передает им параметром этот указатель.
                                                        Нужно ли во всех функциях принимающих параметр указатель проверять его на ноль?
                                                        Не потеряется ли эффективность программы если по сто раз проверять, то что один раз должно быть проверенно?

                                                        Или вот, например:

                                                        void draw_pixel(char* framebuffer, int x, int y)
                                                        {
                                                        //SHOULD I DO THAT?
                                                        if(framebuffer==nullptr) return;

                                                        //draw pixel actually
                                                        ..................
                                                        }

                                                        void draw_line(char* framebuffer, int x1, int y1, int x2, int y2)
                                                        {
                                                        //SHOULD I DO THAT?
                                                        if(framebuffer==nullptr) return;
                                                        for( ..N.. ) {
                                                        draw_pixel(framebuffer,x,y);
                                                        }
                                                        }

                                                        main()
                                                        {
                                                        char* screen = new char[width*height];
                                                        //actual check for null
                                                        if(screen==nullptr) { cout << OOPS; return -1; }
                                                        draw_line(screen, 100,50,500,800);
                                                        }

                                                          +1
                                                          char* screen = new char[width*height];
                                                          //actual check for null
                                                          if(screen==nullptr) { cout << OOPS; return -1; }


                                                          new не вернёт null.
                                                          А оптимизатор. к слову, все проверки на null в этом примере, вырежет (хотя закладываться на это не стоит и писать их в данной ситуации как минимум странно).
                                                            0
                                                            Ну окей, ошибся, быстро писал.
                                                            Например, там malloc.
                                                            Вопрос по существу — нужно ли проверять везде на nullptr или нет.
                                                              0
                                                              Например, там malloc.
                                                              Вопрос по существу — нужно ли проверять везде на nullptr или нет.


                                                              Теперь представьте, что в проверках этих внутри методов не «return;», а какая-то содержательная фигня. В логи написать «ФРЕЙМБУФЕР УПАЛ», например. Байтик переслать на отладочный девайс. Ещё что-то.
                                                              А ещё представьте, что «if(screen==nullptr) { cout << OOPS; return -1; }» в main'е вы не написали.
                                                              И перед вызовом draw_line умудрились разыменовывать screen и не упасть.

                                                              Отправится ли теперь заветный байтик? Найдём ли мы теперь «ФРЕЙМБУФЕР УПАЛ» в логе? Теперь это загадка.

                                                              Вопрос по существу — нужно ли проверять везде на nullptr или нет


                                                              Если нужно — проверяйте, не нужно — не проверяйте. Вопрос проектирования API и оптимизации бутылочных горлышек в вашем коде.
                                                              Может быть эти проверки погоды вам не делают.
                                                              А может быть половину этих методов надо заменить на ассемблерные вставки с огромным комментарием «НА ВХОД ОЖИДАЕТСЯ ВОТ ЭТО И ВОТ ТО — КОД НЕ ТРОГАТЬ!1111».

                                                              Если эти методы — не нечто торчащее наружу — зачем вам вообще чистый указатель, собственно, принимать?
                                                              Если эти методы — нечто торчащее наружу, очевидно, что стоит написать в документации к API, чего вы ждёте и действовать соответственно.
                                                                –2
                                                                В принципе, это я и ожидал прочитать в ответ.
                                                                Иными словами, не существует обязательного правила всегда проверять указатель на ноль. Иногда такая проверка не лишнее, а иногда очень даже и вредная.
                                                                Все по обстоятельствам.
                                                                  +2
                                                                  не существует обязательного правила


                                                                  Если очень хочется, можно даже на ноль делить. Только т-ссс, никому!
                                                            0

                                                            Закон Мерфи. Если программа может вылететь из-за того, что в какую-то функцию на 115м уровне вложенности, какой-то Вася Пупкин передал нулевую ссылку, то она обязательно вылетит.
                                                            Следствие: Вылетит она в тот момент, когда программа будет управлять взлетом ракеты с комсмодрома Восточный
                                                            Следствие: Если суммарная экономия на том, что не надо выполнять проверку на NULL, составляет 3 секунды процессорного времени за 10 лет непрерывной работы программы, то стоимость поиска ошибки будет астрономической
                                                            Следствие: Если программа полагается на системные средства Линух (например), чтобы генерировать сообщения об ошибке, то код этой программы обязательно будет перенесен в Виндоуз/Мак/Ардуино, где эта фича работает по другому и программа навернется


                                                            Вывод: Лучше ВСЕГДА перебздеть, чем недобздеть.

                                                              0
                                                              Странно, что когда я привожу конкретный пример про рисование линии с помощью функции рисования пикселей мне приводят контр доводы про полет спутника.
                                                              Несопоставимые вещи.
                                                              В конкретно моем примере проверка на ноль в функции рисования пикселя может значительно сказаться на быстродействии программы, так как эта проверка будет вызываться миллионы раз в графической программе, а может быть вызвана всего один раз при инициализации графической системы.
                                                              Гораздо разумнее задокументировать АПИ и описать требования к параметрам, чем проверять «везде» и «всегда».
                                                              Давайте я другой пример приведу.
                                                              Представьте, что вы пишите программу майнинга криптовалюты и ваш код на OpenCL будет работать на тысяче процессоров в видеокарте. Вы передаете в процедуру параметры и указатели на блоки памяти. Будете всегда проверять указатели на ноль? Или все же не станете этого делать? Ведь от быстродействия кода зависит прибыль майнинга…
                                                                +2
                                                                Вот именно поэтому и надо проверять в том месте где память выделяется, а не там где она используется.
                                                          –2

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

                                                            –1
                                                            Лично меня больше задела мысль о том, что программа может обратиться к чужой памяти и испортить ее. Когда-то давно, когда я прочитал про 386 процессор и его защищенный режим, я понял, что нормально написанная ОС не дает процессу возможности записи туда, куда не положено.
                                                            Здесь же утверждается, что ОС с ядром Линукс защищают от порчи данных только первые 64Кб некоторого адресного пространства. Но если это так, не значит ли это, что в этих ОС большие проблемы с безопасностью?
                                                            И если это — общеизвестный факт, то почему с этим ничего не делают разработчики ядра?
                                                            Подскажите, пожалуйста, те, кто разбираются, неужели это правда?
                                                            Становится страшно жить.
                                                              +1
                                                              Да, данные сторонних приложений в современных ОС так просто не испортить. Однако, программе достаточно испортить собственные данные, чтобы возникли непредсказуемые эффекты. С начала испортили, потом перехватили исключение/сигнал и продолжили работать. Начали, например, аварийно данные сохранять. А ведь где-то что-то испорчено.
                                                              Или испортили что-то в данных собственной параллельной нити. И пока программа из-за нехватки памяти готовилась к завершению работы (в лог про ошибку писала), эта параллельная нить с неверными данными способна ужас разный наделать.
                                                                +1
                                                                А ещё собственные данные могут обрабатываться для кого-то. Пример — те же базы данных (выше в комментариях было).
                                                                  0
                                                                  Камень с души упал. Значит, я просто неверно понял суть проблемы.
                                                                  А почему бы не убрать в ОС эту проверку на обращение к нулевому адресу, чтобы дисциплинировать программистов? :)
                                                                    +1
                                                                    Наверняка этим же методом можно что-то еще подпортить в шаренной между процессами памяти, например.
                                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                                    0
                                                                    в чем разница: 1234->member и null->member?
                                                                    Прошу уточнить вопрос.

                                                                    порча да, но только данных этого приложения же
                                                                    Этого мало? Уточню: надо думать не в контексте порчи памяти в игре тетрис :).
                                                                      0
                                                                      Было бы интересно в тетрисе найти уязвимость, которая при определённой конфигурации фигурок приводила бы к увеличению счёта до достаточно большой величины :)
                                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                                          +1
                                                                          В целом согласен, но хочу отметить:
                                                                          навредить другому приложению нет возможности.
                                                                          Достаточно навредить себе! Особенно если это большое, сложное параллельное приложение, где malloc вернул 0 в одном из потоков и поток был прибит, а приложение продолжит работать. Вот только, где-то что-то могло быть испорчено.
                                                                          • НЛО прилетело и опубликовало эту надпись здесь
                                                                            +1
                                                                            вот Вы выделили память, а ее нет. Что может сделать приложение? Освободить какую-то память и повторить попытку выделения. Других вариантов у него нет.

                                                                            Есть. Вывести сообщение пользователю или в лог. Подождать. Отменить текущую команду. Убить текущую нить. Умереть. Комбианции всего этого в разных варинатах.

                                                                              +1
                                                                              PS: вот Вы выделили память, а ее нет. Что может сделать приложение? Освободить какую-то память и повторить попытку выделения. Других вариантов у него нет.
                                                                              Почему нет? Есть. Самый простой вариант: выделить память из резервного буффера. Так работал Turbo Pascal 6.0 четверть века назад.

                                                                              Кажется бредом — мы просто исчерпаем и резервный буфер тоже… но на самом деле — это способ делать простые и надёжные приложения. Ибо переключение на резервный буффер просто заметить, а значит — заставить работать программу по другому.

                                                                              Если нас просят открыть какое-нибудь окно, то проверяем — не перешли ли мы на использование резервного буффера и если да — сообщаем об ошике. Создаём окно (так как у нас есть резервный буффер, то проблем с выделением памяти не возникнет), после чего ещё раз проверяем — не начали ли мы использовать «резерв». Если начали — закрываем окно, сообщаем об ошибке.

                                                                              Просто, надёжно — и никаких проблем с «exception safety» и прочими разными NPE. Очень жаль, что за прошедшие с тех пор годы индустрия почти разучилась писать надёжные программы. «И так сойдёт» — девиз современного IT. Вот это вот — всего лишь вишенка на тортике…
                                                                          • НЛО прилетело и опубликовало эту надпись здесь
                                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                                            +2
                                                                            Это спор деонтологиста с консеквентиалистом, просто разные точки зрения.
                                                                            Я (как представитель первого типа) бы не хотел пользоваться, БД, которая использует mmap или ведёт журнал в памяти и который, при отсутствии возможности выделить память, «портится». По поводу адресов и overcommit — это может быть другая платформа (в т.ч. embedded), другая ОС, что угодно может отличаться от того, что «обычно бывает».

                                                                            Самое отвратительное, что это может ухудшить обработку ошибок, допустим, приведя к записи на диск из recovery path повреждённых данных, причём выявить такое «обычным» тестированием может быть весьма проблематично.

                                                                            Отдельно отмечу, что я знаком с мнением, что обработка ошибок вторична, а первична основная функциональность, но не согласен с ним, в любой системе с требованиями к надёжности, отличными от «всё равно что получится», политика обработки ошибок и восстановления после них как минимум не менее важна чем эта основная функциональность (как пример приведу ту же самую БД, я не думаю что было бы много желающих пользоваться БД, которая почти всегда работает нормально, но если на ФС заканчивается место или возникает ошибка записи на диск, то все данные повреждаются). Дам мою любимую ссылку: www.usenix.org/system/files/conference/osdi14/osdi14-paper-yuan.pdf
                                                                            • НЛО прилетело и опубликовало эту надпись здесь
                                                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                                                  0
                                                                                  $ cat /proc/self/maps
                                                                                  00400000-0040c000 r-xp 00000000 08:01 3932328                            /bin/cat
                                                                                  0060b000-0060c000 r--p 0000b000 08:01 3932328                            /bin/cat
                                                                                  0060c000-0060d000 rw-p 0000c000 08:01 3932328                            /bin/cat
                                                                                  01bb9000-01bda000 rw-p 00000000 00:00 0                                  [heap]
                                                                                  ...
                                                                                  
                                                                                  $ uname -a
                                                                                  Linux 4.10.0-42-generic #46~16.04.1-Ubuntu SMP Mon Dec 4 15:57:59 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
                                                                                  


                                                                                  Уже не так невозможно выглядит, правда?
                                                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                                                    +1
                                                                                    перфекционист говорит: «надо всегда выполнять проверки/реакцию»


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

                                                                                    И ситуации с UB для перфекциониста неприятны.
                                                                                    Вот перфекционист хочет, чтобы приложение просто взяло и упало, если malloc вернул null.

                                                                                    Можно написать явно «если null — возьми и упади».

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

                                                                                    И вот как бы на кой чёрт этому самому перфекционисту держать в своей голове все эти инварианты и объяснения, почему оно всё-равно-упадёт-как-надо, если можно взять и написать явно. Тем более, цена такого решения — нулевая.
                                                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                                                        0
                                                                                        то есть в hello world она нулевая, а в реальных приложениях вполне будет ненулевой.
                                                                                        потому что помимо вопроса с malloc будет вопрос с open, перечнем errno, итп итд.


                                                                                        Ага. Или с fork.

                                                                                        и использовать new/malloc только там где она может доставить неприятности. как-то так.


                                                                                        Почему вы таки смешиваете new и malloc? Конкретная проблема, которая обсуждается в этой статье, касается только с malloc (ну или nothrow-вариации оператора new разве что).
                                                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                                                            +1
                                                                                            скажут «достаточно знать о проблеме и применять превентивные меры только там где они необходимы»


                                                                                            В случае с сегфолтами — возможные варианты возникают в рантайме.
                                                                                            В случае с UB, о котором статья — возможные варианты и неопределённость возникают на стадии компиляции.

                                                                                            «достаточно знать о проблеме и применять превентивные меры только там где они необходимы»


                                                                                            Ну, да. Так в общем-то и предлагается делать.
                                                                                            Есть проблема (которую описал автор статьи). Есть превентивная мера, которую предлагает автор статьи — писать без UB.

                                                                                            Вот в рантайме сплошная неопределённость, сами приводите пример с ней, зачем нам дополнительная неопределённость на этапе компиляции — чтобы ещё больше усложнить отладку?
                                                                                            • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                +2
                                                                                                какие проблемы на стадии компиляции от не проверки результата malloc?


                                                                                                Ну, гипотетический пример:
                                                                                                Выделили буфер в 100мб, malloc вернул null, не проверили сразу -> начали разыменовывать (например, положили в конец буфера что-то и попали на свою память).
                                                                                                Передали указатель дальше.
                                                                                                Дальше передали указатель туда-сюда и отправили и имеем код вида

                                                                                                if (p == null)
                                                                                                {
                                                                                                какая-то умная обработка ошибки
                                                                                                }
                                                                                                else
                                                                                                {
                                                                                                начинаем заполнять p целиком
                                                                                                }.

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

                                                                                                Вы, конечно, молодец, и так не напишите, потому что знаете, что оптимизатор может так поступить, и убедитесь перед компиляцией, что проверки на null произойдут позже разыменовывания указателя, но завтра ваш код будет редактировать джуниор, который не знает, что такое UB.
                                                                                                И отлаживая джун тоже будет мучаться — ну как же, ведь если бы в этом месте указатель был равен нулю, сработала бы его обработка!
                                                                                                Ну как же, вот в дампе структура данных повреждённая (мы же раньше записали что-то) — наверное где-то там ошибка!

                                                                                                Ну вы то умный, через пару дней мучения джуна покажете ему что такое UB и как оптимизатор работает, но зачем оно вам нужно, если с самого начала можно было писать не допуская UB бесплатно и без смс?
                                                                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                    0
                                                                                                    по моему что-то очень надуманное.


                                                                                                    Надуманный сценарий, при котором данные инициализируются в таком порядке или надуманное отрезание ветки с проверкой на null?
                                                                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                        0
                                                                                                        При всём при этом крайне желательно, чтобы браузер хотя бы сохранил список открытых закладок и настройки, если те были изменены.
                                                                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                            0
                                                                                                            Для таких вещей есть понятие «Системные требования». Но когда в разделе «Системные требования» будет стоять строка «Объём оперативной памяти: 128 Петабайт» (и в скобках — залобались выделять больше и убили процесс), то это немножко странно, не находите?
                                                                                +1
                                                                                Программа может обработать это структурное исключение/сигнал. Но уже поздно. Где-то в памяти есть испорченные данные. Причем непонятно, какие данные испорчены и к каким последствиям это может привести!

                                                                                А не всё равно какие данные мы сломали? Данные в адресном пространстве нашего приложения. Наше приложение упало. Все данные потеряны и не важно — плохие там данные были или нет.
                                                                                Поэтому в этой ситуации и надеятся на FailFast, прикручивая проверки только там, где это действительно имеет значение и нельзя просто упасть.
                                                                                Ну и конечно, нельзя игнорировать потенциальный нулевой указатель, если у вас на уровень выше сидит тупой обработчик исключений, который молча погасит исключение возникшее в результате.
                                                                                  0

                                                                                  Главное, не успеть записать эти испорченные данные в базу/файл. Но там, где это действительно важно, можно использовать журналирование.

                                                                                    +1
                                                                                    Есть много случаев, когда UB связанное с нулевым указателем может привести к беде. И эти случаи надо знать. Но это сводится к «понимай как работает твой код, если случится nullptr», а не к «везде вставляй проверки».
                                                                                    Вообще вся статья относитсяисключительно к С. В С++ просто не надо использовать malloc и всё.
                                                                                  0
                                                                                  Спасибо за статью. Правильно я понимаю, что с оператором new совсем другая история? При ошибке, он кидает исключение. Поэтому можно спокойно разыменовывать указатель без проверки, и нету никакого неопределенного поведения. Я прав?
                                                                                    0
                                                                                    Да.

                                                                                    P.S. Есть std::nothrow вариант оператора new, но это другая история.
                                                                                      0
                                                                                      Причем nothrow может быть поведением по умолчанию
                                                                                    0
                                                                                    Ни в одном комментарии про Linux OOM killer.
                                                                                    Автор, если сможешь получить в Linux от malloc NULL, напиши пожалуйста статью, с интересом прочитаю. С этого наверное и следовало начать.
                                                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                                                        0
                                                                                        ок. я просто поиском по странице пытался
                                                                                        тем не менее. буду признателен за рабочий пример получения NULL от malloc
                                                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                                                            +1
                                                                                            Слегка модифицированный вариант voices.canonical.com/jussi.pakkanen/2013/05/24/malloc-and-linux:

                                                                                            #include<stdio.h>
                                                                                            #include<malloc.h>
                                                                                            
                                                                                            int main(int argc, char **argv) {
                                                                                              long size=1;
                                                                                              while(1) {
                                                                                                char *x = malloc(size*1024);
                                                                                                printf("Tried to alloc: %ldk.\n", size);
                                                                                                if(!x) {
                                                                                                  printf("Malloc returned null.\n");
                                                                                                  return 0;
                                                                                                }
                                                                                                *x = 0;
                                                                                                free(x);
                                                                                                size *= 2;
                                                                                              }
                                                                                              return 1;
                                                                                            }


                                                                                            $ gcc q.c ; ./a.out
                                                                                            Tried to alloc: 1k.
                                                                                            Tried to alloc: 2k.
                                                                                            Tried to alloc: 4k.
                                                                                            Tried to alloc: 8k.
                                                                                            Tried to alloc: 16k.
                                                                                            Tried to alloc: 32k.
                                                                                            Tried to alloc: 64k.
                                                                                            Tried to alloc: 128k.
                                                                                            Tried to alloc: 256k.
                                                                                            Tried to alloc: 512k.
                                                                                            Tried to alloc: 1024k.
                                                                                            Tried to alloc: 2048k.
                                                                                            Tried to alloc: 4096k.
                                                                                            Tried to alloc: 8192k.
                                                                                            Tried to alloc: 16384k.
                                                                                            Tried to alloc: 32768k.
                                                                                            Tried to alloc: 65536k.
                                                                                            Tried to alloc: 131072k.
                                                                                            Tried to alloc: 262144k.
                                                                                            Tried to alloc: 524288k.
                                                                                            Tried to alloc: 1048576k.
                                                                                            Tried to alloc: 2097152k.
                                                                                            Tried to alloc: 4194304k.
                                                                                            Tried to alloc: 8388608k.
                                                                                            Tried to alloc: 16777216k.
                                                                                            Tried to alloc: 33554432k.
                                                                                            Tried to alloc: 67108864k.
                                                                                            Malloc returned null.
                                                                                            
                                                                                            $ uname -a
                                                                                            Linux 4.10.0-42-generic #46~16.04.1-Ubuntu SMP Mon Dec 4 15:57:59 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
                                                                                            
                                                                                            $ free
                                                                                                          total        used        free      shared  buff/cache   available
                                                                                            Mem:       82479492    39326248     1789876      854504    41363368    41316992
                                                                                            Swap:      16618364     1884660    14733704
                                                                                            
                                                                                              0
                                                                                              64г одним куском. еще варианты? ради этого писать не демона (к примеру), а звездолет?
                                                                                                +4
                                                                                                Много кусков по 100 мегабайт тоже прокатывают.

                                                                                                ради этого писать не демона (к примеру), а звездолет?

                                                                                                Один if превращает программу в звездолёт?

                                                                                                  0
                                                                                                  Много кусков по 100 мегабайт тоже прокатывают.

                                                                                                  Нет, не прокатывают.

                                                                                                  Для справки, тот пример работает только потому, что vm.overcommit_memory=0 и проверяет он только alloc_size > total_free_ram. Таким образом — убери оттуда free() — ничего не поменяется. Аллоцируй хоть милён раз по 100мегабайт — ничего не изменится. Отвалится потом по хардварным лимитам, но такое попросту невозможно.

                                                                                                    0
                                                                                                    Нет, не прокатывают.

                                                                                                    Какую сумму вы прямо сейчас готовы поставить на это?
                                                                                                      –3
                                                                                                      Ответить по существу есть что? Ответить вам нечего, а потом мне надо будет слушать нытьё вида «а я не это имел ввиду», «а у меня вообще другой код», «а мы не договаривались, что в базовом окружении». Хотите спорить — условия вперёд, а нытьё слушать мне неохота.

                                                                                                      Слив я могу уже засчитать. Ведь ответа я не получил, как и хоть каких-то оснований вашим словам.
                                                                                                        +2
                                                                                                        Если это тот персонаж, о котором я думаю, то на linux.org.ru он давно известен. И те, кто его знают, воздерживаются от попыток конструктивного общения с ним. Если же попытаться с ним поговорить как с адекватным человеком, то получается, например, вот так. Можно обратить особое внимание на пассажи вида:
                                                                                                        Неверно, никто этого не сделал, да и даже если сделал — из этого ничего не следует. Нигде обратное не утверждалось, да и вообще разговор был не об этом.
                                                                                                        И это еще не самый худший образчик его мыслеизложения.
                                                                                                    +4
                                                                                                    Вы написали «буду признателен за рабочий пример получения NULL от malloc»? Вот вам рабочий пример, разве нет?
                                                                                                    Вы не допускаете ошибки в расчёте требуемого объёма памяти, которая может привести к тому, что допустим обычно пытаются выделить 40МБ, но если выбран флаг, который «никто не выбирает», то пытаются выделить 64ГБ? И из-за этого, при попытке записи по смещению 15МБ в выбранный массив будет перезаписываться heap например. Это _абсолютно_ нереалистичный сценарий? А если смещение не 15МБ?

                                                                                                    Вероятно инженеры, проектировавшие процессоры тоже думали что временнАя атака на модуль предсказания перехода или спекулятивного исполнения, что это нереалистичный сценарий =)
                                                                                                    Последствия такого мышления в виде ~20% увеличения времени исполнения при той же нагрузке я вот как раз наблюдаю у себя в продакшне после патчей против Meltdown =)
                                                                                                    –1
                                                                                                    Сплошные манипуляции. Первое — почему не убран free()? Он там ничего не делает. С ним мы делаем вид, что всё работает так, как предполагалось — нет. Не работает.

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

                                                                                                    И то, это специально закастыленный в ядре кастыль.
                                                                                                +3
                                                                                                Что может быть лучше, чем код, который надеется не только на ОС, но еще на какой-то специфический ее компонент. А если в ядре ваш код, а если в UEFI? Во встраиваемой системе? Для библиотеки общего назначения это раз плюнуть.
                                                                                                  +1
                                                                                                  Лехко.
                                                                                                  Скрытый текст
                                                                                                  Allocation 0: 0xb34ae008
                                                                                                  Allocation 1: 0xaf4ad008
                                                                                                  Allocation 2: 0xab4ac008
                                                                                                  Allocation 3: 0xa74ab008
                                                                                                  Allocation 4: 0xa34aa008
                                                                                                  Allocation 5: 0x9f4a9008
                                                                                                  Allocation 6: 0x9b4a8008
                                                                                                  Allocation 7: 0x974a7008
                                                                                                  Allocation 8: 0x934a6008
                                                                                                  Allocation 9: 0x8f4a5008
                                                                                                  Allocation 10: 0x8b4a4008
                                                                                                  Allocation 11: 0x874a3008
                                                                                                  Allocation 12: 0x834a2008
                                                                                                  Allocation 13: 0x7f4a1008
                                                                                                  Allocation 14: 0x7b4a0008
                                                                                                  Allocation 15: 0x7749f008
                                                                                                  Allocation 16: 0x7349e008
                                                                                                  Allocation 17: 0x6f49d008
                                                                                                  Allocation 18: 0x6b49c008
                                                                                                  Allocation 19: 0x6749b008
                                                                                                  Allocation 20: 0x6349a008
                                                                                                  Allocation 21: 0x5f499008
                                                                                                  Allocation 22: 0x5b498008
                                                                                                  Allocation 23: 0x57497008
                                                                                                  Allocation 24: 0x53496008
                                                                                                  Allocation 25: 0x4f495008
                                                                                                  Allocation 26: 0x4b494008
                                                                                                  Allocation 27: 0x47493008
                                                                                                  Allocation 28: 0x43492008
                                                                                                  Allocation 29: 0x3f491008
                                                                                                  Allocation 30: 0x3b490008
                                                                                                  Allocation 31: 0x3748f008
                                                                                                  Allocation 32: 0x3348e008
                                                                                                  Allocation 33: 0x2f48d008
                                                                                                  Allocation 34: 0x2b48c008
                                                                                                  Allocation 35: 0x2748b008
                                                                                                  Allocation 36: 0x2348a008
                                                                                                  Allocation 37: 0x1f489008
                                                                                                  Allocation 38: 0x1b488008
                                                                                                  Allocation 39: 0x17487008
                                                                                                  Allocation 40: 0x13486008
                                                                                                  Allocation 41: 0xf485008
                                                                                                  Allocation 42: 0xb484008
                                                                                                  Allocation 43: 0x4047008
                                                                                                  Allocation 44: 0x46008
                                                                                                  Allocation 45: 0xb77ed008
                                                                                                  Allocation 46: 0xbb7ee008
                                                                                                  Allocation 47: 0
                                                                                                  Cleanup

                                                                                                  Скрытый текст
                                                                                                  #include <memory.h>
                                                                                                  #include <iostream>
                                                                                                  #include <vector>
                                                                                                  #include <unistd.h>
                                                                                                  
                                                                                                  int main(int, char**)
                                                                                                  {
                                                                                                          std::vector<void*> v;
                                                                                                          v.reserve(1024);
                                                                                                          const size_t bufSize = (1<<26);
                                                                                                          for (int i = 0;; ++i)
                                                                                                          {
                                                                                                                  usleep(100000);
                                                                                                                  void* ptr = malloc(bufSize);
                                                                                                                  std::cout << "Allocation " << i << ": " << ptr << std::endl;
                                                                                                                  if (ptr == nullptr) break;
                                                                                                                  v.push_back(ptr);
                                                                                                          }
                                                                                                          std::cout << "Cleanup" << std::endl;
                                                                                                          for (auto ptr : v)
                                                                                                          {
                                                                                                                  free(ptr);
                                                                                                          }
                                                                                                          return 0;
                                                                                                  }
                                                                                                  

                                                                                                    0
                                                                                                    Кстати:
                                                                                                    Linux ***** 3.16.0-4-586 #1 Debian 3.16.43-2+deb8u2 (2017-06-26) i686 GNU/Linux
                                                                                                    Linux ***** 3.4.75-sun7i #35 SMP PREEMPT Sat Feb 8 02:10:31 CST 2014 armv7l GNU/Linux
                                                                                                  +1
                                                                                                  Отказ в обслуживании это еще ерунда. Отсутствие проверки malloc на NULL вполне и к RCE может привести.
                                                                                                    +1
                                                                                                    The Linux kernel's memset for the SuperH architecture has this property: link.
                                                                                                    К сожалению, это код на незнакомой мне разновидности ассемблера, поэтому я не берусь рассуждать о нём.

                                                                                                    Там всё просто. Память заливается вот таким циклом.
                                                                                                    	dt	r0  ;декремент счётчика
                                                                                                    	bf/s	1b  ;продолжаем пока r0 != 0
                                                                                                    	 mov.b	r5,@-r4  ;запись r5 => [r4] с преддекрементом


                                                                                                    SH — такой ранний RISC, имеющий delay slot как MIPS или Sparc. Команда mov выполняется параллельно с инструкций перехода.
                                                                                                    Почему бы не поставить постинкремент вместо преддекремента, как на 68k или ARM?
                                                                                                    Не получится — ведь такой инструкции нет (вернее есть, но не у всех процессоров SH)
                                                                                                    www.shared-ptr.com/sh_insns.html

                                                                                                    SH2A mov.b R0,@Rn+

                                                                                                    Для остальных чипов хитрые японцы сделали лишь команды чтения с постинкрементом, и записи с преддекрементом (очевидно для организации стека)
                                                                                                      0
                                                                                                      Заполнять буфер с конца, также как и писать другие алгоритмы «трогающие» адреса памяти с конца на современных архитектурах не следует, потому что современное hardware в основном работает по принципу look-ahead, т.е. кэширует то, что впереди.
                                                                                                        0
                                                                                                        А можно распараллелить запись, начав двигаться с начала и с середины, но на половину длины.
                                                                                                        –4
                                                                                                        Рекомендация загромождать код проверкой воспринимается мной как анти-паттерн, вредная рекомендация.
                                                                                                        Обоснование. Одной только проверки недостаточно, если уж проверять, кроме проверки придется еще и в каждом случае решать что делать дальше, а это означает что придется писать дополнительный код, принимать решения, которые не всегда могут оказаться удачными или безошибочными. Это отвлекает, это время и деньги и дополнительные баги/проблемы. Кроме того, этот код будет кодом решающим системные задачи и по смыслу не имеющим отношения к непосредственному алгоритму/ решаемой задаче, вот почему его быть не должно.
                                                                                                        Проблема тут в самих системных *nix функциях *alloc(). Они слишком старые, слишком «системные», и предназначались для системных реалий 70х-80х годов.

                                                                                                        Как выглядит более корректное решение?
                                                                                                        На мой взгляд лучше не использовать эти системные вызовы напрямую. Использовать или другие библиотечные функции, или написать свои обертки и использовать их. Основная задача — оставить в коде только семантику работы с памятью, не загромождая его ничем посторонним.
                                                                                                          –1
                                                                                                          Это можно обосновать другими словами тем, что хорошо построенные системы разбиваются на слои. Системному слою абстракции, которому принадлежат функции *alloc() — системную логику и системную «ответственность». Пользовательскому слою абстракции — соответственно, свою упрощенную ответственность.
                                                                                                            +4
                                                                                                            принимать решения, которые не всегда могут оказаться удачными или безошибочными


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

                                                                                                            Отличаются ли эти две альтернативы скоростью реализации? Да, секунд на десять. Ну или сколько времени у вас займёт один if.

                                                                                                            Отличаются ли последствия принятий этих решений?
                                                                                                            Когда всё идёт по плану — нет.
                                                                                                            Когда случается баг — да. Обнаруживать UB и отлаживать связанные с ним гейзенбаге в системном коде — адище лютое. Впрочем, случаются они крайне редко и в плане мат.ожидания могут выйти те же самые секунды три, что потратятся на написание if'а.

                                                                                                            эти системные вызовы

                                                                                                            *alloc — это не системный вызов.
                                                                                                            Тем не менее, системные вызовы действительно лучше не использовать напрямую.
                                                                                                              –1
                                                                                                              Просто если не писать проверку — ваше решение приводит к результату вида «Ну на вот этих платформах упадёт, на вот этих упадёт


                                                                                                              Вы не поняли смысл моих слов, видимо воспринимая слова «системные» или «системный код» буквально, system call. А также, можно выдвинуть гипотезу, вы недопонимаете зачем архитектурно выполняется деление между высокоуровневым application-level слоем, и «системным», более низкоуровневым слоем.

                                                                                                              Объясняю этот материал «на пальцах».
                                                                                                              Высокоуровневый user-level слой ничего не знает ни о каких «платформах». Следовательно, ничего там не может «упасть» и пишется уже «не думая» об этом, не предусматривая «падения». За падения, за ошибки выделения памяти и «платформы» ответственнен именно system-level слой, который и должен по моей прямой рекомендации включать *alloc() функции. Это то, что я порекомендовал выполнить как «обертку».

                                                                                                              Соответственно, ваш комментарий хоть и имеет собственную ценность, имеет слабое отношение к сказанному мной и/или не опровергает мой тезис.
                                                                                                                –1
                                                                                                                Еще раз уточню, во избежание. Деление «application/user-level» и «system-level» не имеет отношения к уровням защиты и os-терминологии. Это лишь указания на распределение ответственности в нашем, доступном коде, это символическое название слоев абстракции, которые вводятся при проектировании.
                                                                                                                  +4
                                                                                                                  Объясняю этот материал «на пальцах».
                                                                                                                  Высокоуровневый user-level слой ничего не знает ни о каких «платформах». Следовательно, ничего там не может «упасть» и пишется уже «не думая» об этом, не предусматривая «падения».


                                                                                                                  Разыменовывание нулевого указателя в надежде, что
                                                                                                                  1. Получится сегфолт
                                                                                                                  2. Компилятор раскроет UB именно так как вы планируете.

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

                                                                                                                  Я-то как раз и говорю, что у вас высокоуровневый код — и он на платформу и компилятор мог бы и не закладываться. Но закладывается. А зачем, если этого можно избежать одним if'ом?

                                                                                                                  Абстракции, с которыми работает высокоуровневый код в данном случае хорошо описаны.
                                                                                                                  Вот дёргает ваш код malloc. В спецификации стандартной библиотеки к malloc'у написано — может вернуть ноль.
                                                                                                                  Вот берёт ваш код указатель, который согласно документации может быть нулём и разыменовывает — в стандарте к компилятору написано, что оптимизатор может начать творить неожиданную хрень.

                                                                                                                  Не делать проверку на null -> закладываться на особенности конкретной версии вашего конкретного компилятора и конкретные реализации библиотек.
                                                                                                                  Собственно, обратите внимание: автор поста говорит именно о языке, стандарте и явлении языка (UB).
                                                                                                                  Комментирующие оппоненты: рассказывают про то, что такого не может быть в malloc'е в linux'е (конкретные реализации под конкретные платформы).
                                                                                                                    –2
                                                                                                                    Я думаю стоит начать с этого:
                                                                                                                    Разыменовывание нулевого указателя в надежде, что
                                                                                                                    1. Получится сегфолт
                                                                                                                    2. Компилятор раскроет UB именно так как вы планируете.
                                                                                                                    Уже является закладыванием и на платформу


                                                                                                                    В упомянутом выше «application-level» слое абстракции никогда не может возникнуть такой ситуации из-за того что нижележащий «system-level» слой не должен допускать ситуацию с возвратом не выделенной памяти. (Грубо говоря, null в application-level не может вернуться в принципе, на это можно рассчитывать).

                                                                                                                    Соответственно, начать надо с того, как у вас вообще тогда может возникнуть разыменование нулевого указателя? Никак, возвращаемая вам память всегда валидна. Поэтому развиваемая вами дальше гипотетическая ситуация хоть и имеет ценность, никогда не должна возникнуть на application level уровне абстракций при правильном построении программной системы.

                                                                                                                    Все остальное в вашем сообщении верно и имеет самостоятельную ценность. Вы совершенно верно сообщаете что рассчитывать на segfault нельзя и это в целом system dependent.
                                                                                                                      +3
                                                                                                                      (Грубо говоря, null в application-level не может вернуться в принципе, на это можно рассчитывать).


                                                                                                                      … и именно поэтому нужно использовать new, а не malloc, если уж пишем на C++.
                                                                                                                      Кстати, автор об этом упоминал. Мол, в самом коде хромиума такой проблемы не видно, потому что они в основном используют new.

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


                                                                                                                      Если вы делаете вовне своего клиентского кода (в аллокатор памяти, например) запрос, который никогда не возвращает нулевой указатель (дёрнув new, или дёрнув malloc и сделав проверку, предлагаемую автором) — действительно, никак.

                                                                                                                      Если же вы дёргаете malloc — ты вы используете интерфейс malloc'а. В интерфейсе malloc'а написано — «может вернуть null».

                                                                                                                      Поэтому и имеет смысл избавляться от ситуации, когда в ваш бизнес код приходит UB. Например, сделав все проверки сразу же на месте выделения памяти. Или написав обёртки. Или аллоцируя не через malloc.
                                                                                                                        0
                                                                                                                        В упомянутом выше «application-level» слое абстракции никогда не может возникнуть такой ситуации из-за того что нижележащий «system-level» слой не должен допускать ситуацию с возвратом не выделенной памяти.

                                                                                                                        Но он допускает. Тут же обсуждается вполне конкретная функция malloc, которая описана в стандарте...

                                                                                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                                      +1
                                                                                                                      если мы пишем скажем калькулятор, которым пользователь будет пользоваться нажимая в нем кнопки и тот калькулятор грохнется при недостатке памяти (как и половина приложений системы), то смысл лепить те проверки какой? Тут на первый план начинает выходить стоимость разработки ПО: может быть тот калькулятор пока Вы его покроете всеми перфекциями станет не нужен уже никому?


                                                                                                                      Напоминаю — автор статьи рассказал про то, какое UB бывает, какие у него могут быть эффекты и как одним простым if'ом его избежать.
                                                                                                                      Пользоваться этим знанием вас никто не заставляет.

                                                                                                                      большинство программ не пишут

                                                                                                                      Большинство программ так же содержит ошибки и уязвимости.
                                                                                                                      Это нормально.

                                                                                                                      Я не вижу смысла столкнувшись с указанием на какой-то косяк в программе начинать разглагольствовать про «НУ И ЧТО, В БОЛЬШИНСТВЕ ПРОГРАММ ТАК ЖЕ». Иногда стоит исправить. Иногда разумно проигнорировать или отложить. Информация о наличии косяка, возможных последствиях и способах борьбы и предотвращения возникновения аналогичных ошибок (т.е. ровно то, чем является статья) — ценна для принятия обоснованного решения даже если это решение — забить.

                                                                                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                                    +2
                                                                                                                    Рекомендация загромождать код проверкой воспринимается мной как анти-паттерн, вредная рекомендация.
                                                                                                                    Это мощно.
                                                                                                                    Одной только проверки недостаточно, если уж проверять, кроме проверки придется еще и в каждом случае решать что делать дальше, а это означает что придется писать дополнительный код, принимать решения, которые не всегда могут оказаться удачными или безошибочными.
                                                                                                                    Так ведь это же программирование. Тут нужно думать, решать, обрабатывать различные варианты и т.д. Работа программиста в этом и состоит.

                                                                                                                    Или сейчас уже принято писать программы на C или C++ без всей этой отвлекающей настоящего мастера рутины?
                                                                                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                                        +3
                                                                                                                        Самое последнее в данной задаче что Вас будет волновать — это вопрос «как будет вести себя данная программа/утилита в условиях недостатка памяти?»
                                                                                                                        Было бы очень хорошо, если бы вы не апроксимировали свои заблуждения на весь остальной мир. Нехватка памяти может ничем не отличаться от других ошибок, с которыми сталкивается программа. Например, как невозможность открыть файл.

                                                                                                                        Или вы в своих C/C++ программах результаты open/read/write так же предпочитаете не проверять?
                                                                                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                                            +3
                                                                                                                            я говорю: перфекционируя — надо перфекционировать до конца: начали проверять malloc, проверяем и open (я выше писал об этом несколько раз).
                                                                                                                            Вообще-то это не перфекционизм, это нормальная, обычная работа программиста: выполнил действие, которое может завершиться неудачно, проверь результат. В зависимости от этого принимай решение о дальнейших действиях.

                                                                                                                            Другой подход к делу, т.е. программирование в расчете только на благоприятный сценарий, в мире C/С++ возможен только при написании quick-and-dirty прототипов «на выброс». Может быть где-то в мире Erlang-а, Ruby или JavaScript-а по-другому, а здесь вот так.
                                                                                                                            там где это не нужно предпочитаю не проверять. и не тольк в C/C++, но и на других языках.
                                                                                                                            Очень надеюсь, что мне не придется иметь дело с написанным вами софтом.
                                                                                                                            • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                                                +2
                                                                                                                                давайте еще раз с примером задачи с градиентным спуском поразбираемся?
                                                                                                                                Задачи, как таковой, не было. Есть что-то в вашей голове, о чем я не имею ни малейшего понятия. Было бы ТЗ, можно было бы о чем-то говорить. Но даже если вы и сможете выкатить подробное ТЗ, то у меня и без этого есть чем заняться.
                                                                                                                                то вопрос: нужно ли заморачиваться неблагоприятным сценарием?
                                                                                                                                Конечно. Я вам даже больше скажу, это вопрос дисциплины. При должной дисциплине стоимость минимальной обработки неудачных результатов совершенно невысока. Особенно, если не брать C, а ограничиваться C++ с возможностью использования исключений.
                                                                                                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                                                    +3
                                                                                                                                    Простите мне мой резкий тон, но мне очень жаль времени, которое я на вас вынужден тратить. Есть сильное ощущение, что к программированию (особенно на C и С++) вы никаким боком не относитесь. Может быть учитесь. Посему вам кажется, что дать ссылку на описание задачи коммивояжера — это дать постановку задачи. Боюсь вас огорчить, но это вовсе не так.

                                                                                                                                    А т.к. вы к программированию имеете отношение постольку-поскольку, то предметно с вами разговаривать вряд ли возможно. Т.к. вы просто не понимаете предмета разговора. Причем как на уровне собственно программирования (тупо if-ы в коде), так и на уровне того, что хочет заказчик и что его интересует.
                                                                                                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                                                    0
                                                                                                                                    давайте еще раз с примером задачи с градиентным спуском поразбираемся? (выше)


                                                                                                                                    Давайте. Много весёлых моментов испытал с переписыванием кода всяких лингвистических числодробилок, написанными людьми на плюсах из соображений «ну пусть пишется, как пишется, ой, что значит, что у нас при определённых условиях nan'ы повылезали отовсюду?».

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

                                                                                                                                    Энивер, действительно, сценариев, когда нужно быстро прототипировать — много.
                                                                                                                                    Лучше в таких случаях использовать более устойчивые к выстрелам себе в ногу инструменты (т.е. аккуратнее с C++).
                                                                                                                                    И даже если принимается решение говнокодить весело, отважно, бесрассудно — лучше понимать, в каком моменте ты оставил для себя грабли (в виде того же UB, про который рассказал автор), что делает знания, сообщённые автором, безусловно ценными.
                                                                                                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                                                        +4
                                                                                                                                        тут проблема не перфекционизма а отсутствия автоматического тестирования :)


                                                                                                                                        Ого! То есть, теперь покрыть код тесткейсами (да ещё и заранее придумать такие кейсы, которые, собственно, выстрелили только через годы непрерывной работы с кучей данных) — это не вопрос, который требует затраты времени. А вот один if поставить, чтобы UB убрать — это ужас-ужас, теряем конкуретное преимущество.

                                                                                                                                        И кто ещё из нас максималист? ;)
                                                                                                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                                                            +3
                                                                                                                                            то есть не надо смешивать способ достижения цели с определением цели и все будет хорошо :)


                                                                                                                                            Избегание UB — такая же часть современной парадигмы разработки на С++, как и использование идиом вроде RAII.

                                                                                                                                            автоматические тесты — способ написания ПО

                                                                                                                                            Не тесты, сами тесты не обязательно связаны со способом разработки.
                                                                                                                                            Вот TDD — это способ написания ПО. А вот просто написание каких-то автоматических тестов — это уже реализация.

                                                                                                                                            Интегрировать TDD куда-либо (особенно в наколоченные числодробилки, которые зачастую пишутся не то что бы инженерами) — дороже, чем написание if'а после malloc'а. Я абсолютно серьёзен, хотя тесты искренне уважаю и люблю.
                                                                                                                                            • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                                            +1
                                                                                                                            Это мощно.


                                                                                                                            Каков должен быть ответ на «мощно» — я не знаю.
                                                                                                                            Уместно ли делать такие комментарии в технической дискуссии?

                                                                                                                            Выше я привел логические обоснования, см. деление «app»-level уровень абстракции и «системный», закладываемый при проектировании. Я описал как эта проблема должна быть решена наиболее универсальным и наиболее разумным способом.

                                                                                                                            Тут нужно думать, работа программиста в этом и состоит.


                                                                                                                            Вот и подумайте, что именно предлагается, если вы, конечно, разработчик.
                                                                                                                            То что я предложил находится на чуть более высоком уровне, архитектурном. Возможно просто «программист» этой проблемы не видит/не понимает, думая что выходом является поставить везде copy-paste проверки…

                                                                                                                            Или сейчас уже принято писать программы на C или C++ без всей этой отвлекающей настоящего мастера рутины?


                                                                                                                            Возможно вас это удивит, но — да. К этому всегда стремятся…
                                                                                                                              0
                                                                                                                              Я описал как эта проблема должна быть решена наиболее универсальным и наиболее разумным способом.
                                                                                                                              Могли бы ткнуть пальцем? А то кроме пространных и абстрактных рассуждений о старости системных вызовов в unix-ах вообще и *alloc-ов в частности ничего не было. Никакой конкретики. Ну или я не видел, комментариев здесь много, за всеми не уследить.
                                                                                                                              Вот и подумайте, что именно предлагается, если вы, конечно, разработчик.
                                                                                                                              Временами еще разработчик. И вот думаю-думаю, и глубину вашей мысли не постигаю. Могли бы вы объяснить ее на примерах кода, для тех, кто «от сохи» и в абстрактной архитектуре как-то не очень?
                                                                                                                              Возможно вас это удивит, но — да.
                                                                                                                              Очень сильно удивит. А вам ведь не составит труда на примерах кода показать, как это оно?
                                                                                                                                –1
                                                                                                                                Очень сильно удивит.


                                                                                                                                Если вас удивляет тезис того что программисты постоянно стремятся снизить себе трудоемкость работы — тогда ответьте (самому себе, мне не надо) на вопрос — зачем создаются библиотеки? Зачем добавляется syntactic shugar?

                                                                                                                                А вам ведь не составит труда на примерах кода показать, как это оно?


                                                                                                                                А ведь составит. Мне это затратно, тратить время на вас лично.
                                                                                                                                Изучайте существующие кодовые базы, а не пользуйтесь уловкой «а вот покажите мне пальцем». В простейшем случае изучите поведение/реализацию operator new в языке c++ (на предмет проверки и exeption) или что происходит в managed-языках.
                                                                                                                                Там все происходит именно так, как это вам описано — пользователь языка генерально доверяет распределению памяти, нет проверок по месту на то что аллокация произошла/не произошла.

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

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

                                                                                                                                Вопрос который остался за кадром — это что делать на системном слое сразу после вызова *alloc, если память не вернулась? Все поведение malloc() в оригинале предназначалось для GE-645 с которым работали в Bell Labs и который являлся мультизадачным mainframe с виртуальной памятью. Не исключено что в оригинале можно было подождать :) пока память появится. В данном же случае можно либо завершать процесс, выводить сообщение пользователю и ждать, либо предусмотреть какое-либо другое поведение, но это уже детали.
                                                                                                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                                                    0
                                                                                                                                    Там все происходит именно так, как это вам описано — пользователь языка генерально доверяет распределению памяти, нет проверок по месту на то что аллокация произошла/не произошла.
                                                                                                                                    Хочу вас разочаровать. Когда разработчик имеет дело с исключениями вместо кодов возврата, необходимость думать и принимать решение никуда не исчезает. Там есть свои проблемы, например, позаботиться об exception safety.
                                                                                                                                    Предлагается выполнить эту проверку в одном месте, сразу после вызова malloc(), оформить это как функцию/класс/макро, назначив этому функционалу обязанности «системного слоя» и далее уже писать свой application код в надежде на то что в application-level уровне абстракции память валидна всегда, не может быть ситуации когда память не возвращена, и, соответственно, UB не может возникнуть в принципе.
                                                                                                                                    Уж простите мне мой французский, но какая глубокая мысль. Видимо, мосье теоретик и архитектор-астронавт?

                                                                                                                                    Иначе как объяснить вот эту вот «досадную мелочь»:
                                                                                                                                    В данном же случае можно либо завершать процесс, выводить сообщение пользователю и ждать, либо предусмотреть какое-либо другое поведение, но это уже детали.
                                                                                                                                    Получается в точности как в народной мудрости: гладко было на бумаге, но забыли про овраги. На словах все красиво про уровни абстракции, а как доходит до практики, то «это уже детали».
                                                                                                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                                                        +1
                                                                                                                                        эта необходимость уменьшается. раньше ему надо было думать на каждом вызове.
                                                                                                                                        теперь можно весь стек вызовов охватить одним try/catch и не париться особо.
                                                                                                                                        Попробуйте сделать тот же std::vector::push_back/emplace_back с обеспечением exception safety. Посмотрим, придется вам париться или нет.
                                                                                                                                      –1
                                                                                                                                      Хочу вас разочаровать. Когда разработчик имеет дело с исключениями вместо кодов возврата, необходимость думать и принимать решение никуда не исчезает. Там есть свои проблемы, например, позаботиться об exception safety.


                                                                                                                                      Вы не правы, исчезает.
                                                                                                                                      В легко достижимом идеале пишется код реализующий только свою задачу, не отвлекаясь на посторонние детали, такие как проверки выделения памяти, реализация exception safety, мультипоточности и проч. Мы не должны смешивать код с посторонними «костылями», выполняющими вспомогательные функции. И этот идеал достижим, именно к нему и следует стремиться, приближаясь по мере возможности. В простейшем случае создается библиотечный набор exception-safe классов, берущий на себя соотв. задачи

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


                                                                                                                                      Зачем вы хамите, переходя на личности? Если у вас отсутствуют аргументы — это не повод к Ad hominem.

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


                                                                                                                                      Вы не правы.
                                                                                                                                      Построить обертку и не использовать malloc() повсеместно (т.е. на application-level уровне) — это именно то, что следует сделать. В чем тут проблема и какие тут «овраги»?

                                                                                                                                      Итого, никаких аргументов против простейшего решения вы не привели.
                                                                                                                                      Более того, вы отчаянно сваливаете техническую дискуссию в фанбойский балаган.
                                                                                                                                        +3
                                                                                                                                        В легко достижимом идеале пишется код реализующий только свою задачу, не отвлекаясь на посторонние детали, такие как проверки выделения памяти, реализация exception safety, мультипоточности и проч.
                                                                                                                                        Простите, а вы точно к разработке на C или C++ имеете отношение?
                                                                                                                                        Зачем вы хамите, переходя на личности? Если у вас отсутствуют аргументы — это не повод к Ad hominem.
                                                                                                                                        А вы начните предметно разговаривать, без воспарений в высокие абстракции на счет каких-то слоев, которые волшебным образом решают проблемы неудачных операций (хоть вызовов malloc, хоть чего другого). Тогда можно будет поговорить о решениях. Пока же есть ощущение, что вы живете не в мире реального программирования, а в какой-то параллельной вселенной, в которой легко достигается абстрагирование от многопоточности, exception safety и пр.

                                                                                                                                        Другими словами: покажите, что с вами есть о чем говорить. Что вы не теоретик и архитектор-астронавт.
                                                                                                                                        Построить обертку и не использовать malloc() повсеместно (т.е. на application-level уровне) — это именно то, что следует сделать. В чем тут проблема и какие тут «овраги»?
                                                                                                                                        Ну так покажите, какую обертку вы предлагаете делать. Тогда можно будет сказать, где эта обертка будет работать, а где нет. Но вместо этого вы говорите про какие-то system-level и application-level. А действительно важные вещи, которые влияют и на то, и на другое, это у вас: «уже детали».
                                                                                                                                          –1
                                                                                                                                          Простите, а вы точно к разработке на C или C++ имеете отношение?

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

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


                                                                                                                                          Волшебным?
                                                                                                                                          Сошлитесь на мой фрагмент текста, который для вас непонятен и объясните что там для вас «не предметно» и «волшебно».

                                                                                                                                          Тогда можно будет поговорить о решениях. Пока же есть ощущение


                                                                                                                                          С «ощущениями» и «чувствами» это не ко мне. Я только по технической и архитектурной части.

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


                                                                                                                                          Вы троллите? Что тут может быть «где эта обертка будет работать, а где нет»?

                                                                                                                                          //  Call it "System level"
                                                                                                                                          void* checked_malloc(size_t sz) {
                                                                                                                                              void* ptr = malloc(sz);
                                                                                                                                              if (!ptr) {
                                                                                                                                                  exit(YOUR_ERROR_CODE);
                                                                                                                                              }
                                                                                                                                              return ptr;
                                                                                                                                          }
                                                                                                                                          
                                                                                                                                          // Call it "application level"
                                                                                                                                          int main() {
                                                                                                                                              void* mymem1 = checked_malloc(YOUR_SIZE1); 
                                                                                                                                              // Here I fully trust mymem1, no need for checking
                                                                                                                                              
                                                                                                                                              void* mymem2 = checked_malloc(YOUR_SIZE2); 
                                                                                                                                              // Here I fully trust mymem2, no need for checking
                                                                                                                                          
                                                                                                                                              free(mymem2);
                                                                                                                                              free(mymem1);
                                                                                                                                              return 0;
                                                                                                                                          }
                                                                                                                                          


                                                                                                                                          — Концепция ясна? Это же сверх-тривиально, как тут что-то может быть неясно, что тут требуется иллюстрировать?

                                                                                                                                          Но вместо этого вы говорите про какие-то system-level и application-level.


                                                                                                                                          Это простейшее разбиение на логические слои. Почему вы не можете мне просто написать: «Простите, я не совсем понимаю что вы имеете в виду. Можете мне объяснить?»

                                                                                                                                          А действительно важные вещи, которые влияют и на то, и на другое, это у вас: «уже детали».


                                                                                                                                          Вы не правы.
                                                                                                                                          Решение принимаемое в каждой конкретной программе в случае нехватки памяти не имеет значения (выше это тривиальный вызов exit() ). Мы обсуждаем только узкий контекст необходимости проверки аллокаций памяти. Всё что вне этого контекста — не информативно.
                                                                                                                                            +2
                                                                                                                                            Какая вам разница?
                                                                                                                                            Огромная. Когда в тему о проблемах использования malloc в C и C++ приходят теоретики, ссылающиеся на опыт управляемых языков, то разговаривать, как правило, можно только о личностях, которые до такого додумываются.
                                                                                                                                            Сошлитесь на мой фрагмент текста, который для вас непонятен
                                                                                                                                            Это вы троллите? Я вам уже сказал, что до сих пор вы вообще не сказали ничего конкретного, что можно было бы предметно обсуждать. Вот только сейчас вы привели пример кода. Из которого уже видна степень вашей экспертизы.
                                                                                                                                            Я только по технической и архитектурной части.
                                                                                                                                            Пока что это ничем не подтверждено.
                                                                                                                                            — Концепция ясна?
                                                                                                                                            Более чем.
                                                                                                                                            Это же сверх-тривиально, как тут что-то может быть неясно, что тут требуется иллюстрировать?
                                                                                                                                            Отличная иллюстрация того, что вы не понимаете сложности и широты темы, о которой идет речь. Если такой checked_malloc будет в какой-нибудь библиотеке для парсинга хитрого формата файлов, то использовать библиотеку с подобным checked_malloc будет ой как стремно, скажем, в многооконном офисном приложении.
                                                                                                                                              –2
                                                                                                                                              то разговаривать, как правило, можно только о личностях, которые до такого додумываются.


                                                                                                                                              Я считаю что о вашей деятельности нужно уведомить модераторов. Когда человек переходит на личности нечаянно, это одно дело. Но когда он вбил себе в голову и раздает всем вокруг ярлыки, еще и ОБОСНОВЫВАЯ свое хамство — это совершенно другой случай.

                                                                                                                                              Евгений, вы не правы во всем вашем выступлении.

                                                                                                                                              Моя тривиальнейшая мысль заключалась в том что в коде желательно писать не напрямую библиотечный malloc() с повсеместной проверкой возвращаемого значения, но его собственную замену/обертку — совершенно верна. Это азы. Это тривиальщина.

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

                                                                                                                                              Вы никак мои слова не опровергли. Вот именно от вас ПО СУЩЕСТВУ никто тут ничего интересного не услышал, впустую потеряв время на вычитку ваших бессмысленных текстов.

                                                                                                                                              И вместо этого, будучи изначально неправым вы:
                                                                                                                                              а) Совершаете атаку на мою личность, занимаясь на Habrahabr выяснением кто якобы © «лучше знает XYZ а кто не знает».
                                                                                                                                              — это очень нехорошо.

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

                                                                                                                                              ORLLY? Тривиальный рефакторинг будет иметь проблемы? Чего? Обертка malloc()'а доставит какие-то практические проблемы? Конечно же это не так, и это ложь, бред, потеря реальности.

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

                                                                                                                                              Вынужден разорвать бессмысленную дискуссию.
                                                                                                                                                +1
                                                                                                                                                Давайте я вам напомню, как развивались события. Вы написали нечто, с чем сложно согласиться. Это заставило задать вам уточняющий вопрос. Но вместо того, чтобы на него ответить, вы завели шарманку в стиле:
                                                                                                                                                Вот и подумайте, что именно предлагается, если вы, конечно, разработчик.
                                                                                                                                                Далее я вас попросил говорить более предметно (т.к. и в ответах другим участникам дискуссии вы не утруждали себя конкретными примерами). На что вы продолжили играть на той же шарманке:
                                                                                                                                                Мне это затратно, тратить время на вас лично.
                                                                                                                                                Изучайте существующие кодовые базы, а не пользуйтесь уловкой «а вот покажите мне пальцем». В простейшем случае изучите поведение/реализацию operator new в языке c++ (на предмет проверки и exeption) или что происходит в managed-языках.
                                                                                                                                                И продолжали играть до тех пор, пока из вас таки не удалось выжать хоть какой-то пример кода.

                                                                                                                                                И вот когда код появился, с вами и вашими «убеждениями» все стало окончательно понятно. Ваши же рекомендации не проходят простейшей критики. Ну не подходит предложенный вами вариант checked_malloc для ряда случаев. И именно чего-то подобного я и ожидал, как только вы написали вот это:
                                                                                                                                                В данном же случае можно либо завершать процесс, выводить сообщение пользователю и ждать, либо предусмотреть какое-либо другое поведение, но это уже детали.

                                                                                                                                                Нежелание человека разговаривать о значимых деталях наводит на подозрение о том, что человек не является действующим разработчиком (по крайней мере действующим C или C++ разработчиком), об этих-то подозрениях и приходилось вам мягко намекать.
                                                                                                                                                  –2
                                                                                                                                                  А давайте я взамен тонны вот этого запредельного школьного хамства:
                                                                                                                                                  eao197 Это мощно.

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

                                                                                                                                                  eao197 вы не понимаете сложности и широты темы, о которой идет речь

                                                                                                                                                  eao197 подозрение о том, что человек не является действующим разработчиком

                                                                                                                                                  eao197 покажите, что с вами есть о чем говорить. Что вы не теоретик и архитектор-астронавт.


                                                                                                                                                  Отпишу доступным вам языком? Ну-ка брысь курить xmalloc!
                                                                                                                                                  Потому что весь этот школьный бред просто не мог бы возникнуть, если бы eao197 хотя бы подозревал о его существовании.

                                                                                                                                                  eao197 Ну не подходит предложенный вами вариант checked_malloc для ряда случаев.

                                                                                                                                                  Жгите ещще красавчик, я польщен глубиной беседы и вашими «выводами» о иллюстративном коде, набитом за пару минут.
                                                                                                                                                  © Ге-ни-аль-но!
                                                                                                                                                    +1
                                                                                                                                                    вашими «выводами» о иллюстративном коде, набитом за пару минут.
                                                                                                                                                    Так как прикажете воспринимать приведенный вами здесь код?

                                                                                                                                                    Как демонстрацию ваших рассуждений о слоях абстракции? Или как не имеющую отношения к теме иллюстрацию, на которую не стоит обращать внимания?

                                                                                                                                                    Если это всего лишь иллюстрация, то когда будет хоть какая-то конкретика, которую можно обсуждать предметно? Или обсуждать останется только вашу тонкую душевную организацию?

                                                                                                                                                    Я вас уже который комментарий прошу: давайте ближе к теме.
                                                                                                                                                      –2
                                                                                                                                                      Евгений, вы присмирели, благодарю вас. Таким вы мне нравитесь. :)

                                                                                                                                                      Вы мне только что написали:
                                                                                                                                                      Ну так покажите, какую обертку вы предлагаете делать. Тогда можно будет сказать, где эта обертка будет работать, а где нет.


                                                                                                                                                      Я вам объяснил идею словами и привел иллюстративный пример кода. Кто знает? Может у вас реально проблемы с пониманием русского языка, действительно, бывают такие люди. Не проблема, я пошел вам навстречу набросав простейший пример в качестве иллюстрации… эээ… «архитектурным решением» называть это слишком громко, пусть будет «рефакторинга».

                                                                                                                                                      На это вы выдали в эфир совершенно пустое рассуждение (по смыслу никак не опровергающее и не подтверждающее мой исходный тезис):
                                                                                                                                                      Ну не подходит предложенный вами вариант checked_malloc для ряда случаев.
                                                                                                                                                      Ну очевидно не подходит, а что должен? Это как-то отменяет саму идею, сказанное в самом первом сообщении, как-то ПРИНЦИПИАЛЬНО запрещает заменять malloc на собственную обертку, или что? В чем проблема-то? Вы это никак не прояснили. И вот вы пишете:
                                                                                                                                                      Так как прикажете воспринимать приведенный вами здесь код? Как демонстрацию ваших рассуждений о слоях абстракции? Или как не имеющую отношения к теме иллюстрацию, на которую не стоит обращать внимания?

                                                                                                                                                      Хахахаха. Я после этой фразы пытаюсь понять, чего вы добиваетесь. Реально, уже думаю о нехорошем. :)
                                                                                                                                                      Если это всего лишь иллюстрация, то когда будет хоть какая-то конкретика, которую можно обсуждать предметно? Или обсуждать останется только вашу тонкую душевную организацию? Я вас уже который комментарий прошу: давайте ближе к теме.

                                                                                                                                                      Конкретика?
                                                                                                                                                      А какая вам «конкретика» тут нужна?
                                                                                                                                                      Ну вот вам написали, не используйте malloc, заменяйте враппером, не замусоривайте код проверками, это решение несколько получше, вон даже на *nix ах без проблем существует аналогичный xmalloc. Даже продемонстрировали примерный код. Куда уж конкретней, Евгений? Приведите пример своей «конкретики»?

                                                                                                                                                      Ну и давайте все-таки вернемся к исходной посылке, ибо вы не продемонстрировали какого-либо суждения по существу вопроса.
                                                                                                                                                      Исходное предложение до абсурдного простое, я и другие люди не видим что там ВООБЩЕ можно обсуждать. Но вы, видимо, усматриваете какие-то концептуальные проблемы. Вот только рассказать не успели, всё больше требуя от меня. Я ваши требования выполнил.

                                                                                                                                                      Жду теперь от вас наконец какого-то разумного опровержения или возражения по существу сказанного в исходном сообщении Подчеркиваю, жалобы на якобы «не конкретику» я уже слышал. Код привел, объяснения дал. На xmalloc сослался. Давайте теперь Евгений вашу здравую аргументацию в ответ на исходный пост.

                                                                                                                                                      И у меня к Вам будет предложение. А давайте с вашей помощью поставим на хабре рекорд абсурда и войдем в историю? Ну например, я напишу слова «оператор сравнения», а вы возьмете на себя функцию раз десять написать «давайте конкретику»? Я вам уже и код
                                                                                                                                                      if (x) {}
                                                                                                                                                      приведу, а вы всё знай будете требовать «конкретики», да «конкретики»? Давайте, а? Чего уж там, гулять так гулять абсурд так абсурд! :)
                                                                                                                                                        +3
                                                                                                                                                        Опять графоманские потоки вместо обсуждения технических деталей. Сильно не вяжется с приснопамятным:
                                                                                                                                                        Я только по технической и архитектурной части.
                                                                                                                                                        Скорее я тут вынужден разговаривать не с архитектором-астронавтом, а с теоретиком-графоманом.

                                                                                                                                                        Поскольку вы ссылаетесь еще на кого-то («я и другие люди не видим что там ВООБЩЕ можно обсуждать») то придется потратить время дабы объяснить вам и мифическому еще кому-то очевидные вещи. Очевидные для тех, кто не отмахивается посредством «это уже детали», как вы.

                                                                                                                                                        Итак, предложенная вами идея оберток вокруг malloc-а, во-первых, работает только тогда, когда обертка имеет возможность прервать выполнение кода после возникновения ошибки тем или иным способом. Вызовом abort/exit/terminate, или выбросом исключения, или эмуляцией исключения на подручных средствах. Или даже goto error, если «предлагаемая» вами обертка реализована в виде макроса.

                                                                                                                                                        Проблема здесь в том, что это не всегда работает. Безусловное прерывание программы (abort/exit/terminate) может быть приемлемым для утилит типа grep, wc или xargs, но совершенно не приемлем для библиотек вроде libxml2, zlib или libcurl. Так же это не приемлемо для больного класса приложений, скажем офисных пакетов или СУБД.

                                                                                                                                                        Исключения могут быть недоступны в принципе. Скажем, если мы ограничены чистым C. Или же по каким-то веским причинам исключения отключены в C++. С эмуляцией исключений может быть та же самая беда — они могут быть запрещены.

                                                                                                                                                        Как должна вести себя обертка вокруг malloc в условиях, когда прервать исполнение кода нельзя (например, внутри libxml2 или libcurl)?

                                                                                                                                                        Эта обертка будет вынуждена возвращать результат операции, в том или ином виде. И не суть важно, будет ли этот результат возвращаться в виде голого указателя (что нам придется делать, если мы находимся в чистом C) или в виде какой-то обертки, вроде std::optional или folly::Expected. Все равно мы будем вынуждены этот результат проверять. А раз так, то вопрос «нужно ли проверять коды возврата malloc?» для большого количества прикладных ниш, где применяются C/C++ превратиться в «нужно ли проверять коды возврата обертки над malloc?». Получаем то же самое, вид в профиль.

                                                                                                                                                        Ну и, во-вторых, как уже было сказано, наличие оберток вовсе не отменяет того, что:
                                                                                                                                                        в каждом случае решать что делать дальше, а это означает что придется писать дополнительный код, принимать решения, которые не всегда могут оказаться удачными или безошибочными
                                                                                                                                                          –3
                                                                                                                                                          eao197, Евгений Олейников, вы занимаетесь пустой демагогией и мне уже просто интересно исследовать психологию, т.е у вас реальные проблемы, или же намеренно уходите от ответа, т.е. намеренно врёте в отсутствие аргументации.

                                                                                                                                                          Я же вам написал — давайте теперь по существу поставленного вопроса. А вопрос у нас по существу такой: Автор статьи, Andrey2008 объяснил почему результат вызова malloc() проверять надо, и аргументировал это в общем случае UB в случае отказа функции вернуть память и возврата null pointer. Я на это (пишу раз в десятый) аргументировал тем, что не совсем красиво будет ставить везде проверки, и что это функцию лучше обернуть проверкой, создав условие что null pointer никогда не вернется, также как это делает xmalloc.

                                                                                                                                                          Вы начинаете уводить в сторону, вместо обсуждения вопросов Андрея и моего, рассказывать постороннее, что в общем случае нельзя реализовать универсальную обработку случая отказа выделения памяти, о чем вам было неоднократно уже сказано:
                                                                                                                                                          Вопрос который остался за кадром — это что делать на системном слое сразу после вызова *alloc, если память не вернулась? Все поведение malloc() в оригинале предназначалось для GE-645 с которым работали в Bell Labs и который являлся мультизадачным mainframe с виртуальной памятью. Не исключено что в оригинале можно было подождать :) пока память появится. В данном же случае можно либо завершать процесс, выводить сообщение пользователю и ждать, либо предусмотреть какое-либо другое поведение, но это уже детали.
                                                                                                                                                          Конечно же нельзя предусмотреть абсолютно универсальную, общую функцию для абсолютно любых случаев в жизни. Если уж на то пошло, следует реализовать callback на желаемую функцию обработки ошибки и дело в шляпе. Но это не является поставленным вопросом, это офтопик, совершенно посторонняя деталь. Вопрос тут:
                                                                                                                                                          а) Тема начатая автором статьи
                                                                                                                                                          б) Мое дополнение (см. курсив выше)
                                                                                                                                                          Вот их и следует обсуждать, не уводя в сторону на несущественные по смыслу детали.

                                                                                                                                                          В связи с чем ваша попытка ответа не засчитывается, Евгений, пытайтесь еще. Ждем ответа от Вас на поставленный вопрос по-существу (выделено выше курсивом), ответ вы пока не дали, отделавшись отпиской.
                                                                                                                                                            0
                                                                                                                                                            1. Вы не понимаете, о чем мы с вами разговариваем и почему-то ждете ответов от меня, хотя это вам нужно отвечать за свои убеждения и рекомендации.

                                                                                                                                                            2. Вы настолько тупы, что не можете скопипастить мое имя и фамилию правильно из профиля. Это полностью объясняет проблемы с п.1.

                                                                                                                                                            На якобы хамство с мой стороны лучше пожаловаться сразу в ООН и Спортлотто.
                                                                                                                                                              –3
                                                                                                                                                              Я тут внимательнее вчитался в ваш текст, Евгений eao197, вы выдали себя с потрохами.

                                                                                                                                                              Эта обертка будет вынуждена возвращать результат операции, в том или ином виде. И не суть важно, будет ли этот результат возвращаться в виде голого указателя (что нам придется делать, если мы находимся в чистом C) или в виде какой-то обертки, вроде std::optional или folly::Expected. Все равно мы будем вынуждены этот результат проверять.


                                                                                                                                                              — Обратим внимание на ваши слова, Евгений, «вынуждена возвращать результат», «будем вынуждены этот результат проверять».

                                                                                                                                                              Это, наверное, у Вас как раз тот случай, когда человек понимает мелочи из C++ (std::optional, итд) но не понимает самую важную суть предлагаемого в разговоре, простейшее архитектурное и алгоритмическое решение. Но при этом как надуты щеки! :)

                                                                                                                                                              Еще раз поясняю специально для вас Евгений, суть предложения, что и зачем делается. Читайте внимательно!

                                                                                                                                                              malloc() заменяется на свою функцию-обертку.
                                                                                                                                                              Зачем это делается? Да затем, чтобы результату её вызова доверять в своем коде ВСЕГДА. Почему это можно делать?

                                                                                                                                                              Обертка внутри себя проверяет результат вызова malloc(), и в случае возврата malloc()'ом значения null pointer, обертка НИКОГДА не вернет в вызвавший её код null pointer, сразу предприняв какую-то обработку, которую можно задать универсальным callback'ом или прямиком прописав в данном конкретном случае данной конкретной обертки. Она просто не вернется назад. Всё, краш, fail. Памяти нет. Отказ.

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

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

                                                                                                                                                              ПОВТОРЯЮ :)
                                                                                                                                                              В чем плюс? В чем смысл всего этого?
                                                                                                                                                              В основном в том, что null pointer в вызываемый код не вернется НИКОГДА, и соответственно, результату вызова можно всегда полностью доверять. А это значит, что его НЕ НАДО ПРОВЕРЯТЬ. Это ровно противоположно тому что вы думаете и пишете.

                                                                                                                                                              Тем самым проблема постоянной проверки возвращаемого значения (и каких-либо дополнительных действий) уходит. В «клиентский» код (вызывающий обертку) никогда не может вернуться null pointer, и поэтому и проверять не надо и undefined behaviour не возникнет. Всё, проблема решена. Это понятно?

                                                                                                                                                              Надеюсь Вы всё поняли, Евгений eao197? Потому что если это не так, это фиаско, братан! Это фиаско! :)

                                                                                                                                                              Как и прежде, жду от вас дельных комментариев по-существу поставленного вопроса.
                                                                                                                                                                +3
                                                                                                                                                                Обертка внутри себя проверяет результат вызова malloc(), и в случае возврата malloc()'ом значения null pointer, обертка НИКОГДА не вернет в вызвавший её код null pointer, сразу предприняв какую-то обработку, которую можно задать универсальным callback'ом или прямиком прописав в данном конкретном случае данной конкретной обертки. Она просто не вернется назад. Всё, краш, fail. Памяти нет. Отказ.
                                                                                                                                                                Опять 25. Вы хоть понимаете, что библиотека, позволяющая себе подобные вольности будет просто выкинула и замена чем-то другим? Потому что ей просто нельзя пользоваться в условиях, когда памяти не хватает (а когда её заведомо хватает и атак на вашу программу не ожидается, то никакие обёртки, собственно, не нужны — можно просто вызывать malloc и «надається на лучшее»)

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

                                                                                                                                                                В основном, понятно, нужно обсуждать «цену» такой обёртки (дополнительную память, влияние на код и прочее) — но пока примера реализации нет, а есть одно «механик руками» нет, собственно, и предмета для обсуждений.

                                                                                                                                                                Надеюсь Вы всё поняли, Евгений eao197? Потому что если это не так, это фиаско, братан! Это фиаско! :)
                                                                                                                                                                Да что ж вы опять куда-то в стратосферу устремились-то? Какой выигрыш может быть от подобного подхода — понятно всем. А вот какие могут быть побочные эффекты — нет. А они ведь разные, в зависимости от реализации вашей обёртки. В каких-то случаях они приемлемы, в каких-то — нет. Вот о чём разговор, вот почему от вас требуют конкретики. И не в виде «псевдокода в комментариях» (любимое занятие астронавтов), а виде работающего кода!
                                                                                                                                                                  –3
                                                                                                                                                                  Опять 25. Вы хоть понимаете, что библиотека, позволяющая себе подобные вольности будет просто выкинула и замена чем-то другим? Потому что ей просто нельзя пользоваться в условиях, когда памяти не хватает (а когда её заведомо хватает и атак на вашу программу не ожидается, то никакие обёртки, собственно, не нужны — можно просто вызывать malloc и «надається на лучшее»)


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

                                                                                                                                                                  В основном, понятно, нужно обсуждать «цену» такой обёртки (дополнительную память, влияние на код и прочее) — но пока примера реализации нет


                                                                                                                                                                  И не в виде «псевдокода в комментариях» (любимое занятие астронавтов), а виде работающего кода!


                                                                                                                                                                  Час моей работы стоит $50 и меньше чем на 20 часов я не размениваюсь. Предоплата в столь мелких случаях 100%. Как будете оплачивать?

                                                                                                                                                                    +2
                                                                                                                                                                    Я понимаю, полнолуние, но все же, с чего вы взяли что всенепременно следует говорить о библиотеке

                                                                                                                                                                    Чукча не читатель, чукча — писатель?

                                                                                                                                                                    Вы статью-то вообще читали, которую мы тут обсуждаем? И именно в этом контексте идёт всё обсуждение.

                                                                                                                                                                    Всё что здесь обсуждается — это проблемы только и исключительно библиотек.

                                                                                                                                                                    Цитата:
                                                                                                                                                                    Но прежде надо ответить на вопрос: «а причём здесь Chromium?».

                                                                                                                                                                    Chromium здесь при том, что в используемых в нём библиотеках имеется не менее 70 ошибок, связанных с отсутствием проверки после вызова таких функций, как malloc, calloc, realloc. Да, в самом Chromium эти функции почти нигде не используются. В Chromium применяются только контейнеры или operator new. Однако, раз ошибки есть в используемых библиотеках, то значит, можно сказать, что они есть и в Chromium. Конечно, какие-то части библиотек могут не использоваться при работе Chromium, но определять это сложно и ненужно. Всё равно надо править все ошибки.


                                                                                                                                                                    и что вам мешает реализовать callback?
                                                                                                                                                                    А вот с этого момента обсуждение, собственно, и стоит начать. Покажите пример кода, который предлагает использовать — и давайте его сравним с банальной проверкой ошибок. Не какие-то «сферические принципы в вакууме», а реальный код, который реально может захотеться засунуть в биболиотеку. Не обязательно приводить его здесь — ссылка на реализацию где-нибудь на GitHub'е тоже пойдёт.

                                                                                                                                                                    Час моей работы стоит $50 и меньше чем на 20 часов я не размениваюсь. Предоплата в столь мелких случаях 100%. Как будете оплачивать?
                                                                                                                                                                    Вас понял. Диагноз — «архитектурный астронавт, на работу не брать ни под каким соусом» принят и зафиксирован.

                                                                                                                                                                    Потому что отговорки подобного плана — это как раз типичный признак: говорить умные слова мы умеем, писать код — нет. И неважно даже: вы считаете, что вы «переросли» этот этап и теперь можете работать «учёной совой» и «разрабатывать стратегию» или никогда не умели… важно что сейчас вы код не пишите — иначе пример того, во что вы так свято верите не требовал бы 20 часов работы, а занял бы 5 минут копи-паста.
                                                                                                                                                                      0
                                                                                                                                                                      Чукча не читатель, чукча — писатель?

                                                                                                                                                                      Вы статью-то вообще читали, которую мы тут обсуждаем? И именно в этом контексте идёт всё обсуждение.

                                                                                                                                                                      Всё что здесь обсуждается — это проблемы только и исключительно библиотек.


                                                                                                                                                                      Обсуждаются здесь не «проблемы библиотек», как это может понять только странный человек, а проблема обязательности проверки malloc-подобных функция на результат вызова, и обоснование почему не проверять нельзя. Во-первых, автором указано что проверялся сам Chromium, но в нем не оказалось проблем, поскольку он, как это и предполагалось, имеет чисто C++ код. Всё, точка, ваша карта бита. А далее, анализируя Chromium, чтобы поиметь хоть какой-то улов, автор переходит к массе библиотек (а также вступал в диалог с автором библиотеки, да и советы дает автором библиотек), однако же делать отсюда алогичный вывод что это проблема якобы «ТОЛЬКО библиотек» — нельзя, это вывод по типу «посчитал количество слов, сделал вывод», смотрю в книгу — вижу фигу.
                                                                                                                                                                      Ну я лично, будучи в здравом уме и твердой памяти такого вывода о ТОЛЬКО библиотеках не сделал. Речь идет о любом коде вообще, поскольку функции аллокации памяти встречаются везде где угодно.

                                                                                                                                                                      А вот с этого момента обсуждение, собственно, и стоит начать.


                                                                                                                                                                      То есть до этого это был с вашей стороны трёп?

                                                                                                                                                                      Покажите пример кода, который предлагает использовать — и давайте его сравним с банальной проверкой ошибок. Не какие-то «сферические принципы в вакууме», а реальный код, который реально может захотеться засунуть в биболиотеку. Не обязательно приводить его здесь — ссылка на реализацию где-нибудь на GitHub'е тоже пойдёт.


                                                                                                                                                                      Я-то покажу, а вот мне хотелось бы знать, с каких это щщей вы в разговоре общаетесь со мной личными повелениями — А ПОКАЖИТЕ-КА МНЕ? Вам не кажется это нахальством или троллингом?

                                                                                                                                                                      Вас понял. Диагноз — «архитектурный астронавт, на работу не брать ни под каким соусом» принят и зафиксирован.


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

                                                                                                                                                                      Потому что отговорки подобного плана — это как раз типичный признак: говорить умные слова мы умеем, писать код — нет. И неважно даже: вы считаете, что вы «переросли» этот этап и теперь можете работать «учёной совой» и «разрабатывать стратегию» или никогда не умели… важно что сейчас вы код не пишите — иначе пример того, во что вы так свято верите не требовал бы 20 часов работы, а занял бы 5 минут копи-паста.


                                                                                                                                                                      Её-мое. Да что же это такое тут творится?
                                                                                                                                                                      Да какая вам вообще разница, кто я? Обсуждайте идеи, обсуждайте события, но не людей! См. высказывание Элеоноры Рузвельт: «Великие умы обсуждают идеи. Средние умы обсуждают события. Мелкие умы обсуждают людей.»
                                                                                                                                                                      Постыдились бы!

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

                                                                                                                                                                      // main.c
                                                                                                                                                                      #include <stdio.h>
                                                                                                                                                                      #include <stdlib.h>
                                                                                                                                                                      #include <setjmp.h>
                                                                                                                                                                      #include "testmemlib.h"
                                                                                                                                                                      
                                                                                                                                                                      static int test_function() {
                                                                                                                                                                          const int TEST_MEM_SZ = 16;
                                                                                                                                                                      
                                                                                                                                                                          unsigned char *mem1 = NULL;
                                                                                                                                                                      
                                                                                                                                                                          MALLOC_TRY 
                                                                                                                                                                      		mem1 = (unsigned char *) test_malloc(TEST_MEM_SZ);
                                                                                                                                                                          MALLOC_EXCEPT {
                                                                                                                                                                              printf("memory allocation error!\n");
                                                                                                                                                                              return 0;
                                                                                                                                                                          };
                                                                                                                                                                      
                                                                                                                                                                          // some memory usage, dump contents
                                                                                                                                                                          {
                                                                                                                                                                              int i;
                                                                                                                                                                              printf("memory dump: ");
                                                                                                                                                                              for (i = 0; i < TEST_MEM_SZ; i++)
                                                                                                                                                                                  printf("%02X,", mem1[i]);
                                                                                                                                                                              printf("\n");
                                                                                                                                                                          }
                                                                                                                                                                      
                                                                                                                                                                          test_free(mem1);
                                                                                                                                                                          return 1;
                                                                                                                                                                      }
                                                                                                                                                                      
                                                                                                                                                                      int main() {
                                                                                                                                                                          printf("begin test\n");
                                                                                                                                                                          if (test_function())
                                                                                                                                                                              printf("test completed.  well done!\n");
                                                                                                                                                                          else
                                                                                                                                                                              printf("test failed.\n");
                                                                                                                                                                          return 0;
                                                                                                                                                                      }
                                                                                                                                                                      

                                                                                                                                                                      // testmemlib.c
                                                                                                                                                                      #include <stdlib.h>
                                                                                                                                                                      #include <setjmp.h>
                                                                                                                                                                      #include "testmemlib.h"
                                                                                                                                                                      
                                                                                                                                                                      jmp_buf malloc_buf123;
                                                                                                                                                                      
                                                                                                                                                                      void *test_malloc(size_t sz) {
                                                                                                                                                                          void *ptr = malloc(sz);
                                                                                                                                                                          if (!ptr) {
                                                                                                                                                                              longjmp(malloc_buf123,1);
                                                                                                                                                                          }
                                                                                                                                                                          return ptr;
                                                                                                                                                                      }
                                                                                                                                                                      
                                                                                                                                                                      void test_free(void *ptr) {
                                                                                                                                                                          free(ptr);
                                                                                                                                                                      }
                                                                                                                                                                      

                                                                                                                                                                      // testmemlib.h
                                                                                                                                                                      #ifndef TESTMEMLIB
                                                                                                                                                                      #define TESTMEMLIB
                                                                                                                                                                      
                                                                                                                                                                      extern jmp_buf malloc_buf123;
                                                                                                                                                                      extern void *test_malloc(size_t sz);
                                                                                                                                                                      extern void test_free(void *ptr);
                                                                                                                                                                      
                                                                                                                                                                      #define MALLOC_TRY    if (!setjmp(malloc_buf123))
                                                                                                                                                                      #define MALLOC_EXCEPT else
                                                                                                                                                                      
                                                                                                                                                                      #endif
                                                                                                                                                                      


                                                                                                                                                                      — Это либка из двух файлов. Родил за полчаса.
                                                                                                                                                                      Не надо придираться, ныть о частностях и о возможных ошибках, это рождено реально, только что, за полчаса, в основном под воздействием вашего зашкаливающего троллинга. Это не является панацеей, commercial quality grade code и тестировалось методом двух запусков. На большее я пойтить не могу.

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

                                                                                                                                                                      Интересно, будет ли что-нибудь практическое по существу от вас, нетривиальный вы наш?
                                                                                                                                              +1
                                                                                                                                              Знакома ли вам формальная логика?

                                                                                                                                              Рассмотрим вашу программу. Верно ли в ней утверждение «результат каждого вызова malloc проверяется на 0»? Верно, потому что вызов malloc всего один, и его результат проверяется.

                                                                                                                                              Так с чем же вы спорите-то, ёпрст?
                                                                                                                                                0
                                                                                                                                                Мне-то это очевидно, почему я свободно и высказался.

                                                                                                                                                Но, видимо, это ясно далеко не всем моим оппонентам.

                                                                                                                                                Я тоже искренне недоумеваю, что тут можно не понимать, и о чем тут можно спорить. Но, вот поди ж ты… Можно :-)
                                                                                                                                                  0
                                                                                                                                                  Так спорите-то с этим именно вы…
                                                                                                                                                    0
                                                                                                                                                    Приведите фрагмент моего текста, в котором я «спорю» со своим же предложением вынести все malloc'и, заменив их оберткой, и заявляя что это — неравноценное решение.
                                                                                                                                                      +2

                                                                                                                                                      Самое первое же ваше сообщение:


                                                                                                                                                      Рекомендация загромождать код проверкой воспринимается мной как анти-паттерн, вредная рекомендация.

                                                                                                                                                      В нем вы, по форме, спорите с автором статьи, а автор статьи писал именно это:


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

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


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

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

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

                                                                                                                                                        Прочтите мой исходный комментарий до конца.
                                                                                                                                                        В его конце содержится ровно то, чем вы мне пеняете. Я предлагаю сделать (цитирую конец своего комментария):
                                                                                                                                                        Как выглядит более корректное решение?
                                                                                                                                                        На мой взгляд лучше не использовать эти системные вызовы напрямую. Использовать или другие библиотечные функции, или написать свои обертки и использовать их. Основная задача — оставить в коде только семантику работы с памятью, не загромождая его ничем посторонним.

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

                                                                                                                                                        Как и почему это «работает», какие у этого есть небольшие дополнительные преимущества, а также краткую иллюстрацию си-кодом вы найдете в этой ветке комментариев, там же я кратко выделил всю суть дела (повторно цитирую): Andrey2008 объяснил почему результат вызова malloc() проверять надо, и аргументировал это в общем случае UB в случае отказа функции вернуть память и возврата null pointer. Я на это дополнил тем, что не совсем красиво будет ставить везде проверки, и что это функцию лучше обернуть проверкой, создав условие что null pointer никогда не вернется, также как это делает xmalloc.

                                                                                                                                                        Разумеется, я понимаю что проверки в одном месте (в обертке) в целом изоморфны проверкам в каждом месте после вызова.
                                                                                                                                                        Можно добиться как полностью сходного поведения программ, так и различного, никакой автоматической «панацеей» данное решение не является.

                                                                                                                                                        Помимо этого иметь под «рукой» свою замену malloc (а-ля «перехваченный») очень удобно исходя из массы других соображений. У меня это must have.
                                                                                                                                                          +2
                                                                                                                                                          Ключевая мысль: оставить в коде только семантику работы с памятью, не загромождая его ничем посторонним.
                                                                                                                                                          Прекрасно, великолепно, супер! Мыши, станьте ёжиками!

                                                                                                                                                          А теперь вспомним, наконец, о том, что мы говорим о библиотеках (в коде Хромиума, где мы можем контролировать всё и вся, разумеется в основном обёртки и используются), то есть у нас есть два дополнительных ограничения:
                                                                                                                                                          1. Библиотеки не должна обрушивать всю программу целиком — во многих программах подобное поведение просто недопустимо.
                                                                                                                                                          2. Использовать исключения также нельзя, потому что на многих платформах и во многих организациях их использование запрещено.

                                                                                                                                                          Что и переводит нашу проблему в практическую плоскость: как, собственно, вы собираетесь писать вашу обёртку, чтобы удовлетворить этим самоочевидным требованиям.

                                                                                                                                                          Пока ничего, кроме словоблудия и «махания руками» вы не предложили — а ведь это ключевой момент! Все виденные мною техники очень серьёзно влияют на программу и во многих случаях их использование было менее удобно, чем навешивание атрибута warn_unused_result на malloc и компиляция с -Werror.

                                                                                                                                                          Заметьте: в отличие от вас я, как раз, знаю несколько способов это сделать — но также вижу, что это не панацея. Вы, в отличие от меня, категорично заявляете, что это — единственный возможный подход, что немедленно вызывает реакцию Architecture astronautus detected, fire protocol enabled
                                                                                                                                                            –3
                                                                                                                                                            Что и переводит нашу проблему в практическую плоскость: как, собственно, вы собираетесь писать вашу обёртку, чтобы удовлетворить этим самоочевидным требованиям.

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

                                                                                                                                                            Кто вам сказал, что в комментариях под чужой тематической статьёй я вообще, должен предлагать какие-то замечательные универсальные решения? Это вы с чего взяли? От большого ума?

                                                                                                                                                            Врать что я ничего не предлагал не надо.
                                                                                                                                                            Я предложил то, что посильно сделать в комментарии — самое первейшее решение a-must-have (задел на будущее) — это не обращаться НАПРЯМУЮ к malloc-подобным функциям, предусматривая в качестве задела нечто своё. Это первое что я написал в первом же сообщении! Первый и весьма разумный шаг.

                                                                                                                                                            В чем проблема? С чем спорим? Это что, как-то хуже? Нет, в простейшем случае это полностью равноценное решение прямому вызову malloc'а. Но зато чуть позже (вместе с аналогичным использованием своей free) — это даст ряд «вкусностей».

                                                                                                                                                            Это простейшее первичное решение «обёртка», вообще говоря, ничто иное, как собственный менеджер памяти. Со всеми вытекающими. В простейшем случае, как в комментарии — он заглушка, чекающая результат malloc. Далее у него можно спросить хэндл закладки для серии аллокаций с тем чтобы выйти из ряда вызовов, освобождая цепочки аллоцированных блоков. Можно попросить free(ptr1, ptr2); — т.е. «освободи-ка разом всю память что была между аллокациями такими-то и такими-то».

                                                                                                                                                            Заметьте: в отличие от вас я, как раз, знаю несколько способов это сделать Вы, в отличие от меня,


                                                                                                                                                            Да, с ЧСВ у вас всё в порядке. Прям интересно узнать — вы это такие выводы на базе чего делаете?
                                                                                                                                                            Вообще, годам к 30-ти должно уже отпускать кто «лучшее», а кто нет. Кто чего там «знает». На западе я тут вообще не замечал пальцев веером.

                                                                                                                                                            Но раз вы обозначились, охотно верю, поведайте несколько способов это сделать. Буду ждать.

                                                                                                                                                            категорично заявляете, что это — единственный возможный подход, что немедленно вызывает реакцию Architecture astronautus detected, fire protocol enabled


                                                                                                                                                            Зачем вы врёте?
                                                                                                                                                            Приведите ссылку на мое сообщение, где бы я © «категорично заявлял» что это единственный подход.

                                                                                                                                                            Исходная мысль была очень проста — вообще-то, размножать повсеместно один и тот же код не следует.
                                                                                                                                                            https://en.wikipedia.org/wiki/Don't repeat yourself
                                                                                                                                                            И следует подумать как от этого избавиться наиболее элегантным способом.

                                                                                                                                                            Чего вы хотите от комментариев?
                                                                                                                                                            Я привел самое простейшее и самое прямолинейное, очевидное решение. Не лучшее, просто пример. ЭТО НЕ ЗНАЧИТ ЧТО ЭТО ПАНАЦЕЯ НА ВЕКИ ВЕКОВ, ДЛЯ ВСЕХ СЛУЧАЕВ И ВОЛШЕБНОЕ РЕШЕНИЕ — кто так думает и кидается с пеной это обсуждать… ну… у меня плохие новости…

                                                                                                                                                            Не надо никого хватать за горло и тут же требовать волшебной панацеи, применимой всегда и везде, и причем написать это тут же, в комментах. И объяснять что это невозможно тоже не надо, надо уметь общаться на определенном уровне.
                                                                                                                                                              –1
                                                                                                                                                              Я предложил то, что посильно сделать в комментарии — самое первейшее решение a-must-have (задел на будущее) — это не обращаться НАПРЯМУЮ к malloc-подобным функциям, предусматривая в качестве задела нечто своё. Это первое что я написал в первом же сообщении! Первый и весьма разумный шаг.
                                                                                                                                                              Вообще-то подобный шаг должен быть последним — после того, как вы посмотрели на альтернативы и убедились, что вариант с проверками в вашем конкретном случае действительно неприемлем.

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

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