【问题标题】:Is it safe to assign struct value by using pointer使用指针分配结构值是否安全
【发布时间】:2012-12-05 12:14:35
【问题描述】:

我有以下结构:

struct foo
{
   int  a;
   char b;
};

它存储在内存中,指向它的指针可以是非对齐的(奇数地址)。

那么,这样安全吗?:

const struct foo a = *((struct foo*)char_ptr); 

我很担心,因为源结构的整数成员可能位于奇数地址。在某些系统中,从奇数地址读取(多字节)整数会导致问题。

编辑: 为了避免对const 的使用发表离题评论,我确实从代码中删除了const。 (我从不将 const 指针转换为非 const 指针,即使在这种情况下它也不应该引起任何问题)

以及更多关于问题背景的信息: 这种结构是协议帧的一部分。它可以在框架内的任何偏移处。在实际代码中,结构具有__attribute__((packed)) 属性。但这可能不会改变答案?

无论如何,我可以使用memcopy 和非常量a 来解决问题。但我想使用 assingment,因为它似乎是更优雅的方式(如果它是安全的)。

【问题讨论】:

  • 编译器应该处理它。如果一个变量可以正常访问,就可以通过指针访问。
  • @User1 - 我会回到你的教科书阅读关键字const
  • ^^ 正确指出... LHS 上的 const 不允许 RHS 上的变量。
  • @anishsane 不明白你的意思。始终允许使用“变量”值初始化 const。如果这发生在声明中(如上所述),那是完全合法的。
  • 我希望打包属性能够改变行为。由于结构是打包的,编译器必须预期成员可能会奇怪地对齐,并且它应该生成代码来处理它。不过,我没有明确的文档。

标签: c


【解决方案1】:

Data structure padding。在大多数系统上,结构将对齐,内部成员地址将根据各自的大小对齐,同时 malloc'ing 自身。

如果你手动创建一个内存池并使用其中的某个部分来存储你的结构对象,那么编译器不会保证你对齐,但是如果你使用 malloc 来创建结构对象,编译器通常会很头疼,给你对齐的地址,如果需要,在结构元素之间使用填充。

【讨论】:

  • 他的具体问题是关于源结构,这意味着可能未对齐的指针 char_ptr(我假设它是一个 char*)。这是从文件或网络解析二进制数据时的常见问题。
  • ^^ 因此免责声明:“如果您手动创建内存池并使用其中的某个部分来存储您的结构对象,那么编译器将无法保证您对齐”
  • 我不认为这应该是免责声明;我觉得应该是重点。将指向字符的指针转换为指向结构的指针高度指示访问缓冲区或由某些外部要求布置的其他结构。 可能这个指向字符的指针仅仅是通过内存分配和地址操作创建的但具有必要对齐的东西,但是应该特别提醒提问者。跨度>
  • 他不需要被警告,因为这正是他要问的。 pointer to it can be unaligned 几乎是问题的重点。
【解决方案2】:

不,这不安全,除非您知道指针具有结构所需的对齐方式。

这里有一些方法可以知道指针是否有必要的对齐:

  • 在转换为指向 char 的指针之前,该指针最初是指向此结构类型的指针。
  • 指针是内存分配例程(例如malloc)的结果,该例程保证返回的地址被适当地分配给任何用途(malloc 保证)。
  • 指针是根据上述其中一项计算得出的,并且保留了所需的对齐方式。
  • 如果您的 C 实现支持,您将指针转换为 uintptr_t 并测试其对齐方式。
  • 您的 C 实现(尤其是操作系统和处理器)允许对结构中类型的对象进行非对齐访问。

如果您不知道指针具有必要的对齐方式,那么您不应该通过转换后的指针访问该结构。相反,您可以定义一个新结构并使用memcpy 将指针指向字符复制到新结构中。

【讨论】:

  • 好的,所以编译器逐个字段地进行结构赋值struct foo a =? (而不是逐字节复制)
  • @User1:编译器可以通过多种方式实现赋值。它可能会在某些情况下处理每个字段,或者在某些情况下可能会复制整个内容。然而,即使编译器选择复制整个结构,而不考虑单个元素,这并不意味着它调用 memcpy 并进行逐字节复制。如果根据语言规则,结构应该是四字节对齐的,编译器可以使用四字节加载和存储指令进行复制,即使结构包含一些不同大小的元素。
  • 听起来很清楚。您的评论对我来说是详尽的答案。
【解决方案3】:

如果您从指向数据的指针不一定对齐的源获取数据,您可以做的唯一安全的事情是 memcpy 并希望您的编译器不是“智能”并假装未对齐的指针是实际对齐并且可以优化 memcpy(有几个旧版本的 gcc 存在此错误,需要您编写一个不称为 memcpy 的自定义 memcpy 函数)。根据您的架构,您可以摆脱不正确对齐的访问,但几乎可以肯定它会更慢,有时甚至会通过内核陷阱进行模拟。

附带一则轶事:这通常是操作系统中网络堆栈中的问题,其中以太网标头的奇数大小使 IP 标头未对齐,并且如果硬件无法错位接收到的数据包(某些 DMA 引擎只能在 4 字节边界上写入数据),这需要在软件中再次复制整个标头(或整个数据包)。

【讨论】:

    猜你喜欢
    • 2021-02-14
    • 2015-10-14
    • 1970-01-01
    • 2016-05-06
    • 1970-01-01
    • 2014-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多