如果大小为0,可以使用空指针调用memset()吗?

Ker*_* SB 15 c malloc memset

出于这样或那样的原因,我想手动滚动归零版本malloc().为了最大限度地减少算法的复杂性,我想写:

void * my_calloc(size_t size)
{
    return memset(malloc(size), 0, size);
}
Run Code Online (Sandbox Code Playgroud)

这个定义明确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();
}
Run Code Online (Sandbox Code Playgroud)

Pub*_*bby 9

这是glibc声明:

extern void *memset (void *__s, int __c, size_t __n) __THROW __nonnull ((1));
Run Code Online (Sandbox Code Playgroud)

__nonnull,它期待着指针非空的节目.


Mic*_*urr 8

以下是 C99 标准对此的说明:

7.1.4 “库函数的使用”

如果函数的参数具有无效值(例如函数域外的值,或程序地址空间外的指针,或空指针,或指向不可修改存储的指针)不是 const 限定的)或具有可变数量参数的函数不期望的类型(升级后),行为未定义。

7.21.1“字符串函数约定”(记住memset()是在 string.h

如果参数声明为size_t n指定函数数组的长度,则n在调用该函数时可以具有零值。除非在本小节中对特定函数的描述中另有明确说明,否则此类调用的指针参数仍应具有有效值,如 7.1.4 中所述。

7.21.6.1 “memset 函数”

memset函数将 的值c(转换为 an unsigned char)复制到n指向的对象的每个第一个字符中s

所以严格来说,由于标准规定s必须指向一个对象,所以传入一个空指针就是UB。添加支票(与 相比,成本malloc()将小得惊人)。另一方面,如果您知道malloc()不能失败(因为您有一个终止的自定义),那么显然您不需要在调用memset().

  • @Kerrick:我对标准的理解是,如果您将无效指针(包括“NULL”)传递给“memset()”,即使大小为“0”,它也是 UB。我想在大多数实现中它会像人们想要的那样工作(如果 `size == 0`,`memset()` 是一个 nop),但这只是 UB '工作' 的一个快乐巧合。此外,我认为不执行检查的性能参数很弱 - 将内存块归零将主导此函数的用例的性能,这应该更有可能按数量级计算:当 `size > 0` 时。 (3认同)

seh*_*ehe 6

编辑 Re:

我后来添加了这个:假设malloc()永远不会失败.问题是只有大小可以为0

我知道了.因此,如果指针为NULL 大小为0,则只希望事物安全.

参考POSIX文档

,没有指定使用NULL指针调用memset应该是安全的(如果你用零计数或大小调用它...那就更有趣',但也没有指定).

在"信息"部分甚至没有提及它.

请注意,第一个链接提到

此参考页面上描述的功能与ISO C标准一致.此处描述的要求与ISO C标准之间的任何冲突都是无意的.IEEE Std 1003.1-2001的该卷符合ISO C标准

更新我可以确认ISO C99标准(n1256.pdf)与POSIX文档同样简短, C++ 11规范仅涉及ansi C标准memset和朋友.

  • 所以总而言之...... `memset(NULL, 0, 0)` 是还是不是 UB? (2认同)