C 为什么可以';我们不去引用常数整数吗?

C 为什么可以';我们不去引用常数整数吗?,c,pointers,types,casting,embedded,C,Pointers,Types,Casting,Embedded,为了理解使用嵌入式系统时的指针,我试图了解访问内存映射寄存器的代码中发生了什么: uint32_t regval = *(uint32_t *)0x40048004U; 在我看来,没有类型转换,这意味着regval现在等于地址的内存内容。我想这应该足够了,但事实并非如此。但当类型转换它时,您将其视为单个操作的指针,这似乎有点多余 那么您将regval视为指向0x40048004U内存内容的未知地址的内存内容?为什么类型转换是必需的?指针的类型决定了运行时在给定地址和后续地址期望找到的内容。例如

为了理解使用嵌入式系统时的指针,我试图了解访问内存映射寄存器的代码中发生了什么:

uint32_t regval = *(uint32_t *)0x40048004U;
在我看来,没有类型转换,这意味着regval现在等于地址的内存内容。我想这应该足够了,但事实并非如此。但当类型转换它时,您将其视为单个操作的指针,这似乎有点多余


那么您将regval视为指向0x40048004U内存内容的未知地址的内存内容?为什么类型转换是必需的?

指针的类型决定了运行时在给定地址和后续地址期望找到的内容。例如,如果
p
的类型是
char*
,则
*p
是该地址的单个字节。如果它的类型是
int*
,那么它希望在该地址处找到(可能)4个字节,表示一个整数。如果
p
的类型是指向某个100字节结构的指针,那么它期望在那里找到100字节的结构


此外,类型决定指针数学
p+1
是p处下一个内容的地址,可能是1、4或100字节后的地址。

我理解您在问为什么我们不能这样做:

uint32_t register_value = *0x40048004U;
(例如,“为什么我们不能取消对常量整数的引用?”)

有几个答案:

  • C的整数常量语法没有指针值的任何注释,只有有符号/无符号/短/长值(使用
    U
    L
    等就足够了)
  • 如果可以执行
    *0x40048004U
    操作,则没有指针数据类型的指示-计算机要从地址读取多少字节?签字了吗?没有签名?(记住,常数的类型与内存地址的类型有关,而不是它指向的值)
  • 因此,通过要求使用显式强制转换运算符
    *(valueType*)0xDEADBEFF
    ,意味着可以使用与非常量值强制转换相同的语法指定该类型,而无需引入新的单独语法,并且由于这种强制转换是静态操作,因此运行时性能不会受到任何影响

C是在内存很小的时候设计的,数千个博士学位还没有颁发给编译器类型推断

在这种特殊情况下,编译器可能会推断

uint32_t regval = *0x40048004U;
假设是对隐含uint32_t指针的解引用,与乘法运算中缺少参数相反,实际情况是,在某些情况下它可能会失败(例如,
int x=0x00->foo
,当有多个结构的成员名为
foo


因此,相反,要求是每件事物都有一个类型,如果你想像使用另一个类型一样使用某个东西,你必须把它显式化。45年来一直如此。ford.

您需要显示整数常量将用作指针。您需要向编译器显示该指针指向的对象类型。接下来,您将获得该指针指向的对象的值(在本例中为32位字)。

指针间接寻址运算符
*
只能应用于指针。当然,它的作用是访问指针指向的数据对象。但它必须知道指向的对象的大小和类型

如果你刚才说

uint32_t regval = *0x40048004;
编译器将无法知道大小和类型。(您可能会认为这是一个隐含的事实,即您正在为左侧的
uint32_2
赋值,但C不是这样工作的。)

如果想要“记住”类型,只需创建并初始化指针变量:

uint32_t *regptr = (uint32_t *)0x40048004;
uint32_t regval = *regptr;
不需要类型转换指针<代码>0x40048004U不是指针。强制转换将文本常量整数重新解释为指针


此外,即使将0x40048004U解释为地址,编译器也需要知道在取消引用时它指向的数据类型。

它不是冗余的。
0x40048004U
的类型是一个整数,而不是指针。请记住,正是类型定义了您可以或不能对值执行的操作。因为
0x40048004U
是一个文本或常量,
unsigned int
。C不允许文本指针。请注意,这种类型的强制转换是一种静态操作,因此不会影响运行时性能。这些都是很好的答案,伙计们,谢谢。事实上,我没想到会有这么快的反应,我正在atm机上工作,所以我以后会再看一遍。非常感谢您的时间和见解。与问题无关,代码不正确。读取硬件寄存器时,必须始终执行
*(volatile uint32\u t*)0x40048004U。否则,编译器可能会进行非常奇怪的优化,程序可能无法按预期运行。此外,上下文不会传递所需的类型信息。例如,仅仅因为取消引用的结果被分配给
uint32\u t
类型的变量并不意味着指向的对象必须是该类型的,而不是
int64\u t
uint16\u t
.IIRC,
0x00->foo
在一些预标准代码中是允许的,因为成员在结构中必须是唯一的。因此,像
struct tm
这样的古老结构之所以有
tm_min,tm_hour
这样的成员,而不是
min,hour
。一些小鸭发现了这篇博文,其中有一些人发表了有趣的评论,他们访问了K和/或R:(包括Rob Pike)uv'd,希望你不介意我纠正你的小错误;)有点吹毛求疵:“但是C不是那样工作的。”@FelixPalmen谢谢你的修复。C不是那样工作的原因有很多,所以我决定不深入研究它们,这就是为什么我刚才说“C不是那样工作的”