【问题标题】:Why can't we dereference constant integers?为什么我们不能取消引用常量整数?
【发布时间】:2018-01-10 04:23:15
【问题描述】:

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

uint32_t regval = *(uint32_t *)0x40048004U;

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

所以您将 regval 视为指向 0x40048004U 内存内容的未知地址的内存内容?为什么类型转换甚至是必要的?

【问题讨论】:

  • 这不是多余的。 0x40048004U 的类型是整数,而不是指针。请记住,类型定义了您可以或不能对值执行的操作。
  • 因为0x40048004U 是一个文字或常量unsigned int。 C 不允许使用文字指针。请注意,这种强制转换是静态操作,因此不会影响运行时性能。
  • 这些是很好的答案,伙计们,谢谢。我实际上没想到会有这么快的反应,而且我正在 atm 工作,所以以后必须再过一遍。非常感谢您的时间和洞察力。
  • 与问题无关,代码不正确。读取硬件寄存器时,必须始终使用*(volatile uint32_t *)0x40048004U;。否则编译器可能会进行非常奇怪的优化,程序可能无法按预期运行。

标签: c pointers types casting embedded


【解决方案1】:

我知道你在问为什么我们不能这样做:

uint32_t register_value = *0x40048004U;

(即“为什么我们不能取消引用常量整数?”)。

有几个答案:

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

【讨论】:

    【解决方案2】:

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

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

    uint32_t regval = *0x40048004U;
    

    应该是隐含 uint32_t 指针的取消引用,而不是乘法运算中缺少的参数,现实情况是它可能会失败(例如,int x = 0x00->foo 当有多个结构具有成员名为foo)。

    因此,相反,要求所有东西都有一个类型,如果你想使用一些东西,就好像它是一个不同的类型,你必须明确说明。 45 年来一直如此。福特。

    【讨论】:

    • IIRC, 0x00->foo 在一些准标准代码中被允许,因为成员在结构中必须是唯一的。因此,像struct tm 这样的古代结构的原因是拥有像tm_min, tm_hour 这样的成员而不是min, hour
    • 一些小鸭子发现了这篇博文,其中有几个可以访问 K 和/或 R 的人的有趣的 cmets:spin0r.wordpress.com/2014/11/21/kr-c(包括 Rob Pike)
    【解决方案3】:

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

    如果你说

    uint32_t regval = *0x40048004;
    

    编译器无法知道大小和类型。 (您可能会认为它隐含在您分配给左侧的 uint32_2 的事实中,但 C 不是这样工作的。)

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

    uint32_t *regptr = (uint32_t *)0x40048004;
    uint32_t regval = *regptr;
    

    【讨论】:

    • uv'd,希望你不介意我更正你的小错字;)有点挑剔:“但是 C 不能那样工作。
    • @FelixPalmen 感谢您的修复。
    • 而且有很多所以的原因使 C 不能那样工作,所以我决定根本不深入研究它们,这就是为什么我刚才说“C 不不能那样工作”
    【解决方案4】:

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

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

    【讨论】:

    • 此外,上下文没有传达所需的类型信息。例如,仅仅因为取消引用的结果被分配给uint32_t 类型的变量并不意味着指向的对象必须是该类型,而不是int64_tuint16_t
    【解决方案5】:

    您需要表明整数常量将用作指针。您需要向编译器显示此指针指向的对象类型。接下来你会得到这个指针指向的对象的值(在这种情况下它将是 32 位字)。

    【讨论】:

      【解决方案6】:

      类型转换指针不是必要的; 0x40048004U 不是指针。强制转换将文字常量 integer 重新解释为指针。

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

      【讨论】:

        猜你喜欢
        • 2019-02-16
        • 2015-01-21
        • 1970-01-01
        • 2014-03-30
        • 2019-02-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-18
        相关资源
        最近更新 更多