【问题标题】:Why did coder assign value of struct pointer to a static struct?为什么编码器将结构指针的值分配给静态结构?
【发布时间】:2021-09-09 04:06:30
【问题描述】:

抱歉,如果这是重复的,在这种情况下我找不到正确的关键字来搜索。

这是对我正在处理的一些旧 (MUD) 代码的引用,该代码粘贴在下面。我对下面代码的foo_zero*foo = foo_zero 部分的用途感到困惑。这是它在整个代码库中使用的模式。我的猜测是,这是一种无需显式设置即可将foo 的所有成员初始化为零/NULL 的方法。

typedef struct FOO {
    int buzz;
    char *bazz;
} FOO;

FOO *init_foo(void)
{
    static FOO foo_zero;
    FOO *foo;
    
    foo = malloc(sizeof(*foo));
    *foo = foo_zero; // <-- why?
    return foo;
}

【问题讨论】:

  • 是的,就是这样。 static 变量始终是零初始化的,并且提供的代码对 foo 进行零初始化。曾经可以使用calloc 以略少的代码实现相同的结果。
  • 使用calloc() 几乎肯定会更有效,但我不确定这是否在语义上相同。
  • @SergeyA 从技术上讲,calloc() 不会初始化指向 NULL 的指针,但实际上它在通用处理器上会这样做。
  • @SergeyA:没那么简单,见stackoverflow.com/questions/2759845/…stackoverflow.com/questions/32136092/…stackoverflow.com/questions/9894013/is-null-always-zero-in-c。您可以在源代码中将0 写为空指针常量,但这并不意味着或要求实际的空指针由零字节组成。
  • @SergeyA 这是一个模糊的区别,在实践中几乎没有什么区别,因为所有常见的实现都使用全零字节作为空指针的表示。但是我使用的第一台计算机是 Multics,它的空指针是全一位。

标签: c struct initialization dynamic-memory-allocation


【解决方案1】:

是的,线条

static FOO foo_zero;

*foo = foo_zero;

安排由init_foo()分配的struct FOO的每个新实例都被初始化,就像有人说的那样

struct FOO new_foo = { 0 };

具体来说,所有整数字段将被初始化为 0,所有浮点字段将被初始化为 0.0,所有指针字段将被初始化为空指针(在 C++ 中又称为 NULLnullptr)。

这是一个很好的技术,因为它比其他技术更简单,严格来说,更便于移植。

在 cmets 中有一个关于做其他可能性的讨论

foo = malloc(sizeof(*foo));
memset(foo, 0, sizeof(*foo));

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

这两种方法都会将全新的struct FOO 初始化为 all-bits-0。这里的微妙问题 - 如此微妙以至于许多程序员根本不会称之为问题 - 是处理器和/或操作系统理论上可以表示浮点值 0.0 或 null指针,具有除 all-bits-0 以外的位模式。

但如果你在这样的处理器上,那么做

float f = 0;

char *p = 0;

会做正确的事,用正确的零值初始化变量,即使它不是全位 0。对于 struct FOO 这样的聚合,做

struct FOO new_foo = { 0 };

等效于用 0 显式初始化其每个成员,这意味着您将获得正确的零值,即使这不是全位 0。最后,任何时候你声明一个具有静态持续时间的变量,如

static FOO foo_zero;

您会得到一个隐式初始化,就像您说的是 = { 0 };,因此默认(静态)初始化也无论如何都会为您提供正确的零值。

如果您仍然对 calloc 的 all-bits-0 保证感到好奇,您可以在 question 7.31C FAQ list 中阅读更多信息。

【讨论】:

  • 除了“C FAQ”之外,你可以阅读over here,首先了解为什么calloc 存在。
【解决方案2】:

其实这个声明

static FOO foo_zero;

等价于以下

static FOO foo_zero = { .buzz = 0, .bazz = 0 };

所以在这个赋值语句中

*foo = foo_zero;

指针 foo 指向的对象被初始化为零,与静态变量 foo_zero 相同。

函数返回一个指向零初始化对象的指针。

对于这个简单的情况,如果你不是 malloc,你可以达到几乎相同的效果 用过calloc。

FOO *init_foo(void)
{
    return calloc( 1, sizeof( struct FOO ) );;
}

但有时需要进行重要的初始化。所以你展示的方法是有意义的。例如

struct FOO
{
    size_t n;
    char s[10];
};

struct FOO * init_foo( void )
{
    static struct FOO default_foo = { .n = 6, .s = "Hello" };
    
    struct FOO *foo = malloc( sizeof( *foo ) );
    
    if ( foo ) *foo = default_foo;
    
    return foo;
}

【讨论】:

  • 虽然确实如此,但对于 OP 可能使用的几乎所有流行处理器,calloc 可能会给出相同的结果,但 严格保证C标准。 calloc 将给出所有位 0。 static FOO foo_zero 将初始化指针和浮点字段,就好像它们被赋予初始值 0,理论上可能不是所有位为 0,在一些适当的模糊平台上。
  • 不,正如我们刚刚在 cmets 中约定的那样,在语义上使用 calloc 会有所不同。
  • @VladfromMoscow 有细微差别,您可以阅读问题下的 cmets。底线 - 静态初始化将指针初始化为空指针,而 calloc 将位初始化为 0。从技术上讲,空指针 != 全零位。归功于@Barmar。
  • @VladfromMoscow 零初始化不一定全为 0。请参阅其他地方引用的答案。这是一个晦涩难懂的点,几乎没有实际应用,但它可以防止说 calloc 在这里是等价的。
  • 根据 C 2018 6.7.9 10,空指针的默认初始化将其初始化为空指针。 C 标准不要求空指针的对象表示为包含零的字节(它只要求空指针比较不等于任何对象或函数)。 calloc 确实将内存字节设置为零,因此它不会在 C 实现中生成空指针,其中零字节不是空指针的表示。
【解决方案3】:

为什么复杂?

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef struct {
    int buzz;
    char *bazz;
} FOO;

init_foo() 上面实际上只是返回在堆上创建的新 foo。都干净。所以让我们这样做吧:

static inline FOO * new_foo(void)
{
    return calloc(1,sizeof(FOO));
}

使用与检查:

int main (void)
{
    FOO * foo = new_foo();

   printf("buzz: %d, bazz: %s", foo->buzz, foo->bazz );

   free(foo);

    return 42;
}

Godbolt 显示新的结构 FOO 很好地为空

Program returned: 42
buzz: 0, bazz: (null)

【讨论】:

  • 正如在别处解释的那样,这种技术稍逊一筹。虽然对于任何当前流行的环境都不是这种情况,但理论上非整数类型(包括指针和浮点值)可能具有零值,这些值 not 由所有位表示- calloc 为您提供的 0 位模式。问题中提到的static FOO foo_zero 技术旨在即使在这样的平台上也能正常工作..
  • 感谢@SteveSummit。在实践中,我从未见过这样的平台。自 1991 年至今。
猜你喜欢
  • 1970-01-01
  • 2022-01-14
  • 1970-01-01
  • 1970-01-01
  • 2018-04-15
  • 1970-01-01
  • 2014-05-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多