【问题标题】:C memory allocation for struct with malloc使用 malloc 为结构分配 C 内存
【发布时间】:2015-11-09 01:46:40
【问题描述】:

我试图了解 C 中 struct 的内存分配,但我被困住了。

struct Person {
    char *name;
    int age;
    int height;
    int weight;
};
struct Person *Person_create(char *name, int age, int height, int weight)
{
    struct Person *who = malloc(sizeof(struct Person));
    assert(who != NULL);

    who->age = age;
    who->height = height;
    who->weight = weight;
    who->name = strdup(name);

    return who;
}
int main(int argc, char *argv[])
{
    struct Person *joe = Person_create("ABC", 10, 170, 60);
    printf("Size of joe: %d\n", sizeof(*joe));
    printf("1. Address of joe \t= %x\n", joe);
    printf("2. Address of Age \t= %x\n", &joe->age);
    printf("3. Address of Height \t= %x\n", &joe->height);
    printf("4. Address of Weight \t= %x\n", &joe->weight);
    printf("5. Address of name \t= %x\n", joe->name);
...

我不明白的是这个结构的内存分配。在我的打印输出中,我看到了这个:

Size of joe: 24
1. Address of joe   = 602010
2. Address of Age   = 602018
3. Address of Height    = 60201c
4. Address of Weight    = 602020
5. Address of name  = 602030

问题:

  • 为什么 1 和 2 之间有间隙?
  • 为什么 4 和 5 之间有差距?
  • *name 的大小是如何计算的,因为名称仅指向 第一个字符?

【问题讨论】:

  • 4的间隙是char*的大小。最后一个空白是 char* 而不是指针的地址
  • 要打印带有printf 的指针,请使用"%p" 格式代码。
  • @joachimpileborg 并使用%zu 代替size_t
  • %d对应的参数类型必须为int;您提供了一个 size_t 值,它调用了未定义的行为。也许你的意思是%zu。类似地,对应于%x 的参数类型应该是unsigned int,但是您已经提供了各种指针值......也许您打算使用%p 并强制转换为(void *)assert 应该用作调试辅助,而不是错误处理功能。这些用词不当使我对您正在阅读的书感到担忧;顺便问一下,那本书是什么?
  • 顺便说一下,没有要求两个连续的内存分配应该返回两个连续的内存块。你也不要打印name成员的位置,而是@返回的指针987654339@ 调用(它本身调用malloc),要打印name 成员的位置,您应该使用地址运算符&,就像其他人一样。这也是造成 4 到 5 之间“差距”的原因之一。

标签: c memory memory-management struct malloc


【解决方案1】:

这是由于称为数据对齐。引用this website

C/C++ 中的每种数据类型都会有对齐要求(实际上它是由处理器架构而非语言强制要求的)。

然后将这个要求扩展到结构:

由于各种数据类型的对齐要求,结构的每个成员都应该自然对齐。

您可以通过this文章详细阅读..

【讨论】:

  • -1,对不起。数据对齐不是这里的主要因素。 OP 有三个问题:“为什么 1 和 2 之间有差距?”、“为什么 4 和 5 之间有差距?”和“如何计算 *name 的大小仅作为 name 点到第一个字符?”。数据对齐确实在第二个问题中发挥了作用,但这并不是故事的全部(也不是故事的重要部分);它在回答第一个和第三个问题时没有任何作用。
  • 这个答案的大量赞成/反对票可能是由于to this Meta question
  • 4 年后 - 是的,这是一个答案。这个答案确实回答了一个问题。或者很多。但它在 OP 中完全没有解决任何问题。
【解决方案2】:

对象joe的地址和数据成员age的地址之间没有间隙。该范围被数据成员name占用。

struct Person {
    char *name;
    int age;
    //...

根据输出

1. Address of joe   = 602010
2. Address of Age   = 602018

它占用8个字节,在你的平台上sizeof( char * )等于8。它的地址与对象joe本身的地址一致。

在此声明中

printf("5. Address of name \t= %x\n", joe->name);

您没有输出name 本身的地址。您打印了存储在此指针中的值,该值是使用 strdup 获得的字符串文字 "ABC" 的副本的第一个字符的地址。

所以输出 4 和 5 中的值之间存在差距,因为它们是不同的内存范围。数据成员weight 属于对象joe,而字符串文字"ABC" 的副本存储在对象之外。该对象只有数据成员name,它指向文字副本的第一个字符。

因为name 是一个指针,所以它的大小是这样计算的

sizeof( char * )

sizeof( joe->name )

等于 8,正如我在文章开头所解释的那样。

如果您想确定字符串文字的长度,您应该使用标头<string.h> 中声明的标准函数strlen。例如

printf( "%zu\n", strlen( joe->name ) );

【讨论】:

    【解决方案3】:

    c 标准唯一保证的是第一个成员的地址与结构的地址相同,并且后续成员的地址随着它们在结构中的位置而增加。

    允许编译器在成员之间插入空格;这称为填充。将其视为针对特定平台优化结构的编译器。

    数组在内存中必须始终是连续的。

    【讨论】:

      【解决方案4】:

      为什么1和2之间有差距?

      一个结构的起始地址总是等于它的第一个成员的地址。来自 C 标准:

      6.7.2.1-13。一个指向结构对象的指针,经过适当的转换,指向它的初始成员

      第一个成员不是age,而是name。所以下面两行应该打印相同的地址:

      printf("1. Address of joe \t= %x\n", joe);
      printf("1. Address of name-pointer \t= %x\n", &joe->name);
      

      在您的代码中,

      printf("5. Address of name \t= %x\n", joe->name);
      

      打印的不是指针的地址,而是指针指向的数据的地址。

      *name 的大小如何计算为 name 仅指向第一个字符?

      name是一个指针,无论它指向的数据大小如何,它都会占用 8 个字节的内存(在您的情况下,它可能是一个字符串、单个 char、一个 int 或其他)。

      为什么4和5之间有差距?

      用于存储实际name 字符串的内存不在结构内 - strdup 分配内存某处 以将字符串复制到其中。这恰好是结构的最后一个成员之后的 16 个字节。然后你的name 指针指向这个内存位置。

      请注意,填充和内存对齐仅是结构大小的一个因素(它们与您明确提出的问题无关)。由于该结构包含一个指针(在您的机器上为 8 个字节)和 3 个整数(每个 4 个字节),因此可以假设总大小为 20 个字节。在大多数平台上,内存是 8 字节对齐的——这就是为什么你的结构的大小被四舍五入到 24 字节的原因。这样,如果你声明一个Persons的数组,每个数组元素都从一个8字节对齐的地址开始,即地址值可以被8整除。

      【讨论】:

        【解决方案5】:

        结构的内存布局取决于机器,因此除非您尝试实现 DBMS 或设备驱动程序或类似的东西,否则您不应该为此烦恼。

        sizeof(*name) 等于sizeof(char),我不明白你在这里有什么困惑,你能再解释一下吗?

        【讨论】:

        • 我真的认为这不能解决所提出的问题。
        猜你喜欢
        • 1970-01-01
        • 2015-06-27
        • 2015-01-05
        • 2021-05-29
        • 1970-01-01
        • 2018-11-08
        • 2017-10-05
        • 2012-10-10
        • 2020-11-01
        相关资源
        最近更新 更多