在 c/c++ 中初始化和归零数组的区别? [英] Difference in initializing and zeroing an array in c/c++?

查看:26
本文介绍了在 c/c++ 中初始化和归零数组的区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 c (或者 c++) 中,有什么区别

<前>char myarr[16]={0x00};


<前>char myarr[16];memset(myarr, '\0', sizeof(myarr));

??

我问这个是因为在 vc++ 2005 中结果是一样的..
编辑更多:和

char myarr[16]={0x00,};

?
也许可以得到更全面的答案,而不是模棱两可,因为下面的一些答案是指这种代码,即.在关闭花括号之前放置逗号.在 vc++ 2005 中结果也是一样的.

解决方案

重要的区别在于,第一个默认值以特定于元素的方式初始化数组:指针将接收一个空指针值,不需要是 0x00(如所有位为零),布尔值将是 false.如果元素类型是一个不是所谓的 POD(普通旧数据类型)的类类型,那么你只能做第一个,因为第二个只适用于最简单的情况(你没有虚拟函数、用户定义的构造函数等等).相比之下,第二种使用 memset 的方法将数组的所有元素都设置为零.这并不总是你想要的.例如,如果您的数组有指针,则不一定会将它们设置为空指针.

第一个将默认初始化数组的元素,除了第一个显式设置为 0.如果数组是本地的并且在堆栈上(即,不是静态的),编译器内部通常会执行一个 memset 来清除数组.如果数组是非本地的或静态的,第一个版本会效率更高.编译器可以在编译时将初始值设定项放入生成的汇编代码中,使其根本不需要运行时代码.或者,当程序以快速方式(即逐页)启动时,可以将数组布置在自动归零的部分上(也适用于指针,如果它们具有全位零表示).

第二个在整个数组上显式地执行一个 memset.优化编译器通常会将较小区域的 memset 替换为内联机器代码,这些机器代码仅使用标签和分支进行循环.

<子>这是为第一种情况生成的汇编代码.我的 gcc 东西没有太多优化,所以我们得到了对 memset 的真正调用(总是分配堆栈顶部的 16 个字节,即使我们没有本地变量.$n 是一个寄存器号):

void f(void) {int a[16] = { 42 };}低于 29 美元、29 美元、88 美元;创建堆栈帧,88 字节31 美元、29 美元、84 美元;保存退货地址添加 4 美元、29 美元、16 美元;第一个参数是目的地,数组.添加 $5, $0, 0 ;第二个参数是要填充的值添加 $6, $0, 64 ;第三个参数是要填充的大小:4byte * 16jal memset ;调用内存集添加 $2, $0, 42 ;将第一个元素 a[0] 设置为 422 美元、29 美元、16 美元;ldw 31 美元、29 美元、84 美元;恢复退货地址加 29 美元、29 美元、88 美元;销毁堆栈帧jr 31 美元;返回给来电者

来自 C++ 标准的血腥细节.上面的第一种情况将默认初始化剩余的元素.

8.5:

<块引用>

对类型 T 的对象进行零初始化存储意味着:

  • 如果 T 是标量类型,则存储设置为值 0(零)转换为 T;
  • 如果 T 是非联合类类型,则对每个非静态数据成员和每个基类子对象的存储进行零初始化;
  • 如果 T 是联合类型,则其第一个数据成员的存储是零初始化的;
  • 如果 T 是数组类型,则每个元素的存储都是零初始化的;
  • 如果 T 是引用类型,则不执行初始化.

默认初始化一个 T 类型的对象意味着:

  • 如果 T 是非 POD 类类型,则调用 T 的默认构造函数
  • 如果 T 是数组类型,则每个元素都默认初始化;
  • 否则,对象的存储是零初始化的.

8.5.1:

<块引用>

如果列表中的初始值设定项少于聚合中的成员数,那么每个未显式初始化的成员都应默认初始化 (8.5).

In c (or maybe c++) , what's the difference between

char myarr[16]={0x00};

and

char myarr[16];
memset(myarr, '\0', sizeof(myarr));

??

edit: I ask this because in vc++ 2005 the result is the same..
edit more : and

char myarr[16]={0x00,}; 

?
maybe can get more comprehensive answer and not ambiguous as some answers below refer to this kind of code,ie. put comma just before closing curly braces. Also the result is the same in vc++ 2005.

解决方案

The important difference is that the first default initializes the array in an element-specific manner: Pointers will receive a null pointer value, which doesn't need to be 0x00 (as in all-bits-zero), booleans will be false. If the element type is a class type that's not a so-called POD (plain old data-type), then you can only do the first one, because the second one only works for the simplest cases (where you don't have virtual functions, user defined constructors and so on). In contrast, the second way using the memset sets all elements of the array to all-bits-zero. That is not always that what you want. If your array has pointers for example, they won't be set to null-pointers necessarily.

The first will default initialize the elements of the array, except for the first one, which is set to 0 explicitly. If the array is local and on the stack (that is, not a static), the compiler internally often does a memset to clear the array out. If the array is non-local or static, the first version can be considerably more efficient. The compiler can put the initializers already, at compile time, into the generated assembler code, making it require no runtime code at all. Alternatively, the array can be laid out on a section that is automatically zero'd out (also for pointers, if they have a all-bits-zero representation) when the program starts in a fast manner (i.e page-wise).

The second does a memset explicitly over the whole array. Optimizing compilers will usually replace a memset for smaller regions with inline machine code that just loops using labels and branches.

Here is assembler-code generated for the first case. My gcc stuff isn't much optimized, so we got a real call to memset (16 bytes at the stack-top are always allocated, even if we got no locals. $n is a register number):

void f(void) {
    int a[16] = { 42 };
}

sub     $29, $29, 88 ; create stack-frame, 88 bytes
stw     $31, $29, 84 ; save return address
add     $4, $29, 16  ; 1st argument is destination, the array.
add     $5, $0, 0    ; 2nd argument is value to fill
add     $6, $0, 64   ; 3rd argument is size to fill: 4byte * 16
jal     memset       ; call memset
add     $2, $0, 42   ; set first element, a[0], to 42
stw     $2, $29, 16  ;
ldw     $31, $29, 84 ; restore return address
add     $29, $29, 88 ; destroy stack-frame
jr      $31          ; return to caller

The gory details from the C++ Standard. The first case above will default-initialize remaining elements.

8.5:

To zero-initialize storage for an object of type T means:

  • if T is a scalar type, the storage is set to the value of 0 (zero) converted to T;
  • if T is a non-union class type, the storage for each nonstatic data member and each base-class subobject is zero-initialized;
  • if T is a union type, the storage for its first data member is zero-initialized;
  • if T is an array type, the storage for each element is zero-initialized;
  • if T is a reference type, no initialization is performed.

To default-initialize an object of type T means:

  • if T is a non-POD class type, the default constructor for T is called
  • if T is an array type, each element is default-initialized;
  • otherwise, the storage for the object is zero-initialized.

8.5.1:

If there are fewer initializers in the list than there are members in the aggregate, then each member not explicitly initialized shall be default-initialized (8.5).

这篇关于在 c/c++ 中初始化和归零数组的区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