将整数常量转换为指针

3
我不明白这行代码的作用是什么:
((struct Example*) 0x10000)

我写了一个测试程序:

#include <stdio.h>

struct Elf{
    int bla;
    char bla2;
};

int main(){
    struct Elf *elfPtr;
    printf("Before casting: %p\n", elfPtr);
 
    elfPtr = ((struct Elf *)0x10000);
    printf("After casting: %p\n", elfPtr);
 
    return 0;
}

输出结果为:

转换前:0xb776dff4

转换后:0x10000

这行代码只做了这个事情吗?

elfPtr = 0x10000

在进行强制类型转换之前,它会打印一个垃圾地址。此外,为了使用%p正确的强制类型转换应该是void*。因此,printf("After casting: %p\n", elfPtr); 应该写成 printf("After casting: %p\n", (void*)elfPtr); 才是正确的。 - Grijesh Chauhan
第一个 printf() 调用了未定义的行为,因为 elfPtr 没有被初始化。 - alk
@alk 你确定检查未初始化的指针是未定义行为吗?我认为值是未定义的,但行为不是(即它将具有一个值,并且该值不会改变)。 - pburka
@pburka:是的,访问未初始化的变量会引发未定义行为。尽管根据我的经验,除了得到随机数据之外,它从来没有引起任何问题。 - alk
@alk 你说得对。不知道我当时在想什么。 - pburka
4个回答

5
这确实将指针设定为特定的常量值,但在具有虚拟内存管理的系统中,这是一个坏主意。
显著的例外是具有内存映射资源的嵌入式系统。在这样的系统中,硬件设计人员经常为替代使用保留一系列内存地址范围,使程序员能够访问硬件寄存器,就像它们是常规内存空间的一部分。
以下是一个示例*:
struct UART {
    char data;
    char status;
};
const UART *uart1 = 0xC0000;
const UART *uart2 = 0xC0020;

有了这个设置,嵌入式程序就可以像访问 struct 的成员一样访问 UART 寄存器:

while (uart1->status & 0x02) {
    uart1->data = 0x00;
}


* UART代表通用异步收发器,它是一种常用于异步点对点通信的硬件设备。


1
好的回答,如果要使用%p打印地址,我们应该将地址强制转换为void*,否则会产生未定义的行为,对吗? - Grijesh Chauhan
1
@GrijeshChauhan 正确,%p需要一个void*,否则就是未定义行为。 - Sergey Kalinichenko
1
@GrijeshChauhan:对于 char *,不需要进行强制转换。 - alk
1
如果我没记错的话,C标准允许这样做是为了向后兼容那些没有void数据类型以及显然没有void *的时代。因此,通用引用内存/对象的常见类型是使用char * - alk
1
@GrijeshChauhan:我认为在(n1256)中它在6.5.2.2/6的最后。这是关于默认参数提升的问题。 - alk
显示剩余9条评论

2
通常,给指针赋任意值都是不好的想法。这就是为什么编译器会阻止它,并且您需要添加强制转换来向编译器保证您知道自己在做什么。
(您的代码将把地址0x10000的内存视为“struct Elf”实例,至少在您的简单示例中并非如此。它甚至可能不是可读和/或可写的内存,因此当您尝试访问它时会导致崩溃。)

2
将数字分配给指针变量的一个有用例子是操作系统内核和设备驱动程序,需要使用特定地址与设备通信。在指针变量中“隐藏”数字也很常见,尤其是当只有数字可用时(例如在现有结构或API中)。 - pburka
我希望如果任何数据被分配到那个内存或者任何对那个内存的访问都会抛出分段错误... 我是正确的吗? - Tonmoy
@Tonmoy:如果内存不可访问,那么是的,你会得到一个段错误。但是,如果它是可访问的(比如因为它是某个其他数据结构的一部分),并且你写入它,你将覆盖之前存在的任何内容,而没有任何立即的症状。 - RichieHindle

1
那行代码 elfPtr = ((struct Elf *)0x10000); 会让指向类型为 *Elf * 的指针指向由十六进制数0x10000(十进制中的65536)标识的内存地址。
因此,无论在该内存地址上有什么,您都假定它是“Elf”类型。

0

使用该命令,您告诉编译器地址0x10000处的数据是“Elf”类型的结构体实例,并将该类型的指针分配给该地址。

请注意,您没有在该地址实例化任何该类型的结构体,甚至没有为其保留内存,因此,至少在尝试引用其成员时,您将获得垃圾值,如果不是段错误。

除非进行低级微控制器编程等操作,否则确实应避免使用绝对地址,这会导致代码无法移植,即使运行也是如此。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接