Проверьте errno для ENOMEM, вместо сравнения серии вызовов malloc с NULL - PullRequest
1 голос
/ 28 марта 2019

Обычно один тест проверяет, чтобы результат malloc не был NULL, чтобы узнать, успешно ли выделено память. При серии вызовов malloc это становится длительным или утомительным набором сравнений.

Вместо этого можно было бы установить errno = 0 в верхней части серии вызовов malloc, а затем проверить на errno == ENOMEM в конце?
Предполагается, что если какое-либо распределение завершится неудачно, программа или функция не смогут продолжить работу и должны вернуться / выручить. Также предполагается, что вызовы malloc являются последовательными и непрерывными, и что, согласно руководству, malloc может установить только errno на ENOMEM.

Примером может быть что-то вроде следующего кода:

#include <stdlib.h>
#include <errno.h>
#include <stdio.h>

#define N (1 << 20)

int main()
{
        double *a = NULL;
        double *b = NULL;
        double *c = NULL;

        errno = 0;
        a = malloc(N * sizeof *a);
        b = malloc(N * sizeof *b);
        c = malloc(N * sizeof *c);
        if (errno == ENOMEM) {
                perror(NULL);
                free(a);
                free(b);
                free(c);
                return EXIT_FAILURE;
        }
        errno = 0;

        /* Do interesting stuff */

        free(a);
        free(b);
        free(c);
        return EXIT_SUCCESS;
}

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

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

Ответы [ 2 ]

2 голосов
/ 28 марта 2019

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

За 7,5 Ошибки <errno.h>, пункт 3 стандарта C:

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

Так как malloc() не задокументировано для установки errno по стандарту C, при успехе можно без промедления errno.

1 голос
/ 28 марта 2019

Вместо этого можно было бы установить errno = 0 в верхней части серии вызовов malloc, а затем проверить errno == ENOMEM в конце?

Да, вы могли бы так долгопоскольку ваша реализация malloc задокументирована для установки errno на ENOMEM.Спецификация в стандарте C11 (§ 7.22.3.4) только упоминает, что возвращаемый указатель будет NULL, а не errno, поэтому ваш код технически не переносим.

Реализация по умолчанию malloc в macOS, Windows и Linux действительно устанавливает errno, так что это большая часть компьютеров в Мире.Однако, если требуется истинная переносимость, в конце просто напишите

if (a == NULL || b == NULL || c == NULL)
{
    // Handle the failure
}

Приложение: Нет необходимости сбрасывать errno обратно в ноль после malloc s.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...