是否存在将长整型转换为指针有效的情况



在我的C++学习过程中,我在x86中遇到了这样的代码,我不明白:

unsigned long value = 50;
unsigned char* result = (unsigned char*)value;

检查指针会给出内存访问错误(即我无法写入std::cout << *result;(。如果有的话,铸造线应该是这样的:

unsigned char* result = (unsigned char*)&value;

但事实并非如此。所以我的问题是,在什么情况下(如果有的话(第一个铸件可用?或者换句话说,是否可以从指针中获取数据?

我目前正在研究一些数字信号处理,其中包括对ADI公司的DSP进行编程。以下是他们随开发板附带的一个.h文件中的一个小片段:

#define pUART0THR                ((volatile unsigned int *)0x3c00)    /* Transmit Holding Register */
#define pUART0RBR                ((volatile unsigned int *)0x3c00)    /* Receive Buffer Register */
#define pUART0DLL                ((volatile unsigned int *)0x3c00)    /* Divisor Latch Low Byte */
#define pUART0IER                ((volatile unsigned int *)0x3c01)    /* Interrupt Enable Register */

我敢打赌,如果您深入研究几乎任何其他嵌入式系统的标题,您将能够找到类似的东西。

在这种情况下,0x3c00是一个内存地址,您可以将字节写入其中,并让硬件UART模块读取它并通过串行端口传输它。0x3c01是寄存器的地址,如果您设置第二位(位 1(,那么当 UART 发送缓冲区(地址0x3c00处(为空时,您将获得硬件中断 = 您放置在那里的字节已被发送。

但是,为了让您不必记住所有这些地址,并获得一些漂亮的(或至少更令人难忘的(名称来称呼它们,它们有一堆定义,如上面。

将整数强制转换为指针的一个潜在原因是通过接受指针的 API 传输该整数值。

例如,许多与回调相关的 API 接受将void *user_data参数作为参数传递给回调函数。请考虑以下函数:

void do_async_thing(args..., void (* callback) (int result, void *user_data), void *user_data);

如果你只需要传递给回调一个整数,

那么你可以写:
do_async_thing(
args...,
[](int result, void *request_idx) {
std::cout << "request " << reinterpret_cast<std::uintptr_t>(request_idx) << " returned " << result;
},
reinterpret_cast<void *>(std::uintptr_t{request_idx})
);

当然你应该使用std::uintptr_t,而不是unsigned long(例如,在 Windows 上unsigned long是 32 位(。


另一个原因是,在您的特定平台上,您碰巧知道该地址(例如,在某些嵌入式设备上(存在有效对象。

归根结底,这都是实现定义的。std::uintptr_t甚至可能不存在于特定平台上。

在某些情况下,但通常不合适。

只有在以下情况下,转换才有效:

  • long是足够大的类型,可以表示所有地址。这不能保证。事实上,long在 64 位 Windows 系统上不够大。有一个整数类型可以保证为此目的正确调整大小:std::uintptr_t

当然,除非您打算使用指针执行某些操作,否则此转换完全没有意义。这些东西是否有效取决于更多的要求。

  • 如果转换后的指针值无效,即不引用分配的存储区域,则通过指针进行间接具有未定义的行为。任何其他用途(例如将值与另一个指针进行比较(都具有实现定义的行为。
  • 如果指针指向的对象在其生存期之外,但具有分配的存储空间,则只能以有限的方式使用该指针。通过指针访问内存仍然是典型的(例外情况适用(未定义的行为,但例如,您可以将值相等与另一个有效指针进行比较。请参阅标准部分 [basic.life]、[class.base.init] 和 [class.cdtor] 了解有关允许的内容的详细信息。
  • 如果指针指向其生存期内的对象,并且指针属于相同类型(或兼容类型(,则可以间接通过指针访问指向的对象。

请注意,从整数到指针的转换映射是实现定义的。几乎唯一可以保证的是,转换为足够大小的整数的指针可以转换回相同的值。


以上仅描述转换后的指针的转换和使用有效的情况。另一个问题是这样做是否有任何意义。这些用例在桌面/服务器系统上很少见(参见 andrey 的答案示例(,但在嵌入式系统上,有时会有硬编码地址用于与硬件通信(参见 Frodyne 的答案以获取示例(。

unsigned long value = 50;
unsigned char* result = (unsigned char*)value;  

现在指针result包含数字50,换句话说,它指向地址50。但是在现代平台上,您不能简单地寻址任何内存地址。

因此,此代码将导致未定义的行为(通常是崩溃(。

std::cout << *result;

最新更新