有关C结构顺序的保证吗?

时间:2012-06-15 19:41:20

标签: c pointers struct

我广泛使用了结构,我看到了一些有趣的东西,尤其是*value而不是value->first_value,其中value是指向struct的指针,first_value是第一个成员, *value安全吗?

另请注意,由于对齐,无法保证大小,基于架构/寄存器大小的alginment值是什么?

我们将数据/代码对齐以便更快地执行,我们可以告诉编译器不要这样做吗?那么也许我们可以保证结构的某些东西,比如尺寸?

当对struct成员执行指针算法以便找到成员偏移量时,我认为如果对于big endian有小端-,那么你做+,还是只依赖于编译器?

malloc(0)真正分配了什么?

以下代码用于教育/发现目的,并非具有生产质量。

#include <stdlib.h>
#include <stdio.h>

int main()
{
    printf("sizeof(struct {}) == %lu;\n", sizeof(struct {}));
    printf("sizeof(struct {int a}) == %lu;\n", sizeof(struct {int a;}));
    printf("sizeof(struct {int a; double b;}) == %lu;\n", sizeof(struct {int a; double b;}));
    printf("sizeof(struct {char c; double a; double b;}) == %lu;\n", sizeof(struct {char c; double a; double b;}));

    printf("malloc(0)) returns %p\n", malloc(0));
    printf("malloc(sizeof(struct {})) returns %p\n", malloc(sizeof(struct {})));

    struct {int a; double b;} *test = malloc(sizeof(struct {int a; double b;}));
    test->a = 10;
    test->b = 12.2;
    printf("test->a == %i, *test == %i \n", test->a, *(int *)test);
    printf("test->b == %f, offset of b is %i, *(test - offset_of_b) == %f\n",
        test->b, (int)((void *)test - (void *)&test->b),
        *(double *)((void *)test - ((void *)test - (void *)&test->b))); // find the offset of b, add it to the base,$

    free(test);
    return 0;
}

调用gcc test.c后跟./a.out 我明白了:

sizeof(struct {}) == 0;
sizeof(struct {int a}) == 4;
sizeof(struct {int a; double b;}) == 16;
sizeof(struct {char c; double a; double b;}) == 24;
malloc(0)) returns 0x100100080
malloc(sizeof(struct {})) returns 0x100100090
test->a == 10, *test == 10 
test->b == 12.200000, offset of b is -8, *(test - offset_of_b) == 12.200000

更新的 这是我的机器:

gcc --version

i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

uname -a

Darwin MacBookPro 10.8.0 Darwin Kernel Version 10.8.0: Tue Jun  7 16:33:36 PDT 2011; root:xnu-1504.15.3~1/RELEASE_I386 i386

5 个答案:

答案 0 :(得分:7)

从6.2.5 / 20:

  

结构类型描述了顺序分配的非空成员对象集   (并且,在某些情况下,是一个不完整的数组),每个都有一个可选的   指定的名称和可能不同的类型。

回答:

  

尤其是*值而不是value-&gt; first_value其中value是指向struct的指针,first_value是第一个成员,*值是否安全?

见6.7.2.1/15:

  

15在结构对象内,非位字段成员和位域中的单位   驻留的地址按声明的顺序增加。 指向a的指针   结构对象,适当转换,指向其初始成员(或者如果该成员是a   比特字段,然后到它所在的单位),反之亦然 。可能有未命名的   在结构对象中填充,但不在其开头。 1

然而,在结构的末尾可能存在填充字节,也可能存在于成员之间。

在C中,malloc( 0 )是实现定义的。 (作为旁注,这是C和C ++不同的小事之一。)

[1]强调我的。

答案 1 :(得分:3)

调用malloc(0)将返回一个指针,该指针可以安全地传递给free()至少一次。如果多个malloc(0)调用返回相同的值,则可以为每个此类调用释放一次。显然,如果它返回NULL,那么可以无限次地传递给free()无效。对malloc(0)返回非空的每次调用都应该通过调用带有返回值的free()来平衡。

