Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference in initializing and zeroing an array in c/c++?

Tags:

c++

arrays

c

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.
like image 522
mhd Avatar asked Jan 17 '09 15:01

mhd


People also ask

What are the different ways to initialize and array and element as zero?

Option A, B and C are the different ways to initialize an array with all elements as zero.

What does it mean to initialize an array in C?

An initializer list initializes elements of an array in the order of the list. For example, consider the below snippet: int arr[5] = {1, 2, 3, 4, 5}; This initializes an array of size 5, with the elements {1, 2, 3, 4, 5} in order. This means that arr[0] = 1 , arr[1] = 2 , and so on.

What is the difference between declaring and initializing an array?

Declaring an array does not initialize the array in the memory. When the array variable is initialized, you can assign values to the array. Array is a reference type, so you need to use the new keyword to create an instance of the array. int n= new int[10] {100, 200, 300, 400, 500};

What are the different ways to initialize an array with all elements as zero in C programming?

The array will be initialized to 0 in case we provide empty initializer list or just specify 0 in the initializer list. Designated Initializer: This initializer is used when we want to initialize a range with the same value. This is used only with GCC compilers.


2 Answers

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).

like image 141
7 revs Avatar answered Oct 20 '22 04:10

7 revs


ISO/IEC 9899:TC3 6.7.8, paragraph 21:

If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than there are elements in the array, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.

Arrays with static storage duration are initialized to 0, so the C99 spec guarantees the not explicitly initialized array elements to be set to 0 as well.


In my first edit to this post, I spouted some nonsense about using compound literals to assign to an array after initialization. That does not work. If you really want to use compound literals to set an array's values, you have to do something like this:

#define count(ARRAY) (sizeof(ARRAY)/sizeof(*ARRAY))

int foo[16];
memcpy(foo, ((int [count(foo)]){ 1, 2, 3 }), sizeof(foo));

With some macro magic and the non-standard __typeof__ operator, this can be considerably shortened:

#define set_array(ARRAY, ...) \
    memcpy(ARRAY, ((__typeof__(ARRAY)){ __VA_ARGS__ }), sizeof(ARRAY))

int foo[16];
set_array(foo, 1, 2, 3);
like image 38
Christoph Avatar answered Oct 20 '22 03:10

Christoph