C和C++中未定义,未指定和实现定义的行为有什么区别?
c c++ undefined-behavior unspecified-behavior implementation-defined-behavior
在Bjarne Stroustrup的"C++编程语言"第4版36.3.6 STL类操作部分中,以下代码用作链接示例:
void f2()
{
std::string s = "but I have heard it works even if you don't believe in it" ;
s.replace(0, 4, "" ).replace( s.find( "even" ), 4, "only" )
.replace( s.find( " don't" ), 6, "" );
assert( s == "I have heard it works only if you believe in it" ) ;
}
Run Code Online (Sandbox Code Playgroud)
断言失败gcc(看到它直播)和Visual Studio(看到它的实时),但它在使用Clang时没有失败(请参见实时).
为什么我会得到不同的结果?这些编译器是否错误地评估了链接表达式,或者此代码是否表现出某种形式的未指定 …
c++ operator-precedence language-lawyer unspecified-behavior c++11
C语言中未指定行为的一个示例是评估函数参数的顺序.它可能是左右或左右,你只是不知道.这会影响评估方式foo(c++, c)或foo(++c, c)得到评估.
还有什么其他未指明的行为可以让不知情的程序员感到惊讶?
我以前认为在C99中,即使函数的副作用f和g干扰,虽然表达式f() + g()不包含序列点,f并且g会包含一些,所以行为将是未指定的:要么f()之前调用f()之前的g()或g().
我不再那么肯定了.如果编译器内联函数(即使未声明函数,编译器可能决定这样做inline)然后重新排序指令,该怎么办?可能有人得到上述两种不同的结果吗?换句话说,这是未定义的行为吗?
这不是因为我打算写这种东西,而是在静态分析器中为这样的语句选择最佳标签.
c c99 undefined-behavior sequence-points unspecified-behavior
假设我有以下内容:
#include <memory>
struct A { int x; };
class B {
B(int x, std::unique_ptr<A> a);
};
class C : public B {
C(std::unique_ptr<A> a) : B(a->x, std::move(a)) {}
};
Run Code Online (Sandbox Code Playgroud)
如果我正确理解有关"未指定函数参数顺序"的C++规则,则此代码不安全.如果B构造函数的第二个参数首先使用移动构造函数构造,那么a现在包含一个nullptr表达式a->x将触发未定义的行为(可能是段错误).如果首先构造第一个参数,那么一切都将按预期工作.
如果这是一个正常的函数调用,我们可以创建一个临时函数:
auto x = a->x
B b{x, std::move(a)};
Run Code Online (Sandbox Code Playgroud)
但是在类初始化列表中,我们没有自由创建临时变量.
假设我无法改变B,有没有可能的方法来实现上述目标?即unique_ptr在不创建临时的情况下解除引用并移动相同的函数调用表达式?
如果您可以更改B构造函数但不添加新方法,该setX(int)怎么办?那会有帮助吗?
谢谢
c++ initialization move-semantics unspecified-behavior c++11
C11§6.5.7第5段:
结果
E1 >> E2是E1右移位E2位置.如果E1具有无符号类型或者E1具有有符号类型和非负值,则结果的值是商的整数部分E1 / 2*^E2.如果E1具有有符号类型和负值,则结果值是实现定义的.
但是,viva64参考文档说:
Run Code Online (Sandbox Code Playgroud)int B; B = -1 >> 5; // unspecified behavior
我在GCC上运行了这个代码,它总是给出一个输出-1.
所以,标准说"如果E1有一个带符号的类型和一个负值,结果值是实现定义的",但该文档说这-1>>5;是未指定的行为.
那么,-1>>5;C中是否有未指明的行为?哪个是对的?
从我系统的手册页:
void*memmove(void*dst,const void*src,size_t len);
描述
memmove()函数将字符串src中的len个字节复制到字符串dst.
两个字符串可能重叠 ; 副本总是以非破坏性的
方式完成.
从C99标准:
6.5.8.5比较两个指针时,结果取决于指向的对象的地址空间中的相对位置.如果指向对象或不完整类型的两个指针都指向同一个对象,或者两个指针都指向同一个数组对象的最后一个元素,则它们相等.如果指向的对象是同一聚合对象的成员,则指向稍后声明的结构成员的指针比指向结构中先前声明的成员的指针大,指向具有较大下标值的数组元素的指针比指向同一数组的元素的指针大.具有较低的下标值.指向同一个union对象的成员的所有指针都比较相等.如果表达
P指向数组对象的元素,表达式Q指向同一数组对象的最后一个元素,指针表达式Q+1比较大于P.在所有其他情况下,行为 未定义.
重点是我的.
的参数dst和src可被转化为指针char以便减轻严格别名的问题,但有可能以比较两个指针可以指向内部的不同的块,以便做以正确的顺序的拷贝的情况下,它们指向相同的块内?
显而易见的解决方案是if (src < dst),但未定义如果src和dst指向不同的块."未定义"意味着您甚至不应该假设条件返回0或1(这在标准词汇表中称为"未指定").
另一种选择是if ((uintptr_t)src < (uintptr_t)dst),至少是未指定的,但我不确定标准是否保证何时src < dst定义,它等同于(uintptr_t)src < (uintptr_t)dst).指针比较是从指针算法定义的.例如,当我在添加时阅读第6.5.6节时,在我看来,指针算法可以与uintptr_t算术相反的方向,即兼容的编译器可能具有,当p类型为char*:
((uintptr_t)p)+1==((uintptr_t)(p-1)
Run Code Online (Sandbox Code Playgroud)
这只是一个例子.一般来说,将指针转换为整数时,似乎可以保证很少.
这是一个纯粹的学术问题,因为memmove它与编译器一起提供.在实践中,编译器作者可以简单地将未定义的指针比较提升为未指定的行为,或者使用相关的编译指示强制其编译器memmove正确编译它们.例如,此实现具有以下代码段:
if ((uintptr_t)dst < (uintptr_t)src) {
/*
* As author/maintainer of libc, take advantage …Run Code Online (Sandbox Code Playgroud) 我依稀记得在复合表达式中的多个操作数修改同一个对象时,在某处读取它是未定义的行为.
我相信下面的代码中显示了这个UB的一个例子,但是我编译了g ++,clang ++和visual studio,所有这些都打印出相同的值,并且似乎无法在不同的编译器中产生不可预测的值.
#include <iostream>
int a( int& lhs ) { lhs -= 4; return lhs; }
int b( int& lhs ) { lhs *= 7; return lhs; }
int c( int& lhs ) { lhs += 1; return lhs; }
int d( int& lhs ) { lhs += 2; return lhs; }
int e( int& lhs ) { lhs *= 3; return lhs; }
int main( int argc, char **argv )
{
int i = 100;
int …Run Code Online (Sandbox Code Playgroud) c++ undefined-behavior language-lawyer unspecified-behavior c++11
考虑以下代码:
#include <cctype>
#include <functional>
#include <iostream>
int main()
{
std::invoke(std::boolalpha, std::cout); // #1
using ctype_func = int(*)(int);
char c = std::invoke(static_cast<ctype_func>(std::tolower), 'A'); // #2
std::cout << c << "\n";
}
Run Code Online (Sandbox Code Playgroud)
在此,对的两个调用std::invoke已标记为将来参考。预期的输出是:
a
Run Code Online (Sandbox Code Playgroud)
在C ++ 20中可以保证预期的输出吗?
(注意:有两个函数tolower,一个称为in <cctype>,另一个称为in <locale>。引入了显式强制转换以选择所需的重载。)
c++ c++-standard-library language-lawyer unspecified-behavior c++20
今天我遇到了一些代码,这些代码在clang ++(3.7-git),g ++(4.9.2)和Visual Studio 2013上表现出不同的行为.经过一些减少,我想出了这个代码片段,突出了这个问题:
#include <iostream>
using namespace std;
int len_ = -1;
char *buffer(int size_)
{
cout << "len_: " << len_ << endl;
return new char[size_];
}
int main(int argc, char *argv[])
{
int len = 10;
buffer(len+1)[len_ = len] = '\0';
cout << "len_: " << len_ << endl;
}
Run Code Online (Sandbox Code Playgroud)
g ++(4.9.2)给出了这个输出:
len_: -1
len_: 10
Run Code Online (Sandbox Code Playgroud)
因此g ++将参数计算为缓冲区,然后缓冲区(..)本身,然后评估数组运算符的索引参数.直观地说,这对我来说很有意义.
clang(3.7-git)和Visual Studio 2013都给出:
len_: 10
len_: 10
Run Code Online (Sandbox Code Playgroud)
我想clang和VS2013会在它降到缓冲区(..)之前评估所有可能的东西.这对我来说不太直观.
我想我的问题的要点是这是否是未定义行为的明显案例.
编辑:感谢您清除此问题,未指明的行为是我应该使用的术语.