【问题标题】:Does casting a pointer back and forth from size_t or uintptr_t break strict aliasing?从 size_t 或 uintptr_t 来回转换指针会破坏严格的别名吗?
【发布时间】:2015-08-28 21:02:15
【问题描述】:

我提议更改一个库,其公共 API 当前如下所示:

typedef size_t enh;  /* handle */

int en_open(enh *handle)
{
    struct internal *e = malloc(...);
    *handle = (enh)e;
    return 0;
}

int en_start(enh handle)
{
    struct internal *e = (struct internal*)handle;
    return do_something(e);
}

这种用法,来回转换为size_t 会破坏严格的别名吗?

为了记录,我在公共 API 中提出了 struct internal 的典型不透明前向声明,如 this Programmers.SE question about the same code 所示。

【问题讨论】:

  • size_t 是否保证足够宽以容纳指针?我不这么认为..
  • @EugeneSh。 Nope.
  • @Kninnug void* 不好,因为它使类型检查变得不可能。想想当您的界面中有两种不同的句柄类型时会发生什么。如果其中一个是void*,如果用户将它们混合在一起,编译器将不会捕获错误。使用适当的不透明指针,编译器将捕获这些错误。
  • @JensGustedt 是的。不透明指针优于void*,优于uintptr_t,优于size_t。正是按照这个顺序:-)
  • @rwong:短语其公共 API 目前看起来像确实表明显示的代码是“原始”样式。

标签: c api-design strict-aliasing


【解决方案1】:

别名是关于使用两个不同类型的指针来访问相同的字节。您的代码中不是这种情况。当您访问句柄后面的数据成员时,您总是通过struct internal* 类型的指针来访问。所以这里没有伤害。

您的代码中唯一值得怀疑的是,您使用size_t 来传递指针。 Afaik,该标准不保证您可以安全地将指针转换为size_t 并返回,即使任何理智的实现都允许这样做。正确的整数类型选择是 uintptr_t,但您甚至不需要:

我认为,你应该只在界面中使用一个不透明的指针。即,只需放置声明

typedef struct internal internal;

进入您的公共标头并保留相应的

struct internal {
    ...
}

private(当然,用合理的公共名称替换 internal)。然后可以简单地将公共函数声明为:

int en_open(internal** outHandle);
int en_close(internal* handle);

这样,您可以在客户端代码中获得完美的类型检查,并避免进行任何类型转换。

【讨论】:

  • uintptr_t 保证在指向对象的指针之间来回转换。但是,尽可能避免强制转换是正确的。
  • @Olaf 是的,当然,我在初稿中忽略了这一点,因为不需要强制转换为整数,所以我认为没有必要指出要强制转换为的正确整数类型好。不过,我现在添加了一条关于 uintptr_t 的注释。
  • @Olaf uintptr_t 保证在void * 之间来回转换。确定这适用于任何“指向对象的指针”?
  • @chux:为简洁起见,我省略了中间步骤:6.3.2.3p1。如果你想安全,你可以施放两次。但是,它确实可以在没有 gcc-arm-none-eabi 的情况下工作(启用了大多数警告)。如果我看第 5 和第 6 段,演员表本身对我来说也很好。 (注意,在 C 函数中是 no 对象)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-02
  • 1970-01-01
  • 2022-10-15
  • 2013-06-29
  • 2011-11-29
  • 2020-01-09
相关资源
最近更新 更多