{0}和calloc之间的区别?

时间:2014-10-30 22:07:37

标签: c

this answer on another question所述,使用聚合初始化

struct foo {
    size_t a;
    size_t b;
};

struct foo bar = {0};

导致内置类型初始化为零。

使用上述和使用

之间有什么区别吗?
struct foo * bar2 = calloc(1, sizeof(struct foo));

撇开一个变量是指针的事实 查看调试器,我们可以看到,对于上述两个示例,ab都确实设置为零。

上述两个例子之间有什么区别,是否存在任何问题或隐藏的问题?

5 个答案:

答案 0 :(得分:4)

是的,除了struct foo类型的对象的存储类外,还有一个重要区别:

struct foo bar = {0};
struct foo * bar2 = calloc(1, sizeof *bar2);

bar的每个成员都是零初始化的(如果没有初始值设定项,或者bar属于staticthread_local存储,则填充将归零-class),
虽然*bar2的所有内容都已清零,但可能会产生完全不同的结果:

空指针(T*)0和值为0的浮点数都不能保证为全位-0。
(实际上,仅适用于charunsigned charsigned char(以及<stdint.h>中的一些可选的精确大小类型),保证全部位 - 0匹配值-0直到C99之后的某个时间。后来的技术勘误保证了所有积分类型。)

浮点格式可能不是IEEE754。

(在大多数现代系统中,你可以忽略这种可能性。)

引自c-faq(感谢Jim Balter for linking it):

  

Prime 50系列使用段07777,空指针偏移0 ,至少对于PL / I。

答案 1 :(得分:3)

calloc为您提供了一个堆dynamically allocated归零内存区域(进入bar2)。但是在call stack上分配了一个自动变量(如bar,假设其声明在函数内)。另请参阅calloc(3)

在C中,您需要显式free堆分配的内存区域。但是当函数返回时会弹出堆栈分配的数据。

Rerad还在C dynamic memory allocationgarbage collection上播放了wikipage。 Reference counting是C和C ++中广泛使用的技术,可以视为GC的一种形式。想想circular references,它们很难处理。

Boehm conservative GC可用于C程序。

请注意,内存区域的活跃度是全局程序范围的属性。您通常不能声称给定区域属于特定功能(或库)。但你可以采用约定

当你编写一个返回堆分配指针的函数(即指向动态存储的指针)时,你应该记录这个事实并决定谁负责释放它。

关于初始化:calloc指针归零(当calloc成功时)。初始化为{0}的自动变量也归零。在实践中,一些实现可能calloc不同的大对象(通过向内核请求整个归零页面,例如使用mmap(2))和小对象(通过重用,如果可用,先前{{1} } -d zone并将其归零)。零区域使用快速等效的memset(3)

PS。我忽略了奇怪的机器,其中所有零位存储区不是C标准的清除数据,即像free。我在实践中不知道这样的机器,即使我知道它们原则上是可能的(理论上{0}指针可能不是一个全零位的字)

BTW,编译器可以优化一个全零的本地结构(并且可能根本不在堆栈上分配它,因为它适合寄存器)。

答案 2 :(得分:3)

struct foo bar = {0};

这定义了一个名为struct foo的{​​{1}}类型的对象,并将其初始化为零。

&#34;零&#34;是递归定义的。所有整数子对象都初始化为bar,所有浮点子对象都初始化为0,所有指针都初始化为0.0

NULL

恕我直言,这更好(但等效地)写成:

struct foo * bar2 = calloc(1, sizeof(struct foo));

如果不重复类型名称,我们可以避免以后更改代码时出现不匹配的风险。

这会动态分配类型为struct foo *bar2 = calloc(1, sizeof *bar2); 的对象(在堆上),将该对象初始化为所有位为零,并初始化struct foo以指向它。

bar2无法分配内存。如果是,则返回空指针。你应该经常检查一下。 (calloc的声明也会分配内存,但是如果它失败则会导致堆栈溢出,并且没有好办法处理它。)

并且所有位零是保证与&#34;零&#34;相同。对于整数类型(包括bar),它几乎得到保证。对于浮点数和指针类型,size_t0.0完全合法,除了所有位零之外还有一些内部表示。你不太可能碰到这个,因为你结构的所有成员都是整数,你可能不需要担心它。

答案 3 :(得分:1)

(这个答案侧重于初始化的差异,如果struct仅包含整数类型)

这两种表单都将ab设置为0。这是因为标准定义了整数类型的所有位零必须表示0的值。

如果存在结构填充,则calloc版本设置该值,但零初始化可能不会。例如:

struct foo a = { 0 }, b = { 0 };
struct foo c, d; memset(&c, 0, sizeof c); memset(&d, 0, sizeof d);

if ( memcmp(&a, &b, sizeof a) )
    printf("This line may appear.\n");

if ( memcmp(&c, &d, sizeof c) )
    printf("This line must not appear.\n");

您有时会看到的一种技术(特别是在设计用于存储少量存储的系统中的代码)是使用memcmp来比较两个结构的相等性。当结构构件之间存在填充时,这是不可靠的,因为即使结构构件是相同的,填充也可能不同。

程序员并不想单独比较结构成员,因为代码太大,所以相反,他会使用memcpy复制结构,使用memset初始化它们;为了保留使用memcmp检查相等性的能力。


在现代编程中,我强烈建议不要这样做;并始终使用{ 0 }形式的初始化。后者的另一个好处是没有机会在size参数中出错,并且意外地设置了太多的内存或太少的内存。

答案 4 :(得分:0)

存在一个严重的区别:自动变量的分配在编译时完成并且是免费的(当保留堆栈帧时,房间就在那里。)相反,动态分配在运行时完成,具有不可预测和不可忽视的成本。

关于初始化,编译器有机会使用自动变量进行优化(例如,如果不需要则不清除);调用calloc是不可能的。

如果您喜欢calloc样式,还可以选择在自动变量上执行memset。

memset(&bar, 0, sizeof bar);

更新:自动变量的分配是在编译时准备完成的。