2013-09-29 66 views
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 
+0

在投射之前它正在打印一个垃圾地址,另外使用'%p'正确投射是'void *'.so'printf(“投射后:%p \ n”,elfPtr);'应该写成' printf(“铸造后:%p \ n”,(void *)elfPtr);'正确。 –

+0

由于'elfPtr'没有被初始化,第一个'printf()'调用未定义的行为。 – alk

+0

@alk你确定检查未初始化的指针是未定义的行为吗?我认为价值是未定义的,但不是行为(即它将具有价值,并且该价值不会改变)。 – pburka

回答

5

这确实将指针设置为特定的常量值,这在具有虚拟内存管理的系统中是一个坏主意。

一个值得注意的例外是带内存映射资源的嵌入式系统。在这样的系统中,硬件设计人员通常会预留一定范围的内存地址以供替代使用,从而使程序员可以访问硬件寄存器,就好像它们是常规内存空间的一部分一样。

下面是一个例子*

struct UART { 
    char data; 
    char status; 
}; 
const UART *uart1 = 0xC0000; 
const UART *uart2 = 0xC0020; 

有了这个设置到位,嵌入式程序可以访问UART的寄存器,如果他们struct的成员:

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


* UART代表 Universal Asynchronous Receiver/Transmitter,一种通常用于异步点对点通信的硬件ication。

+1

好的答案,用'%p'打印,我们应该把地址转换为'void *'否则它是未定义的行为,是否正确? –

+1

@GrijeshChauhan更正,'%p'需要'void *',否则就是UB。 – dasblinkenlight

+1

@GrijeshChauhan:对于char *'不需要投射。 – alk

2

通常,给一个指针指定一个任意值是一个坏主意。这就是编译器不鼓励它的原因,并且你需要添加转换来让编译器确信你知道你在做什么。 (你的代码将把地址为0x10000的内存当作struct Elf实例,至少在你的简单例子中,它不是,它甚至可能不是可读和/或可写的内存,所以你会得到一个当您尝试访问它时崩溃。)

+2

向指针分配数字很有用的一种情况包括操作系统内核和设备驱动程序,您需要使用特定地址与设备进行通信。当所有数据都可用时(可能在预先存在的结构或API中),将指针变量中的数字“隐藏”也并不少见。 – pburka

+0

我希望如果任何数据被分配到该内存或对该内存的任何访问都会导致分段错误......我是否正确? – Tonmoy

+0

@Tonmoy:如果内存不可访问,那么会出现段错误。但是,如果它*是*可访问的(说是因为它是其他一些数据结构的一部分)并且你写了它,那么你将覆盖之前没有任何直接症状的东西。 – RichieHindle

0

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

请注意,您尚未在该地址实例化该类型的任何结构,或者甚至为其保留内存,因此在尝试引用其成员时,至少会得到垃圾,如果不是分段错误。

你真的应该避免使用绝对地址(除非例如低级别的微控制器编程),否则它会导致不可移植的代码,即使它运行。

1

那行elfPtr = ((struct Elf *)0x10000);将使类型的指针* 精灵*点由十六进制数0x10000的标识的存储器地址(十进制65536)

所以,无论有在那个内存地址,你就假设它是类型“精灵”