4

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

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

我们对齐数据/代码以更快地执行我们可以告诉编译器不要这样做吗?所以也许我们可以保证结构的某些事情,比如它们的大小?

当对结构成员进行指针运算以定位成员偏移量时,我认为你会为大端的-小端+做,还是只取决于编译器?

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
4

5 回答 5

6

从 6.2.5/20 开始:

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

回答:

特别是 *value 而不是 value->first_value 其中 value 是指向结构的指针, first_value 是第一个成员, *value 安全吗?

见 6.7.2.1/15:

15 在结构对象中,非位域成员和位域所在的单元的地址按声明顺序递增。指向结构对象的指针,经过适当转换,指向其初始成员(或者如果该成员是位域,则指向它所在的单元),反之亦然。结构对象中可能有未命名的填充,但不是在其开头。1

然而,在结构的末尾可能有填充字节,也可能是中间成员。

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

[1] 强调我的。

于 2012-06-15T19:46:21.843 回答
3

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

于 2012-06-15T19:47:35.090 回答
3

我已经广泛使用了结构,并且看到了一些有趣的东西,尤其*value是指向结构的指针value->first_value在哪里,第一个成员是安全的吗?valuefirst_value*value

是的,*value是安全的;它产生value指向的结构的副本。但它几乎可以保证有一个不同的类型 from *value->first_value,所以结果*value几乎总是和 from 不同*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()宏 from<stddef.h>正确处理偏移量(这意味着您没有直接进行指针运算)。无论是大端还是小端,第一个结构元素始终位于最低地址。实际上,字节顺序与结构内不同成员的布局无关。它只影响结构的(基本数据类型)成员中值的字节顺序。

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

于 2012-06-15T19:55:21.953 回答
2

如果您有一个内部结构,则保证从与封闭结构相同的地址开始,如果那是封闭结构的第一个声明。

所以*value并且value->first在下面访问相同地址(但使用不同类型)的内存

struct St {
  long first;
} *value;

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

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

结构成员的对齐通常基于访问目标平台上各个成员的最佳方式

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

于 2012-06-15T19:50:08.553 回答
0

了解结构大小的工作方式很重要。例如:

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 是浪费的填充

于 2012-06-15T19:56:05.520 回答