代码之家  ›  专栏  ›  技术社区  ›  dash-tom-bang

将对象初始化为所有零

  •  17
  • dash-tom-bang  · 技术社区  · 14 年前

    通常,数据结构的有效初始化是将所有成员设置为零。即使在C++中编程时,也可能需要与外部API进行接口。

    以下两者之间是否存在实际差异:

    some_struct s;
    memset(&s, 0, sizeof(s));
    

    简单地

    some_struct s = { 0 };
    

    人们是否发现自己同时使用了这两种方法,并选择了一种更适合给定应用程序的方法?(希望可以理解,这只是目前适用于POD结构;如果在该结构中有一个C++ STD::String,就会受到各种各样的破坏。)

    对我自己来说,作为一个不太使用MeMSET的C++程序员,我永远不会。 某些 关于函数签名,我发现第二个示例除了类型少、更紧凑、甚至更明显之外更容易使用,因为它在声明中说“这个对象被初始化为零”,而不是等待下一行代码并看到“哦,这个对象被初始化为零”。

    在C++中创建类和结构时,我倾向于使用初始化列表;我很好奇人们对上面两种“C风格”初始化的想法,而不是与C++中可用的比较,因为我怀疑我们中的许多人与C库接口,即使我们主要在C++中自己编写代码。

    编辑: 尼尔·巴特沃斯摆姿势 this question 接下来,我认为这是这个问题有趣的推论。

    12 回复  |  直到 11 年前
        1
  •  22
  •   AnT stands with Russia    14 年前

    memset 实际上从来都不是正确的方法。是的,有一个实际的区别(见下文)。

    在C++中,并非所有事物都可以用文字初始化。 0 (EnUM类型的对象不能),这就是为什么C++中常用的成语是

    some_struct s = {};
    

    而在C语言中,习语是

    some_struct s = { 0 };
    

    注意,在c中 = { 0 } 是什么可以称为 通用零初始值设定项 . 它可以用于几乎任何类型的对象,因为 {} -对于标量对象也允许使用封闭的初始值设定项

    int x = { 0 }; /* legal in C (and in C++) */
    

    这使得 = { 0 } 在与类型无关的通用C代码中很有用(例如,与类型无关的宏)。

    缺点 = { 0 } C89/90和C++中的初始化器只能用作声明的一部分。(C99通过引入 复合文字 . 类似的功能也将出现在C++上。为此,您可能会看到许多程序员使用。 清零 为了在C89/90或C++中的中间部分去掉一些代码。不过,我想说的是,正确的做法仍然是 清零 但更像是

    some_struct s;
    ...
    {
      const some_struct ZERO = { 0 };  
      s = ZERO;
    }
    ...
    

    也就是说,在代码中间引入一个“虚构的”块,即使乍一看它可能不太漂亮。当然,在C++中不需要引入块。

    至于实际差异…你可能会听到一些人这样说 清零 将在实践中产生相同的结果,因为在实践中,物理全零位模式用于表示所有类型的零值。然而,这通常不是真的。演示典型C++实现中的差异的一个直接示例是指向数据成员类型的指针。

    struct S;
    ...
    
    int S::*p = { 0 };
    assert(p == NULL); // this assertion is guaranteed to hold
    
    memset(&p, 0, sizeof p);
    assert(p == NULL); // this assertion will normally fail
    

    这是因为一个典型的实现通常使用一位模式( 0xFFFF... )表示此类型的空指针。上面的示例演示了归零之间的实际差异 清零 正常 = { 0 } 初始化器。

        2
  •  15
  •   Mark Ransom    14 年前

    some_struct s = { 0 }; 保证工作; memset 依赖于实现细节,最好避免。

        3
  •  6
  •   Arkku    14 年前

    如果结构包含指针,则生成的所有位零的值 memset 可能与分配 0 在C(或C++)代码中,即 NULL 指针。

    (也可能是 floats doubles 但我从未遇到过。但是,我认为这些标准并不能保证它们在 清零 要么。

    编辑: 从更务实的角度来看,我还是会说不要使用 清零 尽可能避免,因为这是一个额外的函数调用,写的时间更长,而且(在我看来)在意图上比 = { 0 } .

        4
  •  5
  •   drawnonward    14 年前

    根据编译器的优化,可能会有一些阈值,超过这个阈值,memset会更快,但这通常会远远超过基于堆栈的变量的正常大小。在带有虚拟表的C++对象上使用MeMSET当然是不好的。

        5
  •  4
  •   Hugues    11 年前

    我找到了一个很好的解决方案:

    template<typename T> void my_zero(T& e) {
        static T dummy_zero_object;
        e = dummy_zero_object;
    }
    
    my_zero(s);
    

    这不仅对基本类型和用户定义的类型是正确的,而且对定义了默认构造函数的类型进行了零初始化,但不能初始化所有成员变量,尤其是包含非平凡的类。 union 成员。

        6
  •  3
  •   Jerry Coffin    14 年前

    唯一 实际的 区别在于 ={0}; 语法上说“初始化这是空的”(至少在我看来更清楚)。

    纯粹理论上,有一些情况 memset 可能会失败,但据我所知,它们实际上只是理论上的。奥思,考虑到这两种理论都不如 一个实际的观点,我很难弄清楚为什么有人想使用 清零 为了这个任务。

        7
  •  3
  •   anon    14 年前

    我从来没有理解过把所有东西都归零的神秘好处,即使定义了它,也不太可能是可取的。由于这被标记为C++,初始化的正确解决方案是给予结构或类A约束。

        8
  •  3
  •   Johannes Schaub - litb    14 年前

    希望可以理解的是,这只是目前可用于POD结构;如果在该结构中有C++ STD::string,就会得到编译器错误。

    没有你 不会 . 如果你使用 memset 在这种情况下,最好的情况是你会撞车,最坏的情况是你会胡说八道。这个 = { } 只要它们是聚合体,就可以完美地用于非pod结构。这个 = {} 在C++中,方法是最好的方法。请注意,C++中没有理由提出 0 它也不推荐使用,因为它大大减少了可以使用它的情况。

    struct A {
      std::string a;
      int b;
    };
    
    int main() {
      A a = { 0 };
      A a = { };
    }
    

    第一个将不执行您想要的操作:它将尝试创建一个 std::string 从一个C字符串给它的构造函数一个空指针。但是,第二个函数会按照您的需要执行:它创建一个空字符串。

        9
  •  2
  •   daramarak    14 年前

    我认为初始化可以更清楚地说明您实际在做什么。您正在初始化结构。当新标准失效时,初始化的方法将得到更多的使用(用初始化容器是值得期待的)。memset方式稍微容易出错,并且不能清楚地传达您正在做的事情。在单独编程时,这可能不太重要,但在团队中工作时意义重大。

    对于一些用C++工作的人来说,MimSET,Malc& & Co.是非常神秘的生物。我自己也遇到过一些。

        10
  •  2
  •   Thomas Matthews    14 年前

    清除结构的最佳方法是单独设置每个字段:

    struct MyStruct
    {
      std::string name;
      int age;
      double checking_account_balance;
      void clear(void)
      {
         name.erase();
         age = 0;
         checking_account_balance = 0.0;
      }
    };
    

    在上面的示例中,a clear 方法被定义为将所有成员设置为已知状态或值。这个 memset std::fill 由于以下原因,方法可能不起作用 std::string double 类型。一个更健壮的程序分别清除每个字段。

    比起花更少的时间打字,我更喜欢有一个更健壮的程序。

        11
  •  0
  •   maerics    14 年前

    这个 bzero 函数是另一个选项。

    #include <strings.h>
    void bzero(void *s, size_t n);
    
        12
  •  0
  •   pixelbeat    14 年前

    在C语言中,我更喜欢使用0,而不是等价的memset()。 然而,GCC警告这种用法:(详细信息如下: http://www.pixelbeat.org/programming/gcc/auto_init.html

    在C++中,它们通常是等价的,但与C++一样。 有一些角落案例需要考虑(在其他答案中注明)。