中的未定义行为,标准转换运算符const_cast

现在我们需要一个程序从控制台读入一个 INT
型整数,然后输出其绝对值,你可能闭着眼睛就会写出下面的代码:

const_cast转换符是用来移除变量的const或volatile限定符。

#include <iostream>int main(){ int n; std::cin >> n; std::cout << abs << std::endl;}

对于const变量,我们不能修改它的值,这是这个限定符最直接的表现。但是我们就是想违背它的限定希望修改其内容怎么办呢?

等下,好好思考两分钟,然后写几个测试例子跑一下程序。那么你找出程序存在的问题了吗?好了,欢迎走进未定义行为
(Undefined Behavior) 的世界。

下边的代码显然是达不到目的的:

图片 1未定义行为

const int constant = 10;
int modifier = constant;

文章一开始的程序中用到了 abs 求绝对值函数,当n为 INT_MIN
时,函数返回什么呢?C++ 标准中有这么一条:

因为对modifier的修改并不会影响到constant,这暗示了一点:const_cast转换符也不该用在对象数据上,因为这样的转换得到的两个变量/对象并没有相关性。

If during the evaluation of an expression, the result is not
mathematically defined or not in the range of representable values for
its type, the behavior is undefined.

只有用指针或者引用,让变量指向同一个地址才是解决方案,可惜下边的代码在C++中也是编译不过的:

在一个2进制系统中,当 n 是 INT_MIN 时,int abs 返回的值超出了 int
的范围,所以这将导致一个未定义行为。很多时候,标准过于精炼,不便于我们快速查找,因此我们可以在
cppreference 找到需要的信息,以 abs函数为例,cppreference
明确指出可能导致未定义行为:

const int constant = 21;
int* modifier = &constant 
// Error: invalid conversion from 'const int*' to 'int*'

Computes the absolute value of an integer number. The behavior is
undefined if the result cannot be represented by the return type.

(上边的代码在C中是可以编译的,最多会得到一个warning,所在在C中上一步就可以开始对constant里面的数据胡作非为了)

那么到底什么是未定义行为呢?简单来说,就是某个操作逻辑上是不合法的,比如越界访问数组等,但是C++
标准并没有告诉我们遇到这种情况该如何去处理。

把constant交给非const的引用也是不行的。

我们知道在大部分语言(比如 Python 和
Java)中,一个语句要么按照我们的预期正确执行,要么立即抛出异常。但是在
C++
中,还有一种情况就是,某条语句并没有按照预期执行,但是程序还是可以继续执行(C++标准没有告诉怎么继续执行)。只不过程序的行为已经不可预测了,也就是说程序可能发生运行时错误,也可能给出错误的结果,甚至还可能给出正确的结果。

const int constant = 21;
int& modifier = constant;
// Error: invalid initialization of reference of type 'int&' from expression of type 'const int' 

有一点需要注意的是,对于有的未定义行为,现代编译器有时候可以给出警告,或者是编译失败的提示信息。此外,不同编译器对于未定义行为的处理方式也不同。

于是const_cast就出来消灭const,以求引起程序世界的混乱。

C++ 标准中有大量的未定义行为,如果在标准中查找
undefined behavior,将会看到几十条相关内容。如此众多的未定义行为,无疑给我们带来了许多麻烦,下面我们将列出一些常见的未定义行为,写程序时应该尽量避免。

下边的代码就顺利编译通过了:

指针相关的常见未定义行为有如下内容:

const int constant = 21;
const int* const_p = &constant;
int* modifier = const_cast<int*>(const_p);
*modifier = 7;
  • 解引用 nullptr 指针;
  • 解引用一个未初始化的指针;
  • 解引用 new 操作失败返回的指针;
  • 指针访问越界(解引用一个超出数组边界的指针);
  • 解引用一个指向已销毁对象的指针;

 

解引用一个指向已销毁对象的指针,有时候很容易就会犯这个错误,例如在函数中返回局部指针地址。
一些简单的错误代码如下:

为何要去除const限定?

#include <iostream>int * get{ return &tmp;}int main(){ int *foo = get; std::cout << *foo << std::endl; // Undefined Behavior; int arr[] = {1,2,3,4}; std::cout << * << std::endl; // Undefined Behavior; int *bar=0; *bar = 2; // Undefined Behavior; std::cout << *bar << std::endl; return 0;}

从前面代码中已经看到,我们不能对constant进行修改,但是我们可以对modifier进行重新赋值。

其他常见未定义行为如下:

但是但是,程序世界真的混乱了吗?我们真的通过modifier修改了constant的值了吗?修改const变量的数据真的是C++去const的目的吗?

发表评论

电子邮件地址不会被公开。 必填项已用*标注