C和C++中未定义,未指定和实现定义的行为有什么区别?
c c++ undefined-behavior unspecified-behavior implementation-defined-behavior
我知道C++ 中的"未定义行为"几乎可以让编译器做任何想做的事情.但是,我遇到了让我感到惊讶的崩溃,因为我认为代码足够安全.
在这种情况下,真正的问题仅发生在使用特定编译器的特定平台上,并且仅在启用了优化时才发生.
我尝试了几件事来重现问题并将其简化到最大程度.这是一个名为的函数的摘录Serialize,它将获取bool参数,并将字符串true或复制false到现有的目标缓冲区.
如果bool参数是未初始化的值,那么这个函数是否会在代码审查中,没有办法告诉它实际上可能会崩溃?
// Zero-filled global buffer of 16 characters
char destBuffer[16];
void Serialize(bool boolValue) {
// Determine which string to print based on boolValue
const char* whichString = boolValue ? "true" : "false";
// Compute the length of the string we selected
const size_t len = strlen(whichString);
// Copy string into destination buffer, which is zero-filled (thus already null-terminated)
memcpy(destBuffer, whichString, len);
}
Run Code Online (Sandbox Code Playgroud)
如果使用clang 5.0.0 +优化执行此代码,它将/可能崩溃.
boolValue ? "true" …
我总是认为,如果我声明这三个变量,它们都将具有值0
int column, row, index = 0;
Run Code Online (Sandbox Code Playgroud)
但我发现只有索引等于零而其他的都是垃圾,如844553和2423445.
如何在不声明新行的每个变量的情况下将所有这些变量初始化为零?
如果我有:
unsigned int x;
x -= x;
Run Code Online (Sandbox Code Playgroud)
很明显,x 应该这样表达后是零,但到处看,他们说的行为,这种代码是不确定的,而不是仅仅值x(直到减法之前).
两个问题:
这段代码的行为确实未定义吗?
(例如,代码在兼容系统上崩溃[或更糟]?)
如果是这样,为什么 C表示行为是未定义的,当非常清楚x这里应该为零时?
即不在此定义行为给出的优势是什么?
很明显,编译器可以简单地使用它在变量中认为"方便"的任何垃圾值,并且它可以按预期工作......这种方法有什么问题?
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”。那么具体是在哪里提到的呢?
我想知道变量的初始化方式:
#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,并且这些随机值取决于在某事上?!也许寄存器的制作方式?也许内存的容量会以某种方式发挥作用?甚至可能是温度?!!
C中“静态全局变量”和“非静态全局变量”有什么区别?
请举例说明它们的不同之处。
(因为,全局静态变量和简单全局变量在整个程序中都保持活动状态,并且可以在任何块中使用。我很困惑如何区分它们。)有人可以用代码解释这一点吗?
我的老师告诉我,他正在研究编译器,他发现c++中的垃圾值可以用这样的公式来预期或计算=
该数据类型的范围 - 数据类型的大小 = 该数据类型的垃圾值。
例如:对于整数,它应该是 -
2,147,483,647(int 范围) - 4(int 大小)= 2,147,483,643
int 的下一个内存每次都是 - 4 位。
我预计结果是 2,147,483,643
当我自己测试它时,我没有为我工作这是我的代码 -
#include <iostream>
using namespace std;
int main(){
int a,c,d;
cout<<a<<" "<<c<< " " <<d;
}
Run Code Online (Sandbox Code Playgroud)
输出 = 4352496 7339908 4352384
我的预期 = 2,147,483,643 , 2,147,483,639 , 2,147,483,635
这是否意味着我的老师错了?他学错东西了?他是在谈论另一个范围和另一个尺寸吗?我误解了什么吗?任何人都可以请清除我吗?
我在谷歌上搜索这个词但找不到任何解决方案。我发现这令人惊讶,甚至可以计算垃圾值吗?
c ×5
c++ ×4
abi ×1
assembly ×1
implementation-defined-behavior ×1
llvm ×1
llvm-codegen ×1
scope ×1
variables ×1