结构体的指向指针

4

我有以下代码:

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

typedef struct {
    int age;
} data;

int storage (data **str) {
    *str = malloc(4 * sizeof(**str));
    (*str)[0].age = 12;
    return 0;
}

int main() {
    data *variable = NULL;
    storage(&variable);
    return 0;
}

这段话来源于一个网站。我认为自己对基本的指向指针的概念有误解,因为在这段代码中,我们有一个指向结构体的指针variable,并将其传递给了希望接受结构体指针的指向指针参数的函数storage。在内存被分配后,我不理解这个赋值操作。

(*str)[0].age = 12

这个赋值好像是将str分配为(*)[]类型。我不理解这个赋值是如何工作的,就好像str现在是一个指向结构体数组的指针?


1
你查过 & 运算符的作用吗? - EOF
返回变量的地址 - Semantics
1
当我说 a[b] 等同于 *(a+b) 时,我的意思是等同。你交换这两个表达式的“方向”是没有关系的。因此,你认为只有当 a 是一个数组时才能使用 a[b] 的信念是错误的。事实上,在 a[b] 被评估时,a 不再被视为数组,而是作为指针处理。 - EOF
2
如果a是空指针,则a + 0会导致未定义的行为。@EOF - M.M
1
我们一直在学习关于C语言的新知识。例如,@M.M,C11草案标准n1570 6.5表达式6 中提到:“访问存储值的对象的有效类型是对象的声明类型(如果有)。87)分配的对象没有声明类型。” 那么为什么你会说malloc()分配了一个数组 - EOF
显示剩余20条评论
4个回答

2

可以这样进行解释

main:
 data* variable = NULL;   //variable is a pointer
 storage(&variable)       //the address of the pointer is &variable 

storage(data**) 允许函数获取指针 variable 的地址。

这使得 storage 可以改变 variable 指向的内容。

storage 中,以下语句通过解引用(因为我们传递了 variable 的地址)更改了 variable 指向的内容:

*str = malloc(4 * sizeof(**str) )

malloc函数分配一个内存块,其中包含四个结构体(每个结构体的大小为sizeof(struct data)字节)。

结构体只是一种方便访问内存部分的方式,结构体描述了内存的布局。该语句

(*str)[0].age = 12;

是等同于

data* d = *str; 
d[0].age = 12;

或者你可以将其写成带偏移量的指针形式:
data* d = *str;
*(d + 0).age = 12;

编辑:有关 malloc 的澄清

malloc 返回以字节分配的内存块, malloc 的返回类型是 void * ,因此根据定义它没有类型,可以分配给任意类型的指针:

T* ptr = malloc(n * sizeof(T));

在将ptr赋值后,通过使用指针T*,内存被视为一个或多个类型为T的元素。

在你的陈述中,当你说malloc分配了一个由4个结构体组成的数组时,我就感到困惑了。正如@EOF在他的评论中指出的那样,我理解了a[b]等同于*(a+b)这一部分,我想我可以将*str表达式推导为一个数组的第一个元素,但是在阅读了你的答案之后,我仍然感到困惑。Malloc只是分配内存空间,你怎么知道它是一个由4个结构体组成的数组?按照我的理解,大小只是4sizeof(struct)。 - Semantics
@语义学:好的,malloc()并不分配数组。它分配没有声明类型的内存。然而,根据C11规则6.5表达式,6有效类型,一旦您将内存用于特定类型,它就会变成该类型。因此,您可以将malloc()返回的指针视为指向数组第一个元素的指针。 - EOF
@EOF 是的,这正是我需要知道的。这就是我正在寻找的信息。如果您能将其作为答案发布,我将很高兴接受它。或者如果Anders在他的答案中也可以包含这些信息。 - Semantics
@Semantics malloc 的返回类型是 void*,因此它没有类型,只是一块内存。指针类型决定了如何寻址内存,例如当您执行 ptr + 1 时计算字节的偏移量。 - AndersK
@AndersK。malloc() 分配的内存并不会变成指针所指向的类型。只有 存储该类型对象 的行为才会导致内存的 有效类型 被确定。请参阅 C11 草案标准 n1570,6.5 表达式,6 有效类型 - EOF
是的,这就是我说的(或者试图表达的),它并不会变成指针的类型。我说过你通过指针访问内存。现在我已经编辑了我的回答。 - AndersK

2

首先,关于C语言解除指针引用的语法:

a[b] 等同于 *(a + b),等同于 *(b + a),等同于 b[a]

现在,在以下内容中:

int main() {
    data *variable = NULL;
    storage(&variable);
    return 0;
}

variable 是指向 data 的指针类型,因此它的地址 &variable 是指向指针类型的 data。这被传递给 int storage(data **str),并且是参数 str 的正确类型。

int storage (data **str) {
    *str = malloc(4 * sizeof(**str));
    (*str)[0].age = 12;
    return 0;
}

在这里,str被取消引用,产生一个类型为data *的lvalue,指定与main()variable相同的对象。由于它是一个lvalue,因此可以对其进行赋值。 malloc()分配内存而没有声明类型,足够大(并且足够对齐),以包含四个连续的data类型的对象。它返回指向分配开始位置的指针。
现在,(*str)[0]是指定data类型对象的lvalue,并通过访问malloc()通过此表达式分配的内存,使内存的有效类型成为data(*str)[0].age = 12;将值12分配给此对象的age成员, leaving the other members of the struct (and the rest of the allocated memory) uninitialized.

0

嗯,我认为你的代码与以下代码完全相同:

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

typedef struct
{
    int age;
} data;

int main()
{
    data *variable = NULL;
    variable = malloc(4 * sizeof(*variable));
    *(variable + 0).age = 12;
    return 0;
}

所以,variable 被分配了一个内存块,它足够大,可以容纳 4 个 data(从 variable [0]variable [3])。就是这样。


0
这段代码可能有助于说明正在发生的事情,最有趣的行是
assert(sizeof(**str2) == sizeof(data));

你的数字可能与我的不同,但首先让我们创建一个结构体,用于测试目的的大小相当沉闷但难以伪造。

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

typedef struct {
  uint8_t  age;
  uint8_t  here_as_illustartion_only[1728];
} data;

int main() {
    data    str; 
    data *  str1 = &str;  
    data ** str2 = &str1;  
    printf("sizeof(str)    =%*zu\n", 5, sizeof(str));
    printf("sizeof(str1)   =%*zu\n", 5, sizeof(str1));
    printf("sizeof(str2)   =%*zu\n", 5, sizeof(str2));
    printf("sizeof(*str2)  =%*zu\n", 5, sizeof(*str2));
    printf("sizeof(**str2) =%*zu\n", 5, sizeof(**str2));

    assert(sizeof(**str2) == sizeof(data));
    return 0;
}

在我的电脑上,这将打印出以下内容。
sizeof(str)    = 1729
sizeof(str1)   =    8
sizeof(str2)   =    8
sizeof(*str2)  =    8
sizeof(**str2) = 1729

请注意指向指针的大小,即sizeof(**)是我们正在寻找的数字。

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