初始化在这里重要吗?

时间:2016-12-27 06:34:55

标签: c++

我在堆栈上有一个数组

char arr[10];

我有一个宏定义

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

现在我的问题是,如果我初始化“arr”或保持未初始化是否重要?

您更喜欢哪个:

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

我知道初始化始终是首选,但如果我不这样做,请指出问题是什么。

修改

在main()中,我将使用它:

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

我理解“std :: string”将是一个更好的选择,但现在我只想坚持这一点。伙计我知道我不应该读取未初始化的变量,但在这里我不是在“sprintf”之前阅读它。

我只想要一个优于另一个优势,哪个应该是首选(有理由)。我不是在寻找其他方法来做到这一点。

3 个答案:

答案 0 :(得分:2)

让我们看看数组会发生什么。根据cppreference:

  

默认初始化在以下三种情况下执行:

     

1)当变量具有自动,静态或线程本地存储时   声明持续时间没有初始值设定项;

     

2)当a创建具有动态存储持续时间的对象时   没有初始化程序的new-expression或者当a创建对象时   new-expression,初始化器包含一对空的   括号(直到C ++ 03);

     

3)当a中没有提到基类或非静态数据成员时   构造函数初始化列表和该构造函数被调用。

我们的案例是1)。现在,默认初始化程序的效果是什么?

  

默认初始化的效果是:

     
      
  • 如果T是非POD(直到C ++ 11)类类型,则会考虑构造函数并对其进行重载解析   参数列表。选择的构造函数(这是默认值之一   调用构造函数来为新的提供初始值   宾语;

  •   
  • 如果T是数组类型,则数组的每个元素都是默认初始化的;

  •   
  • 否则,什么都不做:具有自动存储持续时间的对象(及其子对象)被初始化为不确定   值。

  •   

我们显然有一个数组,但它的元素是POD类型。类的数组将表现为几个声明的类,POD类型的数组 - 就像POD类型的几个变量声明一样。它们的价值将不确定:

  

使用automatic和自定义非类变量的默认初始化   动态存储持续时间会生成具有不确定值的对象   (静态和线程局部对象初始化为零)

#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)
}

无论您是否初始化,只有在写入之前会有读取操作时才会看到唯一的效果。 sprint只会向内存写入第一个参数点。如果您在之前从该数组读取值,则程序的行为将是未定义的,因为值未确定。它可能是0,它可能是随机值,它可能是识别未初始化内存的特殊标记,取决于编译器。一些编译器为此添加了运行时检查。

答案 1 :(得分:1)

C ++不允许在初始化之前使用数组中的值。它不会停止你这样做,但它允许编译器生成一个崩溃或随机行为的程序。

因此,除非您确定数组在使用前已初始化,否则请将其初始化为无害值。

我更喜欢,

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

{}是&#34;空的C ++习语。&#34;

答案 2 :(得分:1)

由于数组在堆栈上,因此将分配对应于激活记录(对于声明数组的函数)的堆栈帧,并且事物将保持原样,即未初始化。从装配中可以看出这一点。

示例代码A:

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

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

示例代码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;
}

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
  • 另请注意,sprintf "%lld"内部使用的是long long int

(long long int range: –9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 )。因此,数组的最大大小应至少为21.最好是大小= 24。 否则,通过CONVERT(arr, 100000000000LL);

验证结果
  • 从优势的角度来看:如果使用未初始化的版本(代码A),则不会执行三条额外的初始化指令。
  • 从正确的角度来看:正如您所说,您不会使用未初始化的数组,因此无论您是初始化还是代码仍然正确无关紧要。因为sprintf格式化的字符串将以null结尾,如果参数是正确的。