当前位置: 首页 > 知识库问答 >
问题:

使用不触发UB的reinterpret_cast的示例

梁丘翔
2023-03-14

阅读https://en.cppreference.com/w/cpp/language/reinterpret_cast我想知道reinterpret_cast的哪些用例不是UB并且在实践中使用?

上面的描述包含了许多情况,在这些情况下,将指针转换成其他类型是合法的,然后再转换回来也是合法的。但这似乎没什么实际用途。除了通过< code > char * /< code > byte * 指针访问之外,通过< code>reinterpret_cast指针访问html" target="_blank">对象大多是UB,因为违反了严格别名(和/或对齐)。

一个有用的例外是将整数常量强制转换为指针并访问目标对象,这对于操作硬件寄存器(以μC为单位)很有用。

有人能告诉我一些在实践中使用的reinterpret_cast相关性的真实用例吗?

共有1个答案

王飞英
2023-03-14

想到的一些例子:

>

  • 读取/写入可简单复制的对象的对象表示形式,例如将对象的字节表示形式写入文件并将其读回:

    // T must be trivially-copyable object type!
    T obj;
    
    //...
    
    std::ofstream file(/*...*/);
    file.write(reinterpret_cast<char*>(obj), sizeof(obj));
    
    //...
    
    std::ifstream file(/*...*/);
    file.read(reinterpret_cast<char*>(obj), sizeof(obj));
    

    从技术上讲,目前还没有真正指定访问对象表示应该如何工作,除了直接传递指向memcpy等的指针。al,但是目前有一个标准提案,至少澄清了读取(而不是写入)对象表示中的单个字节应该如何工作,参见https://github.com/cplusplus/papers/issues/592.

    在C 20之前,<code>reinterpret_cast</code>是访问对象表示的唯一方法,但自从C 20之后,就有了<code>std::bit_cast>/code,这也允许它(具有相同的可复制要求),尽管它总是生成对象表示的副本。

    在同一整型的有符号和无符号变量之间重新解释,尤其是字符串的< code>char和< code>unsigned char,如果API需要无符号字符串,这可能会很有用。

    auto str = "hello world!";
    auto unsigned_str = reinterpret_cast<const unsigned char*>(str);
    

    虽然别名规则允许这样做,但从技术上讲,标准目前并未定义对结果< code>unsigned_str指针的指针算法。但我不明白为什么不是。

    不过,这通常只是为了避免复制而进行的优化。否则,简单地逐字符复制字符串(可以隐式转换)也很好。

    访问嵌套在字节缓冲区中的对象(尤其是在堆栈上):

    alignas(T) std::byte buf[42*sizeof(T)];
    new(buf+sizeof(T)) T;
    
    // later
    
    auto ptr = std::launder(reinterpret_cast<T*>(buf + sizeof(T)));
    

    只要地址 buf sizeof(T)T 适当对齐,缓冲区的类型为 std::byte无符号字符,并且显然具有足够的大小,这就可以工作。表达式还返回指向对象的指针,但可能不希望为每个对象存储该指针。如果缓冲区中存储的所有对象都是同一类型,则在单个此类指针上使用指针算术也可以。

    获取指向特定内存地址的指针。这是否可行以及对于哪些地址值是实现定义的,结果指针的任何可能使用也是如此:

    auto ptr = reinterpret_cast<void*>(0x12345678);
    

    在核心语言中没有替代方法,甚至标准库也没有提供任何方法来获取指向特定地址的指针。

    dlsym(或类似函数)返回的 void* 转换为位于该地址的函数的实际类型。这是否可能以及语义到底是什么,再次由实现定义:

    // my_func is a C linkage function with type `void()` in `my_lib.so`
    
    // error checking omitted!
    
    auto lib = dlopen("my_lib.so", RTLD_LAZY);
    
    auto my_func = reinterpret_cast<void(*)()>(dlsym(lib, "my_func");
    
    my_func();
    

    这种使用是必需的,并且在使用 dlopen/dlsym 的动态加载工具时没有替代方法。

    各种往返转换对于存储指针值或类型擦除可能很有用。

    对象指针通过<code>void*</code>的往返只需要两侧的<code>static_cast</code>,而对象指针上的<code〕reinterpret_cast</code>是根据两步<code>到(cv限定)<code>void*定义的。

    对象指针通过std::uintptr_tstd::intptr_t,或其他大到足以容纳所有指针值的整数类型的往返,对于具有可以序列化的指针值表示可能很有用(尽管我不确定这是否真的有用)。然而,这些类型中是否存在是由实现定义的。通常它们会存在,但是标准允许内存地址不能表示为单个整数值或所有整数类型太小而无法覆盖地址空间的异国平台。

    通过任意函数指针类型(可能是<code>void(*)()</code>)往返函数指针可能有助于从任意函数中删除该类型,但我也不确定这是否真的有用void*类型擦除的html" target="_blank">html" target="_blank">参数在C API中很常见,当函数只通过数据时,但类型擦除的函数指针则不太常见。

    函数指针通过void*的往返转换可以以与上述类似的方式使用,就像dlsym本质上对额外的动态库复杂性所做的那样。这仅是有条件支持的,尽管POSIX系统实际上需要它。(它通常不受支持,因为在一些更奇特的平台上,对象和函数指针值可能有不同的表示、大小、对齐方式等。)

  •  类似资料:
    • 本文向大家介绍mysql触发器之创建使用触发器简单示例,包括了mysql触发器之创建使用触发器简单示例的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了mysql触发器之创建使用触发器。分享给大家供大家参考,具体如下: 我们可以可以使用CREATE TRIGGER语句创建一个新的触发器,来看下具体的语法: 然后我们来详细看下上述sql的具体含义: 将触发器名称放在CREATE TRIGGER

    • 本文向大家介绍Oracle使用触发器和mysql中使用触发器的案例比较,包括了Oracle使用触发器和mysql中使用触发器的案例比较的使用技巧和注意事项,需要的朋友参考一下 一、触发器   1.触发器在数据库里以独立的对象存储,   2.触发器不需要调用,它由一个事件来触发运行   3.触发器不能接收参数   --触发器的应用     举个例子:校内网、开心网、facebook,当你发一个日志,

    • 我对Android系统比较陌生,所以我在这里很困惑... NotificationReceiver.java manifest.xml

    • 当我的函数方法签名如下时,我有一个成功触发的v3 WebJob: 然而,当我添加一个输出blob时,BlobTrigger永远不会触发。 下面的文档如下:https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-blob#output

    • 参考SOC常见问题解答什么时候应该使用static_cast、dynamic_cast和reinterpret_cast? constcast用于移除或向变量添加const,这是唯一可靠、定义明确且合法的移除const的方法。reinterpretcast用于更改类型的解释。 我以合理的方式理解为什么常量变量应该只使用const_cast转换为非常量变量,但我无法找出使用reinterpret_c

    • 本文向大家介绍触发器的作用?相关面试题,主要包含被问及触发器的作用?时的应答技巧和注意事项,需要的朋友参考一下 答:触发器是一中特殊的存储过程,主要是通过事件来触发而被执行的。它可以强化约束,来维护数据的完整性和一致性,可以跟踪数据库内的操作从而不允许未经许可的更新和变化。可以联级运算。如,某表上的触发器上包含对另一个表的数据操作,而该操作又会导致该表触发器被触发。