Можно ли вызвать memset () с нулевым указателем, если размер равен 0? - PullRequest
15 голосов
/ 22 декабря 2011

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

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

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

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

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

Примерно так:

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

Ответы [ 3 ]

9 голосов
/ 22 декабря 2011

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

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

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

6 голосов
/ 22 декабря 2011

Редактировать Re:

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

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

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

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

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

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

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

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

4 голосов
/ 23 декабря 2011

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

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

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

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

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

7.21.6.1 «Функция memset»

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

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

...