[英]Does initialization matters here?
我在堆栈上有一个数组
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”之前读取它。
我只想要一个相对于另一个的优势,并且应该优先考虑(有理由)。 我不是在寻找替代方法来做到这一点。
让我们看看数组发生了什么。 根据cppreference:
默认初始化在以下三种情况下执行:
1)在没有初始化程序的情况下声明了具有自动,静态或线程本地存储持续时间的变量时;
2)当动态存储持续时间的对象是由没有初始化程序的new表达式创建的,或者当对象是由带有空括号对的初始化程序的new表达式创建的(直到C ++ 03);
3)当构造函数初始化列表中未提及基类或非静态数据成员时,将调用该构造函数。
我们的情况是1)。 现在,默认初始化程序的作用是什么?
默认初始化的效果是:
如果T是非POD(直到C ++ 11)类类型,则考虑构造函数,并针对空参数列表对其进行重载解析。 调用选定的构造函数(它是默认的构造函数之一)为新对象提供初始值。
如果T是数组类型,则数组的每个元素都将默认初始化;
否则,什么也不做:具有自动存储期限的对象(及其子对象)被初始化为不确定的值。
我们显然有一个数组,但是它的元素是POD类型。 类的数组将表现为几个声明的类,POD类型的数组-就像POD类型的变量的几个声明一样。 它们的价值将不确定:
具有自动和动态存储持续时间的非类变量的默认初始化会生成具有不确定值的对象(静态和线程本地对象初始化为零)
#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,可能是随机值,它可能是一个特殊标记,用于标识未初始化的内存,具体取决于编译器。 一些编译器为此添加了运行时检查。
C ++不允许您在初始化之前使用数组中的值。 它不会阻止您这样做,但是它允许编译器生成具有崩溃或随机行为的程序。
因此,除非您确定数组将在使用前初始化,否则将其初始化为无害的值。
我更喜欢,
constexpr int buffer_length = 10; // Ten bytes is always enough because…
char arr[ buffer_length ] = {};
{}
是“空”的C ++习惯用法。
由于数组在堆栈中,因此将分配与激活记录(用于声明数组的函数)相对应的堆栈帧,并且所有内容将保持不变,即未初始化。 从组装中可以明显看出这一点。
示例代码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);
验证结果CONVERT(arr, 100000000000LL);
sprintf
格式化的字符串将以null终止。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.