【问题标题】:De-referencing pointer to a volatile int after increment在递增后取消引用指向 volatile int 的指针
【发布时间】:2016-04-15 00:41:46
【问题描述】:
unsigned int addr = 0x1000;    
int temp = *((volatile int *) addr +  3);

它是否将递增的指针(即addr + 3 * sizeof(int))视为指向volatile int 的指针(解除引用时)。换句话说,我可以期待 temp 中 say (0x1012) 的硬件更新内容吗?

【问题讨论】:

  • addr 是什么类型? char*?我怀疑这违反了严格的别名规则
  • 那么我会假设这违反了严格的别名规则。但是,是的,如果你在地毯下刷它(你不应该,它的 UB),你将 addr 转换为 int*,同时假设 ints 在你的机器上是 4 个字节,这将执行指针运算和取消引用将从 addr 增加 12 个字节
  • 0x1000 不是 C 中的有效指针,而是整数常量。
  • @yano:请详细说明。如果实现定义了转换,但不是 UB。取消引用是 C 标准中公认的缺陷(请参阅缺陷报告),也是大多数(如果不是全部)实现中的常见做法。
  • @jithu83 我会说,“是的”。指针算术不会改变指针的类型或其限定符。但是我在 C 规范中找不到直截了当的陈述,所以我不会发布答案。如果您需要规范中的引用,那么您应该将其中一个标签更改为“language-lawyer”。

标签: c pointers linux-device-driver volatile low-level-io


【解决方案1】:

是的。

指针算术不影响指针的类型,包括任何类型限定符。给定 A + B 形式的表达式,如果 A 具有指向 T 的类型限定指针且 B 是整数类型,则表达式 A + B 也将是指向 T 的限定指针——相同类型,相同限定符.

来自 C 规范的 6.5.6.8(草案 n1570):

当一个整数类型的表达式被添加到指针或从指针中减去时, result 具有指针操作数的类型。

【讨论】:

    【解决方案2】:

    假设addr 是一个整数(变量或常量),您的实现可以安全地将其值转换为int *(见下文)。

    考虑

    volatile int a[4] = [ 1,2,3,4};
    int i = a[3];
    

    这完全一样,除了将 integer 显式转换为 volatile int *(指向...的指针)。对于索引运算符,数组的名称衰减为指向a 第一个元素的指针。这是volatile int *(C 中的类型限定符适用于数组的元素,而不是数组本身)。

    这与演员表相同。留下2个不同点:

    • 将整数转换为“指针”。这是实现定义的,因此如果您的编译器正确支持它(它应该记录这一点),并且值正确,那很好。

    • 终于可以访问了。底层对象不是volatile,而是指针/resp。使用权。这实际上是标准中的一个缺陷(请参阅DR476,它要求对象是volatile,而不是访问权限。这与记录的意图(阅读链接)和 C++ 语义(应该相同)形成对比。幸运的是,所有(大多数)实现都按预期生成代码并按预期执行访问。请注意,这是嵌入式系统上的常见理念。


    因此,如果满足先决条件,则代码是正确的。但请参阅下文以获得更好的(在可维护性和安全性方面)选项。


    注意:更好的方法是

    • 使用uintptr_t 保证整数可以保存指针,或者 - 更好 -
    • #define ARRAY_ADDR ((volatile int *)0x1000)

    后者避免了对整数的意外修改,并清楚地说明了含义。它也可以更容易地使用。它是低级外设寄存器定义中的典型构造。


    回复。你的递增:addr不是指针!因此,您增加一个整数,而不是一个指针。除了使用真正的指针之外,这更多的是键入,它也容易出错并混淆你的代码。如果需要指针,使用指针

    int *p = ARRAY_ADDR + 3;
    

    作为个人说明:在一家至少有一些质量标准的公司中通过此类代码(整数 addr)的每个人都会与她的团队领导进行非常认真的交谈。

    【讨论】:

    • 如果应该读取的内容是硬件寄存器,则数组可能不是一个选项。
    • @Lundin:上面的数组语法只是为了显示指针算法完全相同,因为名称衰减,请参阅 C11 6.3.2.1p3 。请参阅“更好的方法”和下面的文字。这正是典型硬件头文件中的做法(好吧,除了它们不使用int,而是如果作者知道语言,则使用固定宽度类型)。
    【解决方案3】:

    首先请注意,从整数到指针的转换不一定是安全的。将发生什么是实现定义的。在某些情况下,这种转换甚至会调用未定义的行为,以防整数值无法表示为指针,或者指针以未对齐的地址结束。

    使用整数类型uintptr_t 来存储指针和地址更安全,因为它保证能够存储给定系统的指针。

    鉴于您的编译器为此代码实现了安全转换(例如,大多数嵌入式系统编译器都这样做),那么代码确实会按照您的预期运行。

    指针运算将在 volatile int 的类型上完成,因此 + 3 意味着将地址增加 sizeof(volatile int) * 3 字节。如果int 在您的系统上是 4 个字节,您最终将读取地址0x100C 的内容。不确定您从哪里得到0x1012,混合了十进制和十六进制表示法?

    【讨论】:

      猜你喜欢
      • 2011-12-10
      • 2010-10-25
      • 1970-01-01
      • 1970-01-01
      • 2016-12-01
      • 1970-01-01
      • 2013-01-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多