简体   繁体   中英

Does initialization matters here?

I have an array on stack

char arr[10];

I have one macro definition

#define CONVERT(arr, n)  sprintf(arr, "%lld", n)

Now my question is, does it matter if I initialize "arr", or leave it uninitialized?

Which will you prefer:

1. char arr[10];
2. char arr[10] = {0};

I know initialization is always preferred, but please point out what are the issue if I don't.

EDIT:

In main(), i will be using it like :

char arr[10] or arr[10] = {0};
CONVERT(arr, 1000);
cout<<arr;

I understand "std::string" will be a better choice but for now i want to stick with this only. Guys i know i shouldn't read uninitialized variables, but here i am not reading it before "sprintf".

I just want the advantage of one over another, and which should be preferred (with reasons). I am not looking for alternative ways to do this.

Let's see what happens to array. According to cppreference:

Default initialization is performed in three situations:

1) when a variable with automatic, static, or thread-local storage duration is declared with no initializer;

2) when an object with dynamic storage duration is created by a new-expression with no initializer or when an object is created by a new-expression with the initializer consisting of an empty pair of parentheses (until C++03);

3) when a base class or a non-static data member is not mentioned in a constructor initializer list and that constructor is called.

Our case is 1). Now what's the effect of default initializer is here?

The effects of default initialization are:

  • if T is a non-POD (until C++11) class type, the constructors are considered and subjected to overload resolution against the empty argument list. The constructor selected (which is one of the default constructors) is called to provide the initial value for the new object;

  • if T is an array type, every element of the array is default-initialized;

  • otherwise, nothing is done: the objects with automatic storage duration (and their subobjects) are initialized to indeterminate values.

We clearly have an array, but its elements are POD types. Array of classes will behave lie several declared classes, array of POD types - like several declaration of variable of POD type. And value of them will be undetermined:

Default initialization of non-class variables with automatic and dynamic storage duration produces objects with indeterminate values (static and thread-local objects get zero initialized)

#include <string>

struct T1 { int mem; };

struct T2
{
    int mem;
    T2() { } // "mem" is not in the initializer list
};

int n; // static non-class, a two-phase initialization is done:
       // 1) zero initialization initializes n to zero
       // 2) default initialization does nothing, leaving n being zero


int main()
{
    int n;            // non-class, the value is indeterminate
    std::string s;    // class, calls default ctor, the value is "" (empty string)
    std::string a[2]; // array, default-initializes the elements, the value is {"", ""}
//  int& r;           // error: a reference
//  const int n;      // error: a const non-class
//  const T1 t1;      // error: const class with implicit default ctor
    T1 t1;            // class, calls implicit default ctor
    const T2 t2;      // const class, calls the user-provided default ctor
                      // t2.mem is default-initialized (to indeterminate value)
}

Whether you initialized or not, the only effect would be seen only if there will be read operation before writing. sprint does only writing to memory it first argument points. If you read value from that array before , the behavior of program would be undefined, as value there is undetermined. It might be 0, it might be random value, it might be a special marker that identifies uninitialized memory, depends on compiler. Some compilers add run-time checks for that.

C++ does not allow you to use a value from the array before it is initialized. It won't stop you from doing so, but it allows the compiler to produce a program with crashing or random behavior.

So, unless you're sure the array will be initialized before use, initialize it to an innocuous value.

I'd prefer,

constexpr int buffer_length = 10; // Ten bytes is always enough because…
char arr[ buffer_length ] = {};

{} is the C++ idiom for "empty."

Since the array is on stack, a stack frame corresponding to the activation record (for the function inside which the array is declared) would be allocated and things will be kept as it is, ie uninitialized. This is evident from the assembly.

Sample Code A:

#include <iostream>
#define CONVERT(arr, n)  sprintf(arr, "%lld", n)
int main()
{
    char arr[10];
    CONVERT(arr, 1000);
    std::cout << arr;
    return 0;
}

Assembly Code for A:

    .LCFI14:
    movl    $1000, 8(%esp)
    movl    $.LC0, 4(%esp)
    leal    -14(%ebp), %eax
    movl    %eax, (%esp)
    call    sprintf
    leal    -14(%ebp), %eax
    movl    %eax, 4(%esp)
    movl    $_ZSt4cout, (%esp)
    call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    movl    $0, %eax
    addl    $36, %esp
    popl    %ecx
    popl    %ebp
    leal    -4(%ecx), %esp
    ret

Sample Code B:

#include <iostream>
#define CONVERT(arr, n)  sprintf(arr, "%lld", n)
int main()
{
    char arr[10] = {0};
    CONVERT(arr, 1000);
    std::cout << arr;
    return 0;
}

Assembly Code for B:

    .LCFI14:
    movl    $0, -14(%ebp)   <<--- extra instruction for initialization
    movl    $0, -10(%ebp)   <<--- extra instruction for initialization
    movw    $0, -6(%ebp)    <<--- extra instruction for initialization
    movl    $1000, 8(%esp)
    movl    $.LC0, 4(%esp)
    leal    -14(%ebp), %eax
    movl    %eax, (%esp)
    call    sprintf
    leal    -14(%ebp), %eax
    movl    %eax, 4(%esp)
    movl    $_ZSt4cout, (%esp)
    call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    movl    $0, %eax
    addl    $36, %esp
    popl    %ecx
    popl    %ebp
    leal    -4(%ecx), %esp
    ret
  • Also, note that inside sprintf "%lld" is used which is for long long int .

(long long int range: –9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 ) . So, max size of array should be atleast 21. Better to have size = 24. Else verify the result by having CONVERT(arr, 100000000000LL);

  • From advantage point of view: Three extra initialization instructions will not be executed, if uninitialized version (Code A) is used.
  • From correctness point of view: As you said you will not use the uninitialized array, so it doesn't matter whether you initialize and code would still be correct. Because the string formatted by sprintf will be null terminated, if arguments are correct.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM