在C结构中对两个连续指针的打包做出假设是否安全?

时间:2015-12-12 16:58:06

标签: c pointers struct packing

我正致力于从基于GObject的库到Standard ML的语言绑定。更准确地说,我实现了对G(S)List集合类型的支持。此实现需要从G(S)列表链接中提取数据并从标准ML中获取下一个链接。而是通过FFI调用其中一个G(S)List函数,我希望可以使用FFI的指针操作函数来访问适当的struct元素以提高效率。但是,这要求可以预测结构中第二个下一个链接指针的偏移量。作为参考,这是GSList链接结构的样子

struct GSList {
    gpointer data;
    GSList *next;
};

我认为这对任何人都不会感到惊讶。可以安全地假设指向结构的指针指向它的第一个元素,即数据指针,但第二个元素怎么样呢?我可以对第二个元素相对于第一个元素的偏移做出任何与平台无关的假设吗?

3 个答案:

答案 0 :(得分:2)

  

可以安全地假设指向结构的指针指向它的第一个   元素,即数据指针,但第二个元素怎么样?

是。这很安全。 struct对象的地址和第一个元素的地址保证相同。在结构的第一个成员之前,不允许填充。

  

我可以对偏移量进行任何与平台无关的假设   与第一个相关的第二个要素?

没有。这不安全。在结构的第一和第二元素之间可能存在填充字节,并且任何假设都是不可移植的。但你可以使用 offsetof从结构的开头获取任何成员的偏移量。

例如,您可以next的偏移量为:

size_t next_offset =  offsetof(struct GSList, next);

GCC提供attribute来禁用填充:

__attribute__((packed))

MSVC也提供了类似的选项。

答案 1 :(得分:1)

不,一般来说,你不能做出这样的假设。您的结构的第二个成员可能存在特定的对齐要求,这将导致填充字节插入结构布局中。

有一种特殊情况:如果第二个成员与第一个成员的类型相同,那么假设没有这样的填充似乎是合理的,但是标准为编译器留下了额外的灵活性,因此无法保证。

此特殊情况无论如何都不适用,因为gpointer可能是void *指针的typedef。在某些体系结构上(很少见并且消失,但Cray曾经有过),指向不同类型的指针可能具有不同的表示形式,从而具有不同的对齐要求。

答案 2 :(得分:1)

正如其他人所说,标准甚至要求struct在第一个成员之前没有填充。

对于其他成员,这是不安全的。但是,您可以使用offsetof来获取偏移量。

实际上,Linux内核使用container_of宏从其成员之一的地址获取结构的基址。请注意,这不使用内核的其他功能,因此它也可以在代码中使用。:

#define container_of(ptr, type, member) ({ \
        const typeof( ((type *)0)->member ) *__mptr = (ptr); 
        (type *)( (char *)__mptr - offsetof(type,member) );})

struct S {
    int a;
    int b;
};

int *ip;  // assume points to the `b` member in an object of struct S

// get address of the struct S
struct S *sp = container_of(ip, struct S, b);

根据定义,它使用gcc扩展来添加一些类型安全性(请参阅链接)。通过删除安全措施,人们可以采用不同的工具链。