我在堆栈上有一个数组
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”之前阅读它。
我只想要一个优于另一个优势,哪个应该是首选(有理由)。我不是在寻找其他方法来做到这一点。
答案 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);
sprintf
格式化的字符串将以null结尾,如果参数是正确的。