我有一些代码,基本上归结为以下内容:
void bar(bool b, double f)
{
if (b){
double g = f;
}
}
void foo()
{
double f;
bool b = false;
bar(b, f);
}
Run Code Online (Sandbox Code Playgroud)
这里有任何未定义的行为吗?我怀疑有可能是因为我走的是未初始化的值复制double路过时f到bar.也就是说,我没有使用传递,double因为if块不会运行.
此外,如果我double通过参考,一切都会好的:
void bar(bool b, double& f)
Run Code Online (Sandbox Code Playgroud)
然后我不是"使用"一个未初始化的变量,而只是指它.
由于我已经编码 C 20 多年了,我认为是时候参加考试了!看看我是否学到了任何东西,或者我是否只是一些欺诈行为,向互联网上的初学者发布免费但不正确的建议。
这个站点(我没有附属)提供免费的 C 测试。https://www.tutorialspoint.com/cprogramming/cprogramming_mock_test.htm。
我参加了测试 1,但以 50 分中的 34 分惨败!是这个吗?我必须放弃我的 C 程序员职业吗?这个 Tutorialspoint 站点及其测试有多好?
具体来说,我未能用测试预期的答案回答 Q7、Q9、Q10、Q14、Q16、Q17、Q19、Q21、Q27、Q28、Q31、Q32、Q33、Q35、Q38、Q46。这些问题的正确答案是什么?
此外,当我在我的符合实现 C 编译器 ( gcc -std=c11 -pedantic-errors)上编译问题时,它们甚至都不会通过编译。这是为什么?我和/或我的编译器坏了吗?或者这个网站根本不是很好?
SO 上的各种受人尊敬的高代表用户一直坚持认为读取具有不确定值的变量“始终是 UB”。那么在 C 标准中究竟在哪里提到了这一点?
很明显,不确定的值可能是未指定的值或陷阱表示:
3.19.2
indeterminate value 不确定值
或未指定值或陷阱表示3.19.3
未指定值
本国际标准对在任何情况下选择哪个值没有强加要求的相关类型的有效值
注:未指定值不能是陷阱表示。3.19.4
陷阱表示
不需要表示对象类型值的对象表示
同样很明显,读取陷阱表示会调用未定义的行为,6.2.6.1:
某些对象表示不需要表示对象类型的值。如果对象的存储值具有这样的表示形式并且被没有字符类型的左值表达式读取,则行为未定义。如果这种表示是由没有字符类型的左值表达式修改对象的全部或任何部分的副作用产生的,则行为是未定义的。50) 这种表示称为陷阱表示。
但是,不确定的值不一定包含陷阱表示。事实上,陷阱表示对于使用二进制补码的系统来说是非常罕见的。
C 标准中的哪个地方实际上说读取不确定的值会调用未定义的行为?
我在看C11的非规范性附件J,发现这确实被列为UB的一个案例:
具有自动存储期限的对象的值在不确定时使用(6.2.4、6.7.9、6.8)。
但是,列出的部分是无关紧要的。6.2.4 仅说明有关生命周期和变量值何时变得不确定的规则。类似地,6.7.9 是关于初始化并说明变量的值如何变得不确定。6.8 似乎几乎无关紧要。这些部分都没有包含任何规范性文本,说明访问不确定的值会导致 UB。这是附件 J 中的缺陷吗?
然而,在 6.3.2.1 中有一些关于左值的相关规范文本:
如果左值指定了一个自动存储持续时间的对象,该对象可以使用寄存器存储类声明(从未获取其地址),并且该对象未初始化(未使用初始化程序声明,并且在使用之前未对其进行赋值) ),行为未定义。
但这是一种特殊情况,它仅适用于从未被取地址的自动存储期变量。我一直认为 6.3.2.1 的这一部分是关于不确定值(不是陷阱表示)的唯一 UB 情况。但人们一直坚持“它总是UB”。那么具体是在哪里提到的呢?
我想使用https://research.swtch.com/sparse中描述的技巧在C++中构建一个密集的整数集.这种方法通过允许自己读取未初始化的内存来实现良好的性能.
如何在不触发未定义行为的情况下实现此数据结构,并且不会与Valgrand或ASAN等工具发生冲突?
编辑:似乎响应者正在关注"未初始化"这个词,并在语言标准的上下文中对其进行解释.对我而言,这可能是一个糟糕的词语选择 - 这里"未初始化"仅意味着它的价值对于算法的正确运行并不重要.显然可以安全地实现这个数据结构(LLVM在SparseMultiSet中实现它).我的问题是,最好和最有效的方法是什么?
以下代码可能存在未定义的行为:
uint64_t arr[1]; // Uninitialized
if(arr[0] == 0) {
Run Code Online (Sandbox Code Playgroud)
C标准指定具有自动存储持续时间的未初始化变量具有不确定的值,该值是未指定的或陷阱表示.它还指定uintN_t类型没有填充位,并且定义了值的大小和范围; 所以陷阱表示uint64_t是不可能的.
所以我得出结论,未初始化的值本身并不是未定义的行为.读它怎么样?
6.3.2.1左值,数组和函数指示符
- ...
除非它是运算
sizeof符的操作数,_Alignof运算符,一元&运算符,++运算符,--运算符或者左运算符.运算符或赋值运算符,没有数组类型的左值被转换为存储在指定对象中的值(并且不再是左值); 这称为左值转换....... 如果左值指定了一个自动存储持续时间的对象,该对象可以使用寄存器存储类声明(从未使用过其地址),并且该对象未初始化(未使用初始化程序声明并且未对其进行任何赋值)在使用之前),行为是未定义的.
- 除非它是sizeof运算符,_Alignof运算符或一元&运算符的操作数,或者是用于初始化数组的字符串文字,否则将具有类型''数组类型''的表达式转换为表达式输入''指向类型'的指针,指向数组对象的初始元素,而不是左值.如果数组对象具有寄存器存储类,则行为未定义.
问题:下标数组是否计为获取对象的地址?
下面的文字似乎暗示下标数组需要转换为指针,如果没有获取地址似乎无法做到:
6.5.2.1数组下标
约束
- 其中一个表达式应具有类型''指向完整对象类型'的指针,另一个表达式应具有整数类型,结果具有类型''type''.
语义
- 后缀表达式后跟方括号[]中的表达式是数组对象元素的下标名称.下标运算符[]的定义是E1 [E2]与(*((E1)+(E2)))相同.由于适用于binary +运算符的转换规则,如果E1是数组对象(等效地,指向数组对象的初始元素的指针)并且E2是整数,则E1 [E2]指定E2-元素E1(从零开始计数).
这使§6.3.2.1第3段看起来很奇怪.如果订阅需要转换为指针,那么数组怎么可以注册存储类呢?
我想知道变量的初始化方式:
#include <stdio.h>
int main( void )
{
int ghosts[3];
for(int i =0 ; i < 3 ; i++)
printf("%d\n",ghosts[i]);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这给我带来了随机值,例如 -12 2631 131 ..它们来自哪里?
例如,x86-64 Linux 上的 GCC:https ://godbolt.org/z/MooEE3ncc
我有一个猜测来回答我的问题,无论如何它都可能是错误的:
内存的寄存器在“清空”后获得 0 和 1 之间的随机电压,这些值“四舍五入”为 0 或 1,并且这些随机值取决于在某事上?!也许寄存器的制作方式?也许内存的容量会以某种方式发挥作用?甚至可能是温度?!!
为什么打印32767(或其他随机数)?什么是std::cout印刷品?为什么不NULL(或0)?
int main()
{
int a;
std::cout << a;
}
Run Code Online (Sandbox Code Playgroud) 可能重复:
是否已明确指定未初始化的数据行为?
我尝试了以下代码
#include<stdio.h>
void main()
{
int i; \
printf('%d',i);
}
Run Code Online (Sandbox Code Playgroud)
结果给出了VC++中的垃圾值,而tc中的垃圾值为零.什么是正确的价值?默认情况下,未初始化的变量的值是否为零?或者它会包含垃圾值?
接下来是一样的
#include<stdio.h>
void main()
{
int i,j,num;
j=(num>0?0:num*num);
printf("\n%d",j);
}
Run Code Online (Sandbox Code Playgroud)
上面代码的输出是什么?
这是Coursera的一个测验(未评分).问题是,以下代码可能评估的内容是什么?正确的答案是127和0(其他选项崩溃,-1,128.为什么以下代码可能评估为0?我理解为什么它会评估为127.它是否就像char字节未初始化一样简单因此随机?还可以评估0到127之间的任何#吗?
int foo(void) {
char bar[128];
char *baz = &bar[0];
baz[127] = 0;
return strlen(baz);
}
Run Code Online (Sandbox Code Playgroud) 在阅读本文时,我看到了一个我不了解的UB,希望您能澄清一下
size_t f(int x)
{
size_t a;
if(x) // either x nonzero or UB
a = 42;
return a;
}
Run Code Online (Sandbox Code Playgroud)
我猜UB是由于a没有初始化的值,但这不是它定义的行为吗?意思是,无论f(0)变量a是什么, 都将返回其所保存的值(我认为这类似于rand())。我们是否必须知道代码片段返回的值使代码具有明确定义的行为?