【问题标题】:Data alignment of structure using malloc使用 malloc 对结构进行数据对齐
【发布时间】:2019-02-08 20:08:45
【问题描述】:

考虑我的结构如下。

struct st
{
  char c;
  double d;
  int i;
};

根据struct st 的内存对齐大小将是 (1+7(padded)+8+4+4(padded))=24bytes

当我像下面这样malloc

struct st *p = malloc(sizeof(*p));

一样
struct st *p = malloc(13);

既然我们只是传递结构的大小(13字节),malloc如何分配24字节来满足内存对齐?

【问题讨论】:

  • 你能证明malloc(13)确实分配了24个字节吗?
  • 它没有,它只分配13 字节,但是有些系统分配的比你要求的多,因为它们以最小大小的块处理内存。不要依赖这个。
  • 对齐方式将取决于实现。例如,在 32 位架构上,double 可能只需要 4 字节对齐,在这种情况下您的 sizeof 不正确。
  • @WeatherVane 在这种情况下,由于 malloc 不处理内存对齐,因此对结构执行 malloc 是错误的?
  • @KBlr 在我的系统上sizeof(struct st)24,因此是sizeof(*p),所以我不知道你为什么还要malloc(13),因为你已经知道需要24 字节.

标签: c malloc memory-alignment


【解决方案1】:

问题错误地假设了

struct st *p = malloc(sizeof(*p));

相同
struct st *p = malloc(13);

事实并非如此。为了测试,

printf ("Size of st is %d\n", sizeof (*p));

打印24,而不是13。

分配和管理结构的正确方法是使用sizeof(X),而不是假设元素是如何打包或对齐的。

【讨论】:

  • 在这种情况下,malloc 没有任何东西可以处理内存对齐和数据打包,因为一切都将由编译器处理
  • @KBlr: malloc 确实在对齐中起作用。需要为可能放置在其中的任何受支持对象返回适当对齐的内存。因此,如果您要求 24 字节,并且 C 实现具有大小小于或等于 24 字节的对象需要 8 字节对齐,那么内存 malloc 返回必须至少 8 字节对齐。如果您要求 4 个字节,而 C 实现没有任何需要 8 字节对齐的大小或更小的对象,但确实有需要 4 字节对齐的 4 字节对象,那么malloc 只需要返回 4-字节对齐的内存。
  • @KBlr :什么内存对齐?唯一必要的对齐是分配块的 start - 然后成员相对于由编译器应用的填充确定的对齐。需要malloc() 才能为任何适合分配空间的对象返回具有适当对齐方式的指针。
  • @EricPostpischil:使用 C11 并包含适当的标头,我创建了类型(和类型别名)typedef struct {alignas(double) char ch;} Char;。显然,sizeof(Char) == sizeof(double)。但是在malloc(1)返回的内存分配中访问Charch成员是UB吗?我一直只是假设malloc 必须符合相同的对齐方式,而不管它的论点是什么——据我所知,常见的实现也是如此——但现在你让我想知道它是否真的是合法的malloc(1) 返回一个未对齐的指针。 TBH,这个想法让我很紧张
  • @rici: alignas 不是标准 C。假设您使用 _Alignas,那么您不应尝试通过从malloc(1) 的结果。 Char 将至少与 double 的对齐要求(不是大小)一样大,如果大于一,则不能在 malloc(1) 的空间中构造。 malloc 必须符合 C 2018 7.22.3 中的对齐方式的措辞对我来说是模棱两可的,但 Char 是(比如说)八个字节的事实意味着在一个字节空间中访问它是不正确的。
【解决方案2】:

malloc 如何分配 24 字节以满足内存对齐,因为我们只是传递结构的大小(13 字节)?

它没有。如果 malloc(13) 恰好返回至少 24 个字节,则这是 malloc 实现的一个怪癖。 malloc 被允许分配比必要更多的空间,并且通常出于字节对齐和各种其他实现原因而必须分配。

我们可以通过一个简单的程序看到这一点。

struct st *a = malloc(13);
struct st *b = malloc(13);
struct st *c = malloc(13);
struct st *d = malloc(13);

printf("%p\n%p\n%p\n%p\n", a, b, c, d);

0x602000003210
0x602000003230
0x602000003250
0x602000003270

从地址中我们可以看出,malloc(13) 返回的指针之间有 32 个字节。大量适合您的 24 个字节,并且程序“有效”。即使malloc(1) 返回相同的结果。

但是如果我们让你的结构更大一点...

struct st {
  char c;
  double b;
  double a;
  double d;
  int i;
};

这是 40 个字节,对齐。现在它不适合 32 字节,我们看到损坏,因为结构的内存相互重叠。

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

struct st {
  char c;
  double b;
  double a;
  double d;
  int i;
};

void print_struct(struct st* st) {
    printf("%c %lf %d\n", st->c, st->d, st->i);
}

int main() {
    const size_t struct_size = sizeof(char) + (sizeof(double) * 3) + sizeof(int);

    printf("sizeof(struct st): %zu\n", sizeof(struct st));
    printf("sizeof fields added together: %zu\n", struct_size);

    struct st *a = malloc(13);
    struct st *b = malloc(13);
    struct st *c = malloc(13);
    struct st *d = malloc(13);

    printf("%p\n%p\n%p\n%p\n", a, b, c, d);

    a->c = 'a';
    a->d = 1.0;
    a->i = 1;

    b->c = 'b';
    b->d = 2.0;
    b->i = 2;

    c->c = 'c';
    c->d = 3.0;
    c->i = 3;

    d->c = 'd';
    d->d = 4.0;
    d->i = 4;

    print_struct(a);
    print_struct(b);
    print_struct(c);
    print_struct(d);
}

sizeof(struct st): 40
sizeof fields added together: 29
0x602000003210
0x602000003230
0x602000003250
0x602000003270
a 1.000000 98
b 2.000000 99
c 3.000000 100
d 4.000000 4

98 是 ascii b。 99 是 ascii c。 100 是 ascii d。这表示a-&gt;ib-&gt;c 重叠,b-&gt;ic-&gt;c 重叠,等等。

【讨论】:

  • 这个答案接受了问题的错误前提,即sizeof *p 产生 13 并且未能纠正它。
  • @EricPostpischil OIC,OP 认为 sizeof(*p) 是 13。好吧,它回答了问题的另一半;为什么malloc(13) 恰好起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-05-19
  • 1970-01-01
  • 2011-07-15
  • 2013-06-03
  • 2023-03-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多