出于这样或那样的原因,我想手工滚动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();
}
下面是glibc声明:
extern void *memset (void *__s, int __c, size_t __n) __THROW __nonnull ((1));
__nonnull
显示它期望指针非空
C99标准是这么说的:
7.1.4"库函数的使用"
如果函数的实参有一个无效的值(例如函数域之外的值,或者程序地址空间之外的指针,或者空指针,或者当相应的形参不是const限定时指向不可修改存储的指针),或者具有可变数量实参的函数不期望的类型(在提升之后),则行为是未定义的。
7.21.1 String函数约定;(记住memset()
在string.h
中)
如果声明为
size_t n
的实参指定了函数的数组长度,则在调用该函数时n
的值可以为0。除非在本小节中特定函数的描述中另有明确说明,否则此类调用的指针实参仍应具有有效值,如7.1.4所述。
7.21.6.1"memset函数"
memset
函数将c
的值(转换为unsigned char
)复制到s
所指向对象的前一个n
字符中。
所以严格来说,由于标准规定s
必须指向一个对象,传递一个空指针将是UB。添加检查(与malloc()
相比,成本将非常小)。另一方面,如果您知道malloc()
不会失败(因为您有一个自定义的终止),那么显然您不需要在调用memset()
之前执行检查。
Edit Re:
我后来添加了这一点:假设
malloc()
永远不会失败。问题是size是否可以为0
我明白了。所以只有当指针为空和的大小为0时,你才希望事情是安全的。
引用POSIX文档
- http://pubs.opengroup.org/onlinepubs/009695399/functions/memset.html
- http://pubs.opengroup.org/onlinepubs/7908799/xsh/memset.html
没有,没有指定使用空指针调用memset
应该是安全的(如果您以零计数或大小调用它…那样会更"有趣",但也没有特别说明)。
在"信息"部分甚至没有提到它。
注意,第一个链接提到了
本参考页描述的功能与ISO C标准保持一致。此处描述的要求与ISO C标准之间的任何冲突都是无意的。本卷IEEE Std 1003.1-2001遵循ISO C标准
更新我可以确认ISO C99标准(n1256.pdf)与POSIX文档和一样简短,c++ 11规范只是参考了memset
和朋友的ANSI C标准。N1256州:
memset
函数将c
的值(转换为unsigned char
)复制到s
所指向对象的前一个n
字符中。
并没有说明s
为空的情况(但请注意,空指针不指向对象)。