动态内存分配与指向指针的指针

6

我正在尝试理解在使用多级指针时何时需要使用malloc。例如,

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

int main() {
    typedef struct {
        char first[10];
        char last[10];
    } Person;

    Person *p;

    p = malloc(sizeof(Person));
    strcpy(p->first, "John");
    strcpy(p->last, "Doe");

    printf("First: %s Last:%s\n", p->first, p->last);

    return 0;
}

在这个第一个版本中,我使用 Person *p 并且只使用 malloc 来为类型 Person 分配空间。在第二个版本中,我将会把 Person *p 改为 Person **p
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    typedef struct {
        char first[10];
        char last[10];
    } Person;

    Person **p;

    *p = malloc(sizeof(Person));
    strcpy((*p)->first, "John");
    strcpy((*p)->last, "Doe");

    printf("First: %s Last:%s\n", (*p)->first, (*p)->last);

    return 0;
}

我仍然只使用一个 malloc 即可,即使现在有另一个指针。
在这第三个版本中,我将使用 Person ***p
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    typedef struct {
        char first[10];
        char last[10];
    } Person;

    Person ***p;

    *p = malloc(sizeof(void));
    **p = malloc(sizeof(Person));
    strcpy((**p)->first, "John");
    strcpy((**p)->last, "Doe");

    printf("First: %s Last:%s\n", (**p)->first, (**p)->last);

    return 0;
}

我的问题:

1)为什么在第三个版本中需要为**p分配空间,但不需要为*p分配空间?它们都是指向指针的指针?

2)另外,在第二个或第三个版本中,为什么我不需要为p分配空间?

3)在第三个版本中,为*p分配的正确大小是多少?在我的64位Mac上,sizeof(void)为1,sizeof(void*)为8,两者似乎都可以工作,但哪一个是正确的?


1
你需要在v2上分配p。你的代码是错误的。 - Amit
3
启用警告重新编译代码,你会发现一些问题。 - August Karlstrom
1
正确的是void。void是一个指针,这意味着它的大小足以包含您的64位计算机中的每个内存地址。如果您的计算机使用32位系统,则它可能会更小。此外,Amit是正确的,您的第二个版本是错误的,您需要为第一个指针(p)分配内存。如果您使用警告标志(-Wall -Wextra)编译,您将得到以下警告:warning: ‘p’ is used uninitialized in this function [-Wuninitialized] *p = malloc(sizeof(Person)); - loginn
1
@Idr 没错。虽然最好使用 malloc(sizeof(Person*)); 而不是 malloc(sizeof(void*));,因为你已经知道要使用的类型。这并不会改变计算机方面的任何东西,但可以使代码更清晰。 - loginn
1
@ldr 尝试和错误不是学习C语言的好方法,您无法确定您的代码是否正确,或者它是否已损坏但恰好此次产生了正确的输出。 - M.M
显示剩余4条评论
2个回答

7
  1. 在任何情况下,对一个未初始化的指针*p进行解除引用将引发未定义的行为。

  2. 当为指针分配空间时,您通常希望使用sizeof运算符分配其所指向内容的大小的内存。这是唯一一种例外可以编写*p的情况,并且不违反第1条规则。

因此,第3个示例可能如下所示:

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

typedef struct {
        char first[10];
        char last[10];
} Person;

int main(void) {
    Person ***p;

    p = malloc(sizeof *p); 
    *p = malloc(sizeof **p);
    **p = malloc(sizeof ***p);
    strcpy((**p)->first, "John");
    strcpy((**p)->last, "Doe");

    printf("First: %s Last:%s\n", (**p)->first, (**p)->last);

    free(**p);
    free(*p);
    free(p);

    return 0;
}

非常棒的指向指针的演示,谢谢! - ProfNandaa

5
我只是在这里总结一下:
1)您需要为*p分配空间。如果您使用valgrind运行第二个程序,则会出现分配大小错误(1而不是8);*p是指向指针的指针,但**p不是,它是指向结构体的指针。
2)在两种情况下都需要分配空间,并且如果您打开警告(无论如何,您都不应将其关闭),则会收到以下警告:
warning: ‘p’ is used uninitialized in this function [-Wuninitialized] *p = malloc(sizeof(Person));

3) 正确的是 void*void* 是一个指针,意味着它的大小足以容纳你的 64 位计算机中的每个内存地址。如果您的计算机使用的是 32 位系统,则其可能会更小。尽管使用

malloc(sizeof(Person*)); 而不是 malloc(sizeof(void*));

因为您已经知道要使用的类型,这并不会改变计算机方面的任何东西,但会使代码更清晰易懂。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接