当我们创建一个变量并且不对其进行初始化时,会为其分配一些称为垃圾值的(随机)数字.
c compiler-construction variables programming-languages initialization
这是代码:
unsigned int a; // a is indeterminate
unsigned long long b = 1; // b is initialized to 1
std::memcpy(&a, &b, sizeof(unsigned int));
unsigned int c = a; // Is this not undefined behavior? (Implementation-defined behavior?)
Run Code Online (Sandbox Code Playgroud)
a该标准是否保证在我们进行初始化的地方是一个确定的值c?Cppreference说:
void* memcpy( void* dest, const void* src, std::size_t count );将
count字节从指向的对象复制到src指向的对象dest。这两个对象都重新解释为的数组unsigned char。
但是我看不到cppreference中的任何地方,如果说这样不确定的值被“复制到”,它将变成确定的。
从标准看来,这类似于:
unsigned int a; // a is indeterminate
unsigned long long b = 1; // b …Run Code Online (Sandbox Code Playgroud) 我意识到差异可以忽略不计,但是在尝试将无符号长度归零时哪个更有效?
unsigned long x;
...
x=0;
--OR--
x^=x;
Run Code Online (Sandbox Code Playgroud)
泰勒
在ia64上,每个64位寄存器实际上是65位.额外的位被称为"NaT",代表"不是一件事".当寄存器不包含有效值时,该位置1.可以将其视为浮点NaN的整数版本.
NaT位最常见于推测执行.在ia64上有一种特殊形式的加载指令,它试图从内存中加载值,但是如果加载失败(因为内存被分页或地址无效),那么所有发生的事情都会发生,而不是引发页面错误是NaT位被设置,并继续执行.
NaT的所有数学运算都会再次产生NaT.
源文章接着解释了在推测加载期间寄存器如何最终具有NaT表示并发出以下注释:
对于你看,如果你有一个值为NaT的寄存器,并且你以错误的方式呼吸(例如,尝试将其值保存到内存中),处理器将引发STATUS_REG_NAT_CONSUMPTION异常.
似乎从陷阱表示的其他堆栈溢出答案,
"任何类型(unsigned char除外)可能有陷阱表示".
这个链接说明了
标准给出的关于访问未初始化数据的唯一保证是unsigned char类型没有陷阱表示,并且该padding没有陷阱表示.
如果这样的寄存器(具有NaT位设置的寄存器)被分配用于存储未初始化的无符号字符(类似于下面的缺陷报告中的代码片段),那么如何根据ISO C11处理?
以下缺陷报告是否指向同一问题,是否在ISO C11中得到纠正?
如果不是如何处理这种特殊情况?
如果左值指定了一个自动存储持续时间的对象,该对象可能已经使用寄存器存储类声明(从未使用过其地址),并且该对象未初始化(未使用初始化程序声明,并且未对其进行任何赋值)使用),行为未定义
在"更改为C1X"部分的缺陷报告末尾添加上述内容是否处理此案例?
以下函数在C90下具有未定义的行为,但在C99下似乎严格符合
int foo(void) {
unsigned char uc;
return uc + 1 >= 0;
}
Run Code Online (Sandbox Code Playgroud) 我一直以为C不接受NULL参数,直到我开始学习指针.在某些编程语言中,如python for one,可以将NULL参数作为参数传递,但在CI中始终认为这会导致未定义的行为.
我的问题只是一个好奇心,一个功能怎么可能,比如...
waitpid(child_pid, &status, options); //pointer &status
Run Code Online (Sandbox Code Playgroud)
...接受一个NULL指针作为参数而不会运行到未定义的行为,不是NULL指针只是指向什么?
简单地说,为什么这在C中可以接受?
我正在讨论使用具有不确定值的变量导致未指定的行为,而不是未定义的行为,如此处所述。这假设具有自动存储持续时间的变量已获取其地址并且陷阱表示不适用。
在具体情况下,讨论了ptr之后发生的情况free(ptr),在这种情况下 C17 6.2.4 适用:
当指针指向(或刚刚过去)的对象到达其生命周期结束时,指针的值变得不确定。
我做了这个例子:
#include <stdlib.h>
#include <stdio.h>
int main (void)
{
int* ptr = malloc(sizeof *ptr);
int* garbage;
int*volatile* dummy = &garbage; // take the address
free(ptr);
puts("This should always print");
fflush(stdout);
if(ptr == garbage)
{
puts("Didn't see that one coming.");
}
else
{
puts("I expect this to happen");
}
puts("This should always print");
}
Run Code Online (Sandbox Code Playgroud)
我提出的论点是,从理论上讲,我们无法知道是ptr == garbage真是假,因为此时它们都是不确定的。因此编译器甚至不需要读取这些内存位置 - 因为它可以推断两个指针都保存不确定的值,所以在优化期间可以根据需要自由地将表达式评估为 true 或 false。(实际上大多数编译器可能不会这样做。)
我在 x86_64 编译器 …
在C++ 14标准(n3797)中,左值转换的左值部分如下所示(强调我的):
4.1左值 - 右值 - 转换[conv.lval]
T可以将非函数非数组类型的glvalue(3.10)转换为prvalue.如果T是不完整类型,则需要进行此转换的程序格式不正确.如果T是非类类型,则prvalue的类型是cv-nonqualified versionT.否则prvalue的类型是T.当在未评估的操作数或其子表达式中发生左值到右值转换时(第5条),不访问引用对象中包含的值.在所有其他情况下,转换结果根据以下规则确定:
- 如果
T是(可能是cv限定的)std::nullptr_t则结果是空指针常量.- 否则,如果
T有类类型,则转换复制 -T从glvalue 初始化一个临时类型,转换的结果是临时的prvalue.- 否则,如果glvalue引用的对象包含无效指针值,则行为是实现定义的.
- 否则,如果
T是(可能是cv限定的)无符号字符类型,并且glvalue引用的对象包含不确定的值,并且该对象没有自动存储持续时间,或者glvalue是一元运算&符的操作数,或者它是绑定到引用,结果是一个未指定的值.- 否则,如果glvalue引用的对象具有不确定的值,则行为未定义.
- 否则,glvalue指示的对象是prvalue结果.
- [ 注:另见3.10]
这一段有什么意义(粗体)?
如果此段落不在此处,那么它适用的情况将导致未定义的行为.通常,我希望unsigned char在具有不确定值时访问值会导致未定义的行为.但是,这一段意味着
&或将其绑定到引用,或者unsigned char没有自动存储时间,然后转换产生一个未指定的值,而不是未定义的行为.
我是否正确地得出这个程序的结论:
#include <new>
#include <iostream>
// using T = int;
using T = unsigned char;
int main() {
T * array = new T[500];
for …Run Code Online (Sandbox Code Playgroud) c++ undefined-behavior language-lawyer lvalue-to-rvalue c++14
假设我有一大块动态分配的数据:
void* allocate (size_t n)
{
void* foo = malloc(n);
...
return foo;
}
Run Code Online (Sandbox Code Playgroud)
我希望将指向的数据foo用作特殊类型type_t.但我想稍后这样做,而不是在分配期间.为了给分配的数据一个有效的类型,我可以这样做:
void* allocate (size_t n)
{
void* foo = malloc(n);
(void) *(type_t*)foo;
...
return foo
}
Run Code Online (Sandbox Code Playgroud)
根据C11 6.5/6,这个左值访问应该是有效的类型type_t:
对于没有声明类型的对象的所有其他访问,对象的有效类型只是用于访问的左值的类型.
但是,该行不(void) *(type_t*)foo;包含任何副作用,因此编译器应该可以自由地优化它,我不希望它生成任何实际的机器代码.
我的问题是:像上面这样的技巧安全吗?是否将数据作为副作用提供有效类型?或者通过优化代码,编译器是否也会优化掉有效类型的选择?
也就是说,使用上面的左值访问技巧,如果我现在调用上面这样的函数:
int* i = allocate(sizeof(int));
*i = something;
Run Code Online (Sandbox Code Playgroud)
这是否会导致严格的别名违规UB,或者现在是有效的类型int?
我知道使用未初始化的变量是错误的,但问题出现在未初始化的整数的上下文中,在我稍后在代码中为其赋值之前我没有使用.
我应该期待得到奇怪的结果吗?或者这只是不好的做法?
我是一名新生计算机科学专业的学生,对任何错误都感到抱歉!