c – 通过重用的非`constst`名称修改动态分配的`const`对象是否合法?

前端之家收集整理的这篇文章主要介绍了c – 通过重用的非`constst`名称修改动态分配的`const`对象是否合法?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
考虑以下程序:
#include <iostream>

int main()
{
   int x = 0;
   const int* px = new (&x) const int(0);
   x = 1;
   std::cout << *px;  // 1?
}

compiles under GCC 4.8(并产生“预期的”输出),但我怀疑它完全是UB,因为动态对象的类型为const int(which remains part of the type).但是,那么,如果是这样,为什么编译器不会阻止我违反const-correctness?

解决方法

tl;博士:是的,它是未定义的行为.不,编译器不会诊断它.

通常,编译器不会(有时不能)诊断UB.更明显的const-correctness违规示例实际上是不正确的,can be diagnosed

#include <iostream>

int main()
{
   const int x = 0;
   x = 1;
   std::cout << x;
}

// g++-4.8 -std=c++11 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
// main.cpp: In function 'int main()':
// main.cpp:6:6: error: assignment of read-only variable 'x'
//     x = 1;
//       ^

但是,除此之外,它won’t stop you from performing obvious violations of const-correctness

#include <iostream>

int main()
{
    const int x = 0;
    *const_cast<int*>(&x) = 1;
    std::cout << x;
}

// Output: 1

所以,回到你的代码片段,我不希望那里的编译器诊断太多.

但是,您的代码确实会调用未定义的行为.我们来看看吧:

#include <iostream>

int main()
{
   int x = 0;
   const int* px = new (&x) const int(0);
   x = 1;
   std::cout << *px;  // 1?
}

这是发生的事情:

>创建具有自动存储持续时间的int,初始化为0.
>名称x指的是此对象.
>使用动态存储持续时间创建const int,重新使用int的存储.
> int的生命周期结束1,2.
> x现在指的是const int3.
>虽然名称x的类型为int,但它现在引用了一个const int,因此赋值是undefined4.

这是一个有趣的漏洞,你可以用来“绕过”const-correctness,只要原始的int不存在于只读内存中,它甚至可能不会导致崩溃.

然而,它仍然是未定义的,虽然我看不出可能会执行哪些优化可能会破坏作业和随后的阅读,但你肯定会对各种意想不到的肮脏行为持开放态度,比如你后花园里的自发火山或者你所有的努力-earned rep被转换成英镑并存入我的银行账户(谢谢!).

脚注1

[C++11: 3.8/1]: @H_301_43@[..] The lifetime of an object of type T ends when:

  • if T is a class type with a non-trivial destructor (12.4),the destructor call starts,or
  • the storage which the object occupies is reused or released.

脚注2

请注意,我没有必要在int对象上显式调用“析构函数”.这主要是因为这样的对象没有析构函数,但即使我选择了一个简单的类T而不是int,我也可能不需要显式的析构函数调用

[C++11: 3.8/4]: A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. For an object of a class type with a non-trivial destructor,the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however,if there is no explicit call to the destructor or if a delete-expression (5.3.5) is not used to release the storage,the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.

脚注3

[C++11: 3.8/7]: If,after the lifetime of an object has ended and before the storage which the object occupied is reused or released,a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object,a reference that referred to the original object,or the name of the original object will automatically refer to the new object and,once the lifetime of the new object has started,can be used to manipulate the new object,if:

  • the storage for the new object exactly overlays the storage location which the original object occupied,and
  • the new object is of the same type as the original object (ignoring the top-level cv-qualifiers),and
  • the type of the original object is not const-qualified,and,if a class type,does not contain any non-static data member whose type is const-qualified or a reference type,and
  • the original object was a most derived object (1.8) of type T and the new object is a most derived object of type T (that is,they are not base class subobjects). @H_301_43@[..]

脚注4

[C++11: 7.1.6.1/4]: Except that any class member declared mutable (7.1.1) can be modified,any attempt to modify a const object during its lifetime (3.8) results in undefined behavior. @H_301_43@[..]

(示例如下,与您的代码段类似,但不完全相同.)

原文链接:https://www.f2er.com/c/119879.html

猜你在找的C&C++相关文章