是否保证 C 中的数组元素将连续存储,没有填充?

Is it guaranteed that array elements in C will be stored consecutively, with no padding?

换句话说:如果我以这种方式分配数组,是否可以保证:

void *arr = calloc(nmemb, sizeof(some_type))

那么eltaeltbeltc都会指向内存中的同一个位置,也就是这个数组的some_type类型的第二个元素?

some_type *elta = &((some_type*)arr)[1];
some_type *eltb = ((some_type*)arr)+1;
some_type *eltc = (char*)arr+sizeof(some_type);

我问这个问题的原因是因为我想在 C 中做一个“容器”,如果这不成立,那么我不知道如何 return 一个指针到第一个元素以外的任何其他元素。

是的,有保证。 如果添加了 填充字节,它们将添加到struct some_type内,但不会添加到两个数组元素之间。

E. g.:

struct S
{
    int n;
    short s;

// this is just for illustration WHERE byte padding (typically) would occur!!!
#if BYTE_ALIGNMENT >= 4
    unsigned char : 0;
    unsigned char : 0;
#endif
};
struct S s[2];
size_t d = (char*)(s + 1) - (char*)s;

将字节对齐调整为 4 或 8(或更大的 2 的幂),此结构的大小将为 8,d 也将等于 8,字节对齐设置为 1 或 2,结构将具有大小6 就像 d...

注意:这不是唯一可以出现填充字节的地方:如果您切换成员 ns,则在 s 和 [= 之间需要填充字节12=] 使 n 正确对齐。另一方面,在 n 之后不再需要填充字节,因为结构大小已经可以确保正确对齐。

参考标准:C11, 6.2.5.20:

An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type. 36) Array types are characterized by their element type and by the number of elements in the array. [...]

(由我突出显示!)。

数据对齐是一件复杂的事情,如下例所示(您甚至可以画出自己的实验):

#include <stdio.h>
struct A {  /* no padding */
    char a[3];
};
struct B {
    int  a;
    char b[3];
    /* one byte of padding (in 32bit arch) added here */
};
struct C {  /* no padding again */
    char a[4];
    char b[3];
};

struct D {
    char a[3];
    /* one byte of padding to ensure alignment of next field */
    int  b;
    char c[3];
    /* one byte of padding to ensure alignment of whole struct in arrays */
}

#define P(X) printf("sizeof struct %s == %ld\n", #X, sizeof (struct X))
int main()
{
    P(A);
    P(B);
    P(C);
    P(D);
} /* main */

如您所见,struct A 类型所需的对齐(1 个字节)允许将其放置在内存中的任何位置,并确定不需要填充字节来确定 sizeof 值。

在第二个示例中,我们引入了一个 int 值,它强制整个 struct 进行字对齐(因此可以在正确对齐的内存地址中访问 int)所以这一次,编译器已经(在结构的末尾)填充了额外的字节,因此指定类型的任何数组类型都可以正确对齐,并且可以在有效地址访问其 int 字段。

在第三个示例中,我说明了第二个示例中填充的效果是由于结构中存在 int 字段,因为第三个示例具有相同大小的字段,但是这次一个不需要对齐的字段,所以没有插入填充,因为里面的所有单独的数据类型都需要对齐 1.

输出(在我的系统上,MAC OSX 系统)是:

sizeof struct A == 3
sizeof struct B == 8
sizeof struct C == 7
sizeof struct D == 12