Может ли memset() вызываться с нулевым указателем, если размер равен 0?

По той или иной причине я хочу вручную свернуть нулевую версию malloc(). Чтобы свести к минимуму алгоритмическую сложность, я хочу написать:

void * my_calloc(size_t size)
{
    return memset(malloc(size), 0, size);
}

Является ли это корректным, когда size == 0? Хорошо называть malloc() нулевым размером, но это позволяет ему возвращать нулевой указатель. Будет ли следующий вызов memset в порядке или это поведение undefined, и мне нужно добавить условный if (size)?

Я бы очень хотел избежать избыточных условных проверок!

Предположим, что malloc() не терпит неудачу. На самом деле там будет и ручная версия malloc(), которая завершится с ошибкой.

Что-то вроде этого:

void * my_malloc(size_t size)
{
    void * const p = malloc(size);
    if (p || 0 == size) return p;
    terminate();
}

суб >

Ответы

Ответ 1

Вот объявление glibc:

extern void *memset (void *__s, int __c, size_t __n) __THROW __nonnull ((1));

__nonnull показывает, что он ожидает, что указатель будет не нулевым.

Ответ 2

Изменить Re:

Я добавил это позже: предположим, что malloc() никогда не прерывается. Вопрос только в том, что размер может быть 0

Я вижу. Таким образом, вы хотите, чтобы все было безопасно, если указатель равен NULL и, размер равен 0.

Ссылаясь на документы POSIX

Нет, не указано, что безопасно вызывать memset с помощью указателя NULL (если вы назвали его нулевым числом или размером... это было бы еще более "интересным", но также не указано).

Ничего об этом даже не упоминается в "информативных" разделах.

Обратите внимание, что в первой ссылке упоминается

Функциональность, описанная на этой справочной странице, соответствует стандарту ISO C. Любой конфликт между описанными здесь требованиями и стандартом ISO C является непреднамеренным. Этот объем IEEE Std 1003.1-2001 относится к стандарту ISO C

Обновление. Я могу подтвердить, что стандарт ISO C99 (n1256.pdf) не менее краток, поскольку POSIX docs и спецификация С++ 11 относится только к ansi C для memset и друзей.

Ответ 3

Вот что говорит об этом стандарт C99:

7.1.4 "Использование библиотечных функций

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

7.21.1 "Соглашения о строковых функциях" (помните, что memset() находится в string.h)

Если аргумент, объявленный как size_t n, задает длину массива для функции, n может иметь значение 0 при вызове этой функции. Если явно не указано иначе в описании конкретной функции в этом подпункте, аргументы указателя на такой вызов все равно будут иметь допустимые значения, как описано в 7.1.4.

7.21.6.1 "Функция memset"

Функция memset копирует значение c (преобразуется в unsigned char) в каждый из первых n символов объекта, на который указывает s.

Так строго говоря, так как стандарт указывает, что s должен указывать на объект, передавая нулевой указатель, будет UB. Добавьте проверку (стоимость по сравнению с malloc() будет исчезающе мала). С другой стороны, если вы знаете, что malloc() не может потерпеть неудачу (потому что у вас есть пользовательский, который завершается), то, очевидно, вам не нужно проверять проверку перед вызовом memset().