最近我发现RE2库使用this technique进行快速查找。在查找期间,它使用来自未初始化数组的值,据我所知,这是未定义的行为。
我甚至发现this issue有关于使用未初始化内存的valgrind警告。但是这个问题已被关闭,并且评论说这种行为已被取消。
我认为实际上未初始化的数组只会在所有现代编译器和体系结构中包含一些随机数据。但另一方面,我将“未定义行为”声明视为“字面上任何事情都可能发生”(包括您的程序格式化您的硬盘或哥斯拉来破坏您的城市)。
问题是:在C ++中使用未初始化的数据是否合法?
答案 0 :(得分:0)
最初设计C时,如果arr
是某个类型T
占用N
字节的数组,则arr[i]
这样的表达式意味着“取{的基址{1}},向其添加arr
,在结果地址获取i*N
个字节,并将其解释为N
“。如果T
字节的每个可能组合在解释为类型N
时都具有意义,则获取未初始化的数组元素可能会产生任意值,但行为本来是可预测的。如果T
是32位类型,则尝试读取类型为T
的未初始化数组元素将产生最多4294967296个可能行为中的一个;当且仅当这些行为中的每一个都符合计划的要求时,这样的行动才是安全的。如您所知,有些情况下这种保证很有用。
T
并且稍后执行a=x;
和b=a;
,并且编译器无法“看到”原始分配与可能更改的后分配之间的任何内容{{ 1}}或c=a;
,它可以省略第一个分配,并将后两个分配更改为a
和x
。但是,如果后两个分配之间发生了某些情况会发生变化b=x;
,那么可能会导致c=x;
和x
获得不同的值 - 如果没有任何变化,则应该是不可能的{{{ 1}}。
如果没有任何改变b
,那么应用该优化本身不会成为问题。另一方面,考虑使用某些已分配存储作为类型c
的代码,释放它,重新分配它,并将其用作类型a
。如果编译器知道原始请求和替换请求的大小相同,则可以在不释放和重新分配存储的情况下回收存储。但是,这可能导致代码序列:
x
被重写为:
float
特别是在处理int
和float *fp = malloc(4);
...
*fp = slowCalculation();
somethingElse = *fp;
free(fp);
int *ip = malloc(4);
...
a=*ip;
b=a;
...
c=a;
的写入之间缓慢计算的结果时,性能很少受益。遗憾的是,编译器的设计方式并不能方便地保证延迟计算不会偶然降落在导致故障的地方。
就我个人而言,我认为编译器编写者的哲学是严重错误的:如果某个程序员在某种情况下知道保证会有用,那么要求程序员解决它的缺乏会带来很大的成本和100%的确定性。相比之下,编译器避免以缺乏该保证为基础的优化的要求很少会花费任何成本(因为解决其缺失的代码几乎肯定会阻止“优化”)。不幸的是,有些人似乎更感兴趣的是优化那些不需要超出标准要求的保证的源文本的性能,而不是优化编译器生成代码以完成有用任务的效率。