18

出于某种原因,我想手动滚动一个归零版本的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();
}

4

3 回答 3

10

这是 glibc 声明:

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

__nonnull表明它期望指针为非空。

于 2011-12-21T22:15:02.550 回答
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(转换为) 的值复制到指向的对象的unsigned char每个第一个字符中。ns

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

于 2011-12-22T20:39:26.697 回答
7

编辑回复:

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

我懂了。因此,如果指针为空且大小为 0 ,您只希望事情是安全的。

参考 POSIX 文档

,没有指定用空指针调用应该是安全memset的(如果你用零计数或大小调用它......那会更“有趣”,但也没有指定)。

在“信息性”部分中甚至没有提到它。

请注意,第一个链接提到

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

更新我可以确认 ISO C99 标准 (n1256.pdf) 与 POSIX 文档C++11 规范一样简短,只是参考了 ANSI C 标准memset和朋友。N1256 状态:

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

并没有说明snull 的情况(但请注意,空指针不指向object)。

于 2011-12-21T22:09:46.123 回答