Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/apache-flex/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;编译器优化不正确的分层向下转换以导致真正的未定义行为_C++_Compiler Optimization_Undefined Behavior - Fatal编程技术网

C++ C++;编译器优化不正确的分层向下转换以导致真正的未定义行为

C++ C++;编译器优化不正确的分层向下转换以导致真正的未定义行为,c++,compiler-optimization,undefined-behavior,C++,Compiler Optimization,Undefined Behavior,考虑以下示例: class Base { public: int data_; }; class Derived : public Base { public: void fun() { ::std::cout << "Hi, I'm " << this << ::std::endl; } }; int main() { Base base; Derived *derived = static_cast<Derived

考虑以下示例:

class Base {
public:
    int data_;
};

class Derived : public Base {
public:
    void fun() { ::std::cout << "Hi, I'm " << this << ::std::endl; }
};

int main() {
    Base base;
    Derived *derived = static_cast<Derived*>(&base); // Undefined behavior!

    derived->fun(); 

    return 0;
}
类基{
公众:
int数据;
};
派生类:公共基{
公众:

void fun(){::std::cout在同一台机器上无限次地运行同一代码,如果幸运的话,您可能会看到它工作不正确且出人意料


需要理解的是,未定义行为(undefined behavior,UB)并不意味着它肯定不会按预期运行;它可能会按预期运行,1次、2次、10次甚至无限次。UB只是意味着它不能保证按预期运行。

函数
fun()
实际上并不做任何与
这个
指针有关的事情,因为它不是一个虚拟函数,所以不需要特别查找函数。基本上,它的调用与任何正常(非成员)函数一样,带有一个坏的
这个
指针。它只是不会崩溃,这是完全有效的未定义行为(如果这不是矛盾的话)。

你必须理解你的代码在做什么,然后你才能看到它没有做错什么。 “this”是编译器为您生成的隐藏指针

class Base
{
public:
    int data_;
};

class Derived : public Base
{

};


void fun(Derived* pThis) 
{
::std::cout << "Hi, I'm " << pThis << ::std::endl; 
}

//because you're JUST getting numerical value of a pointer, it can be same as:
void fun(void* pThis) 
{
    ::std::cout << "Hi, I'm " << pThis << ::std::endl; 
}

//but hey, even this is still same:
void fun(unsigned int pThis) 
{
    ::std::cout << "Hi, I'm " << pThis << ::std::endl; 
}
类基
{
公众:
int数据;
};
派生类:公共基
{
};
虚无乐趣(派生*pThis)
{
::std::cout fun();被显式声明为UB。但是,这种行为通常在编译器文档中关于调用约定的部分中定义。

我应该写“对于我所知道的所有编译器,没有什么会出错。”

对代码的注释是不正确的

Derived *derived = static_cast<Derived*>(&base);
derived->fun(); // Undefined behavior!
Derived*Derived=static_cast(&base);
派生->乐趣();//未定义的行为!
更正版本:

Derived *derived = static_cast<Derived*>(&base);  // Undefined behavior!
derived->fun(); // Uses result of undefined behavior
Derived*Derived=static_cast(&base);//未定义的行为!
派生->乐趣();//使用未定义行为的结果
未定义的行为始于
静态\u cast
。任何后续使用此生而不好的指针的行为都是未定义的行为。未定义的行为是编译器供应商的免监禁卡。编译器的几乎所有响应都符合标准

没有什么可以阻止编译器拒绝您的强制转换。一个好的编译器很可能会为该
静态\u强制转换发出致命的编译错误。在这种情况下,很容易看到冲突。一般来说,这不容易看到,所以大多数编译器都不需要检查

大多数编译器采用最简单的方法。在这种情况下,最简单的方法是假设指向类
Base
实例的指针是指向类
派生的
实例的指针
是相当良性的,在这种情况下,简单的解决方法会产生相当良性的结果


仅仅因为你得到了一个良好的结果并不意味着一切都很酷。它仍然是未定义的行为。最好的办法是永远不要依赖未定义的行为。

例如,编译器可能会优化代码。 考虑不同的程序:

if(some_very_complex_condition)
{
  // here is your original snippet:

  Base base;
  Derived *derived = static_cast<Derived*>(&base); // Undefined behavior!

  derived->fun(); 
}
if(某些非常复杂的情况)
{
//以下是您的原始代码片段:
基地;
派生的*Derived=static_cast(&base);//未定义的行为!
派生->乐趣();
}
编译器可以

(1) 检测未定义的行为

(2) 假设程序不应该公开未定义的行为

因此(编译器决定)_某些非常复杂的条件应始终为false。假设这一点,编译器可能会将整个代码消除为不可访问

[编辑]编译器如何消除“服务于”UB情况的代码的真实示例:


此代码经常工作的实际原因是,任何破坏此代码的内容都会在版本中进行优化/针对性能构建进行优化。但是,任何侧重于查找错误(如调试构建)的编译器设置都更有可能在这一点上出错

在这些情况下,您的假设(“注意,
派生的
无论如何不应该携带任何附加数据”)不成立。为了便于调试,它肯定应该成立

一个稍微复杂一点的例子更为棘手:

class Base {
public:
    int data_;
    virtual void bar() { std::cout << "Base\n"; }
};

class Derived : public Base {
public:
    void fun() { ::std::cout << "Hi, I'm " << this << ::std::endl; }
    virtual void bar() { std::cout << "Derived\n"; }
};

int main() {
    Base base;
    Derived *derived = static_cast<Derived*>(&base); // Undefined behavior!

    derived->fun(); 
    derived->bar();
}
类基{
公众:
int数据;

virtual void bar(){std::cout“真正的未定义行为”是什么意思?标准中定义的未定义行为难道还不够真实吗?@Plasmah,显然,任何编写此类代码的人都希望它在本例中有一些真实的行为(打印“Hi!”)。但是“未定义行为”在标准中,这意味着编译器可以用另一种行为生成代码。这种情况我称之为“真正的未定义行为”我认为这是有道理的。你如何定义“不正确”呢?如果标准规定它是未定义的,那么它所做的定义肯定既不正确也不错误。我不确定你要做什么,但一般来说,未定义的行为场景是我想你应该避免的。@Component10,因为这是UB,无论如何就符合标准而言,编译器所做的是“正确的”。UB使编译器/运行时环境可以自由地做任何事情,并且仍然符合标准。(程序员应该避免UB的众多原因之一)@anxieux:如果你的编译器能够检测到这一点并启动nethack,那么这也是真正的未定义行为吗?你为什么还要关心特定编译器中的未定义行为,对于某些特定代码,是否会产生一些看似可靠的行为?它仍然是UB。好的,我们可以添加打印
数据_
进入
fun()
主体。这一部分是不相关的(如果没有误导的话)>>“函数fun()实际上不做任何与此指针是什么有关的事情,并且由于它不是虚拟函数,因此不需要特别查找函数”好的,打印这个怎么样?@EJP:因为它是UB,这意味着即使有虚拟函数,它也可以工作。如果是这样,那么pa