c++ - 使用char类型的glvalue访问另一种类型的对象时是否不需要导致UB
问题描述
int main(){
int v = 1;
char* ptr = reinterpret_cast<char*>(&v);
char r = *ptr; //#1
}
在这个片段中,表达式 ptr 指向一个 int 类型的对象,根据:
expr.static.cast#13
否则,指针值不会因转换而改变。
根据expr.unary#op-1 ,间接ptr
将产生一个表示对象ptr
指向的左值
结果是一个左值,引用表达式指向的对象或函数。
根据basic.lval#11,使用允许类型的 glvalue 访问对象不会导致 UB
如果程序尝试通过类型与以下类型之一不相似 ([conv.qual]) 的泛左值访问 ([defns.access]) 对象的存储值,则行为未定义:
- char、unsigned char 或 std::byte 类型。
它似乎也没有违反以下规则:
expr#pre-4
如果在计算表达式期间,结果未在数学上定义或不在其 type 的可表示值范围内,则行为未定义。
假设测试情况下的宽度char
为8位,其范围为[-128, 127]。的v
值为1。那么,这是否意味着片段#1
不会导致 UB?
作为对比,给定以下示例
int main(){
int v = 2147483647; // or any value greater than 127
char* ptr = reinterpret_cast<char*>(&v);
char r = *ptr; //#2
}
#2
会是UB,对吧?
解决方案
语言的意图是两个片段都是实现定义的。我相信他们是,直到 C++17 中断了对该语言功能的支持。请参阅此处的缺陷报告。据我所知,这在 C++20 中还没有得到修复。
目前,访问内存表示的可移植解决方法是使用std::memcpy
(示例):
#include <cstring>
char foo(int v){
return *reinterpret_cast<char*>(&v);
}
char bar(int v)
{
char buffer[sizeof(v)];
std::memcpy(buffer, &v, sizeof(v));
return *buffer;
}
foo
在技术上是 UB,而bar
定义明确。原因是foo
UB是遗漏的。标准未能定义的任何内容都是定义为 UB,并且该标准在其当前状态下无法定义此代码的行为。
bar
生成与 gcc 10 相同的程序集foo
。对于简单的情况,实际副本已被优化。
关于你的理性,推理似乎是合理的,除了我认为定义一元operator*
(expr.static.cast#13)的规则在这种情况下没有你期望的效果。指针必须指向底层表示,正如链接缺陷所描述的那样,它的定义很差。指针的值不变的事实并不能减轻它指向不同对象的事实。如果对象的类型不同,C++ 允许对象具有相同的地址,例如标准布局类中的第一个成员与拥有的实例共享相同的地址。
请注意,关于片段#1,作者是缺陷报告得出与您相同的结论,但我不同意。但由于我们正在处理一种语言缺陷,并且与国家意图相冲突,因此很难明确证明一种行为是正确的。众所周知,这些论点所依据的基本规则在这种特殊情况下存在缺陷。
推荐阅读
- spring-boot - 生成 PDF / XLSX 文件作为 REST API 的附件
- c# - 更新 elsticsearch 索引 upsert 文档不起作用
- reactjs - ReactJS 通过 ref 渲染元素
- android - Phonegap Audio 没有得到正确的链接
- javascript - 蓝鸟承诺订单问题
- pycharm - PyCharm:如何在提示输入参数的情况下运行?
- python - 使用时覆盖python文件?
- c# - Digest Auth - 电子邮件地址用户名的编码
- arrays - 带单选按钮的角度过滤数组
- sql - 如何检查日期在 SQL 中的两个日期之间?