我研究了某个库的代码,并注意到对calloc
的调用后面是memset
,用于calloc
分配的块。
我发现这个问题对calloc
和malloc
+ memset
之间的区别给出了相当全面的答案,并在分配存储之前调用memset
:
Why malloc+memset is slower than calloc?
我仍然不明白的是为什么有人想要这么做。 此操作有什么好处?
上述库的代码示例:
light_pcapng_file_info *light_create_default_file_info()
{
light_pcapng_file_info *default_file_info = calloc(1, sizeof(light_pcapng_file_info));
memset(default_file_info, 0, sizeof(light_pcapng_file_info));
default_file_info->major_version = 1;
return default_file_info;
}
已分配结构的代码(每个数组包含32个元素):
typedef struct _light_pcapng_file_info {
uint16_t major_version;
uint16_t minor_version;
char *file_comment;
size_t file_comment_size;
char *hardware_desc;
size_t hardware_desc_size;
char *os_desc;
size_t os_desc_size;
char *user_app_desc;
size_t user_app_desc_size;
size_t interface_block_count;
uint16_t link_types[MAX_SUPPORTED_INTERFACE_BLOCKS];
double timestamp_resolution[MAX_SUPPORTED_INTERFACE_BLOCKS];
} light_pcapng_file_info;
编辑:
除了可接受的答案外,我还想提供一些同事指出的信息。 glibc中存在一个错误,有时会阻止calloc将内存清零。这里是链接: https://bugzilla.redhat.com/show_bug.cgi?id=1293976
当链接被移动时,实际的错误报告文本:
glibc:calloc()返回非零的内存
问题描述:
在Facebook,我们有一个从glibc-2.12-1.149.el6.x86_64转到glibc-2.12-1.163.el6.x86_64时奇怪地挂起并崩溃的应用程序。原来是这个补丁
glibc-rh1066724.patch
介绍了问题。
您将以下位添加到_int_malloc()
/* There are no usable arenas. Fall back to sysmalloc to get a chunk from
mmap. */
if (__glibc_unlikely (av == NULL))
{
void *p = sYSMALLOc (nb, av);
if (p != NULL)
alloc_perturb (p, bytes);
return p;
}
但这不是可以的,alloc_perturb无条件地将内存集的前字节设置为0xf,这与上游情况不同,它会检查perturb_byte是否已设置。需要更改为
if (p != NULL && && __builtin_expect(perturb_byte, 0))
alloc_perturb (p, bytes);
return p;
我随附的补丁可以为我解决问题。
以下事实加剧了这个问题:竞技场上的任何类型的锁争用都会导致我们退回到mmap()的新块上。这是因为我们检查看看我们检查的无人竞技场是否已损坏,如果是循环的,则循环进行,如果循环到开始,则知道没有找到任何东西。除非我们最初的竞技场没有真正损坏,否则我们仍然会返回NULL,因此我们会更频繁地使用mmap()这个东西,这确实使事情变得不稳定。
请尽快解决此问题,我什至将其称为可能的安全问题。
答案 0 :(得分:7)
调用memset()
可确保操作系统实际上在进行虚拟内存映射。如您所链接问题的答案中所述,可以calloc()
进行优化,以便推迟实际的内存映射。
该应用程序可能有理由不推迟实际创建虚拟内存映射-例如使用缓冲区从超高速设备读取数据,尽管在使用{{ 1}}将内存清零,使用memset()
代替calloc()
似乎是多余的。
答案 1 :(得分:2)
没有人是完美的,仅此而已。是的,memset
之后的calloc
为零是奢侈的。如果要使用显式memset
来确保拥有所需的内存,则应改用malloc
。
新代码每1000行大约有20个错误,概率法则表明并非全部被淘汰。另外,这并不是真正的错误,因为没有不良行为要观察。
答案 2 :(得分:1)
我将其称为错误,因为它正在进行毫无意义的工作,所以指定calloc()
来返回已经清除的内存,为什么还要再次清除它呢?当有人从malloc()
切换时,重构可能失败。
如果代码是开源的和/或在您有权访问的存储库中,我将检查这些行的提交历史记录,并查看发生了什么。幸运的是,有一条提交消息可以告诉您动机……