V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
africwildman
V2EX  ›  C

用 c 分配内存,为什么普遍都是用 malloc 而不是用 calloc?

  •  
  •   africwildman · 2019-06-10 21:31:30 +08:00 · 8618 次点击
    这是一个创建于 2028 天前的主题,其中的信息可能已经有所发展或是发生改变。

    用 c 分配内存,为什么普遍都是用 malloc 而不是用 calloc ? calloc 会初始化为 0,更安全啊?《深入理解 c 指针》里,我记得也是 malloc 用的多。百度了一下,没看到比较好的解释。我是用 calloc。

    23 条回复    2019-06-13 20:08:09 +08:00
    junkun
        1
    junkun  
       2019-06-10 21:41:55 +08:00   ❤️ 2
    实际应用中可能申请之后,就直接赋值或初始化了,或者今后会初始化(比如申请内存做栈,没使用的部分就未初始化)。calloc 直接赋值为 0,很可能是不必要的,尤其在申请空间较大的情况下,尤其是一些老的编译器可能不会做这样的优化。
    zhuangzhuang1988
        2
    zhuangzhuang1988  
       2019-06-10 22:01:45 +08:00
    实际上你看开源的库都是有个自己 Memory 包装的
    里面想怎么玩怎么玩
    zmj1316
        3
    zmj1316  
       2019-06-10 22:13:04 +08:00 via Android
    性能 填 0 也没什么意义
    vs 里面 debug 下默认就是填 0xcc 更不容易搞错
    pkookp8
        4
    pkookp8  
       2019-06-10 23:11:51 +08:00 via Android   ❤️ 1
    我觉得大家学习的时候第一个接受到的是 malloc,并且被教导使用未初始化的内存是很危险的,这个可能也是一个原因,所以大家常用 malloc
    malloc 是 memory alloction,也比较好记
    平时看到代码中也有 malloc+memset 的写法,应该是写的人不知道 calloc 的功能
    其实现代编译器和芯片不会真的因为 malloc+memset 改成 calloc 性能就上升一大截,如果是的话还是从考虑优化代码角度考虑
    msg7086
        5
    msg7086  
       2019-06-11 01:45:21 +08:00 via Android
    甚至 memset 可以用优化过速度更快的版本。
    kingcos
        6
    kingcos  
       2019-06-11 01:54:10 +08:00 via iPhone
    有同样的疑问。
    autogen
        7
    autogen  
       2019-06-11 02:01:57 +08:00
    所以才会有红红火火恍恍惚惚烫烫烫烫烫。。。。
    wheeler
        8
    wheeler  
       2019-06-11 08:01:43 +08:00   ❤️ 1
    个人觉得主要是 0 字节作为的初始化的场景不是那么多。比如 NULL 指针,浮点数 0.0,C 标准都没有规定它们的底层表示是 0 字节。

    https://stackoverflow.com/questions/1538420/difference-between-malloc-and-calloc

    malloc 比 calloc 快也不一定对,这与实现有关系。

    void *malloc(size_t size);
    void *calloc(size_t nmemb, size_t size);

    calloc 相对于 malloc 可能的一点好处是:
    calloc 的函数原型是 nmem + member_size 的形式,而如果用 malloc 的话得:

    malloc(nmem * member_size);

    这时得考虑无符号乘法溢出的问题了,用 calloc 的话也不是一点问题没有,还是得看实现。见:

    https://wiki.sei.cmu.edu/confluence/display/c/MEM07-C.+Ensure+that+the+arguments+to+calloc%28%29%2C+when+multiplied%2C+do+not+wrap
    0x11901
        9
    0x11901  
       2019-06-11 08:21:15 +08:00
    书上和老师这么教的。
    0x11901
        10
    0x11901  
       2019-06-11 08:22:53 +08:00
    @zmj1316 讲道理 calloc 相比 malloc memset 性能更高,但主要看编译器实现,理论上是一样的
    pwrliang
        11
    pwrliang  
       2019-06-11 08:30:40 +08:00
    我认为大多场景都是申请空间,然后灌满,再读取多次。主动填 0,会不会在大多数场景都影响性能?
    urmyfaith
        12
    urmyfaith  
       2019-06-11 08:55:57 +08:00
    在需要填 0 的时候,就是用 calloc;

    在不关系初始值,或者明确会覆盖的话,直接 malloc 不是更省操作.
    urmyfaith
        13
    urmyfaith  
       2019-06-11 08:57:31 +08:00
    另外,很多人可能不知道有 calloc 这个东西.

    导致 malloc + memset 或者 malloc + bzero 之类的.
    xdeng
        14
    xdeng  
       2019-06-11 09:22:21 +08:00
    当你明确知道要用多大空间覆盖多少内容的时候,申请时去填 0 简直是多此一举。
    zycpp
        15
    zycpp  
       2019-06-11 10:23:49 +08:00 via iPhone
    C 语言的大原则是程序员知道自己在干什么,
    在这个前提下,也就没必要初始化了,
    junkman
        16
    junkman  
       2019-06-11 10:32:20 +08:00   ❤️ 2
    @wheeler 赞同

    实际上,calloc 的实现在多数操作系统的实现和 malloc 几乎一样快,比如 Linux/FreeBSD 内核可能会直接返回 pre zero-mapped pages。

    > You're likely to still see a performance improvement without overcommit. The OS will try to zero free pages in the background, so there's a good chance that it'll have pre-zeroed pages to hand you when you ask for them, rather than making you wait for them to be zeroed on demand.
    Of course, there are plenty of systems where this doesn't happen, or there are no pages in the first place, or there's no kernel zeroing stuff for you.

    回到问题本身,我觉得这个可能是历史原因,早期 calloc 作为一个库函数用于消除平台之间内存分配的差异,当初实现初始化零也是直接按字节填零操作的,可能是出于防御性编程的考虑(当时可能还没有这种概念),方便 debug。可能和 @wheeler 所说的类似,零内存使用场景并不大,而且清零还费时(当时机器性能可不如现在这么强大),malloc 孕育而生。大部分情况下申请内存之后我们都会填入一些数据,这样来看,使用 malloc 可以获得 performance agin。

    > So the (slightly modified) question still stands: Why do calloc and malloc exist? Indeed it looks like calloc was originally intended as a portable way to allocate memory. It used the function alloc which apparently was not meant to be used directly; most iolib functions have a 'c' tacked on. So when iolib was reworked into the stdlib why was calloc kept? saretired suspects backward compatibility but I don't believe this, because no other c-prefixed iolib function was kept and i couldn't find any code that actually used calloc in the v6 distribution either. So maybe whoever is responsible for malloc/calloc in v7 (I think it was ken, not dmr) thought malloc should be a public function but saw a use for calloc and changed the semantics to be a bit more predictable.

    see:
    https://news.ycombinator.com/item?id=13108434

    如果什么地方说的不对,麻烦指正。:-P
    junkman
        17
    junkman  
       2019-06-11 10:33:28 +08:00
    performance agin -> performance gain
    xuddk727
        18
    xuddk727  
       2019-06-11 10:42:58 +08:00
    做下位机开发的不知道什么情况,我只知道桌面开发一般都会有自己的内存分配策略,因为一些情况下可能得根据分页情况做性能优化,另一种是使用内存池,所以很少见,当然不是没有人用,不然何来屯屯屯屯屯屯和烫烫烫烫烫烫
    africwildman
        19
    africwildman  
    OP
       2019-06-11 11:14:55 +08:00
    听了大家讨论,很受教啊。
    wutiantong
        20
    wutiantong  
       2019-06-11 11:21:52 +08:00
    @wheeler 感谢分享
    Yggdroot
        21
    Yggdroot  
       2019-06-11 14:53:43 +08:00
    先问是不是,再问为什么。难道不是 prefer calloc to malloc?
    tigereatsheep
        22
    tigereatsheep  
       2019-06-13 20:04:26 +08:00
    如果让我来实现编译器
    在 32 位的机器上 void *malloc(size_t size) 函数可以简单地把 SP 寄存器中的值加了 size * 4,
    而 void *calloc(size_t nmemb, size_t size) 函数还需要把原 SP~SP+size * 4 地址中的内存值都初始化一下,
    那么 malloc 会快一些,但是一些细心的码农在做编译器的时候可能还会做一些保护之类的事,这样效率就不好说了
    tigereatsheep
        23
    tigereatsheep  
       2019-06-13 20:08:09 +08:00
    说错了,应该是 FP 寄存器。。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2640 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 10:11 · PVG 18:11 · LAX 02:11 · JFK 05:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.