【问题标题】:Is it guaranteed that array elements in C will be stored consecutively, with no padding?是否保证 C 中的数组元素将连续存储,没有填充?
【发布时间】:2017-10-01 15:30:29
【问题描述】:

换句话说:如果我有一个这样分配的数组,是否可以保证:

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 中做一个“容器”,如果这不成立,那么我不知道如何返回指向任何其他元素的指针,而不是第一个。

【问题讨论】:

  • 为什么会有一些填充?当您malloc(nmemb * sizeof(some_type)) 时,您没有填充。
  • 数组在哪里?
  • ((some_type*)arr)++ 你不能后增右值。
  • 再问,这里的数组在哪里
  • @gaazkam 指针不是数组,请注意。 :)

标签: c arrays padding memory-alignment pointer-arithmetic


【解决方案1】:

是的,这是有保证的。 如果添加了填充字节,它们会被添加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,则在 sn 之间需要填充字节以使 n 正确对齐.另一方面,在 n 之后不再需要填充字节,因为结构大小已经可以确保正确对齐。

参考标准:C11, 6.2.5.20:

数组类型描述了具有特定成员对象类型(称为元素类型)的连续分配非空对象集。 36) 数组类型的特征在于它们的元素类型和数组中元素的数量。 [...]

(由我突出显示!)。

【讨论】:

  • 这个语法是什么:unsigned char : 8;?我以前从未见过这样的语法。
  • 什么是BYTE_ALIGNMENT?字节与字节以外的任何东西对齐,即1short 和 unsigned char` 之间不一定有填充 - 通常没有。
  • @gaazkam 这是bit-field。我用它来添加匿名字节...
  • @Olaf 好吧,看我的评论:“这只是为了说明会发生字节填充的地方”——宏不存在,当然,它应该代表编译器设置,以及 unsigned char位字段被认为表示填充字节。这不是很明显吗?
  • @Aconcagua:填充字节可以出现在结构中的 any 字段之后。无法保证特定布局。这取决于目标平台的实现和 ABI。另请注意,C99 不是 C 标准。它在 6 年前随着 C11 的发布而被取消。
【解决方案2】:

数据对齐是一个复杂的问题,如下例所示(你甚至可以画出来做自己的实验):

#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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-25
    • 2011-02-19
    • 2020-07-14
    • 2022-01-26
    • 2013-07-21
    • 2014-01-21
    • 1970-01-01
    相关资源
    最近更新 更多