【问题标题】:Casting integer constant to a pointer type将整数常量转换为指针类型
【发布时间】:2019-04-19 15:47:20
【问题描述】:

将任意整数常量转换为指向对象/函数类型的指针(例如在单元测试中使用)是否是 UB?

struct helper; //opqaue, creation the structure is complicated

struct my_struct{
   struct helper *h_ptr;
   char another_member;
};

static inline struct my_struct *create_my_struct(struct helper *h_ptr, char am){
    struct my_struct *m_ptr = malloc(sizeof(*m_ptr));
    m_ptr->h_ptr = h_ptr;
    m_ptr->another_member = am;
    return m_ptr;
}

我想为它编写单元测试如下:

uintptr_t helper_ptr = (uintptr_t) 2; //How about this?
char am = 'a';
struct my_struct *ms_ptr = create_my_struct((struct helper *) helper_ptr, am);
//do asserts

我不确定的是(struct helper *) helper_ptr。是UB吗?如果(uintptr_t) 2 没有正确对齐怎么办?

【问题讨论】:

  • 在你要取消引用之前没关系。
  • @Sandro 所以这里的对齐不是问题。我不确定。谢谢。
  • 即使它没有正确对齐,如果你不取消引用它,你应该不会遇到问题。
  • @Sandro 我同意这一点,但不幸的是,当我尝试比较它们时,我会有 UB。

标签: c pointers struct


【解决方案1】:

来自 C11 标准 6.3.2.3 指针,第 5 段:

整数可以转换为任何指针类型。除非前面指定,否则结果是实现定义的,可能未正确对齐,可能不指向引用类型的实体,并且可能是陷阱表示。

所以只要不取消引用就可以做到,而且不是UB,但无论发生什么都取决于平台。

【讨论】:

  • 啊,现在我明白了。正如你所提到的,铸造本身并不是 UB。但不幸的是比较两个这样的指针是UB......
  • 嗯,比较(为了相等)本身并不是问题。问题在于存储或读回该值,因为它可能是一个陷阱表示。
  • 6.5.8 是关于关系运算符,而不是相等测试运算符 (6.5.9)。相等测试运算符没有相同的限制。这就是为什么我指定平等测试就是我所说的。
  • @Acorn 其实6.5.9,6.5.8是关于关系运算符的。
  • 不,@St.Antario,6.5.9/6 只是指定了两个指针比较相等的条件。如果不满足这些条件,则它们比较不相等。没有 UB 出现(由此而来)。
【解决方案2】:

即使指针未正确对齐,我也相信它已定义(如果您不取消引用)。

6.3.2.3p5 提到了 int 到 ptr 转换的不正确对齐的可能性,而似乎没有附加未定义的行为:

整数可以转换为任何指针类型。除了以前 指定,结果是实现定义的,可能不是 正确对齐,可能不指向被引用的实体 类型,并且可能是一个陷阱表示。

相比之下,6.3.2.3p6 表示导致指针未对齐的指针到指针 转换是未定义的行为。 (非正式的J.2 附录列出了未定义的行为,也只列出了由 pointer-to-pointer 转换引起的指针错位作为 UB。

(有趣的是,这意味着任何指针到指针的转换都可以在没有 UB 的情况下通过使用足够大的整数(如果存在)作为中介来促进。)

【讨论】:

  • @Acorn 我认为 6.3.2.3p6 提供了有用的对比。但这并不是说我从你的回答中错过了那个,所以我发布了我的 - 我只是在发布我的之后才看到你的(也赞成;-))。
  • 我同意,除了最后一句,标准明确允许这种转换产生陷阱表示。如果是,则转换本身仍然具有已定义的行为,但将结果存储在对象中则没有。
【解决方案3】:

整个内存映射寄存器硬件级编程在其上进行中继。

例如:

#define GPIO_Typedef *GPIOA (voaltile GPIO_Typdef *)0x40000300

然后我们可以取消引用它访问内存中的寄存器

 GPIOA -> MODER = .....

【讨论】:

  • 当然这总是有效的(否则 C 没用),但那些是有效的地址;而 OP 正在询问可能不包含任何有效内容的任意地址,或者甚至不是一个有效的指针地址。
  • 这是一个任意地址。不是吗?
  • 并非如此,这是您为映射硬件而精心选择的地址。
  • 地址中必须有一些逻辑。但是在大多数 RTOS 中使用具有任意地址的指针(没有内存地址意义)在指针中传递参数(仅使用具有指针大小的队列,而没有分配数据的实际内存)
  • 对于具有特定实现的特定地址是否按预期工作并不代表就语言标准而言行为是否未定义。
猜你喜欢
  • 2014-08-15
  • 2022-07-10
  • 2015-01-06
  • 1970-01-01
  • 1970-01-01
  • 2016-04-03
  • 2021-09-20
  • 2015-06-28
相关资源
最近更新 更多