答案 2 :(得分:2)

  

我广泛使用了结构,我看到了一些有趣的东西,特别是*value而不是value->first_value,其中value是结构的指针,first_value是第一个成员,*value安全吗?

是的,*value是安全的;它会生成value指向的结构的副本。但它几乎可以保证与*value->first_value具有不同的类型,因此*value的结果几乎总是与*value->first_value不同。


反示例:

struct something { struct something *first_value; ... };
struct something data = { ... };
struct something *value = &data;
value->first_value = value;

在这种相当有限的情况下,您可以从*value*value->first_value获得相同的结果。在该方案下,类型将是相同的(即使值不是)。在一般情况下,*value*value->first_value的类型属于不同类型。


  

另请注意,由于对齐,无法保证尺寸,但是寄存器大小始终是对齐吗?

由于'寄存器大小'不是定义的C概念,因此不清楚您要问的是什么。在没有编译指示(#pragma pack或类似)的情况下,当读取(或写入)值时,将对齐结构的元素以获得最佳性能。

  

我们调整数据/代码以加快执行速度;我们可以告诉编译器不要这样做吗?那么也许我们可以保证关于结构的某些事情,比如尺寸?

编译器负责struct类型的大小和布局。您可以通过精心设计以及#pragma pack或类似指令来影响。

当人们担心序列化数据时(或者更确切地说,试图避免通过一次处理一个结构元素来序列化数据),通常会出现这些问题。一般来说,我认为你最好编写一个函数来进行序列化,从组件中构建它。

  

当对结构成员进行指针运算以便定位成员偏移时,我认为你做了减法,如果是小端,为大端添加,还是仅仅依赖于编译器?

你最好不要对struct成员进行指针算术。如果必须,请使用offsetof()中的<stddef.h>宏来正确处理偏移量(这意味着您不会直接执行指针运算)。无论big-endianness还是little-endianness,第一个结构元素始终位于最低地址。实际上,字节顺序与结构中不同成员的布局无关;它只影响结构(基本数据类型)成员中值的字节顺序。

C标准要求结构的元素按照它们的定义顺序排列;第一个元素位于最低地址,下一个元素位于较高地址,依此类推每个元素。不允许编译器更改顺序。在结构的第一个元素之前不能有填充。在编译器认为合适的结构的任何元素之后可以填充以确保它认为适当的对齐。结构的大小使得您可以分配(N×大小)经过适当对齐的字节(例如,通过malloc())并将结果视为结构的数组。

答案 3 :(得分:1)

如果你有一个内部结构,如果这是封闭结构的第一个声明,它保证与封闭的地址相同的地址开始。

所以*valuevalue->first正在访问以下

中相同地址(但使用不同类型)的内存
struct St {
  long first;
} *value;

此外,结构的成员之间的顺序保证与声明顺序相同

要调整对齐方式,可以使用编译器特定的指令或使用位域。

结构成员的对齐通常基于最适合访问目标平台上各个成员的内容

此外,对于malloc,它可能会在返回的地址附近保留一些簿记,因此即使对于零大小的内存,它也可以返回有效地址(只是不要尝试通过返回的地址访问任何内容) )

答案 4 :(得分:0)

了解struct的大小工作方式非常重要。例如:

struct foo{
  int i;
  char c;
}

struct bar{
  int i;
  int j;
}

struct baz{
  int i;
  char c;
  int j;
}

sizeof(foo) = 8 bytes (32 bit arch)
sizeof(bar) = 8 bytes
sizeof(baz) = 12 bytes

这意味着结构大小和偏移必须遵循两个规则:

1-结构必须是它的第一个元素的倍数(为什么foo是8而不是5个字节)

2-结构元素必须以自身的倍数开始。 (在baz中,int j无法从6开始,因此字节6,7和8是浪费的填充