繁体   English   中英

指向结构的动态数组指针

[英]Dynamic array of pointers to structs

我必须使用以下代码块进行学校作业,严格禁止任何修改。

typedef struct 
{
    char* firstName;
    char* lastName;
    int id;
    float mark;
}* pStudentRecord;

pStudentRecord* g_ppRecords;
int g_numRecords =0;

这里g_ppRecords应该是一个指向结构的指针数组。 我完全没有理解的是, pStudentRecords *g_ppRecords;的语句pStudentRecords *g_ppRecords; g_ppRecords表示为数组,因为数组应定义为

type arrayname[size];

我尝试动态地为g_ppRecords分配内存,但这没有帮助。

g_ppRecords = (pStudentRecord*) malloc(sizeof(pStudentRecord*)*(g_numRecords+1));

编辑:更新了“BIG MISTAKE”部分。

快速学习C风格(不同于C ++!)typedef,以及它是如何使用它以及如何使用它。

首先,一个基本的typedef技巧。

typedef int* int_pointer;
int_pointer ip1;
int *ip2;
int a;    // Just a variable
ip1 = &a; // Sets the pointer to a
ip2 = &a; // Sets the pointer to a
*ip1 = 4; // Sets a to 4
*ip2 = 4; // Sets a to 4

ip1和ip2是相同的类型:指向类型int的指针,即使你没有在ip1的声明中放置*。 那*代替了宣言。

切换主题。 你说的是将数组声明为

int array1[4];

要在运行时动态执行此操作,您可能会:

int *array2 = malloc(sizeof(int) * 4);
int a = 4;
array1[0] = a;
array2[0] = a; // The [] implicitly dereferences the pointer

现在,如果我们想要一个指针数组怎么办? 它看起来像这样:

int *array1[4];
int a;
array1[0] = &a; // Sets array[0] to point to variable a
*array1[0] = 4; // Sets a to 4

让我们动态分配该数组。

int **array2 = malloc(sizeof(int *) * 4);
array2[0] = &a; // [] implicitly dereferences
*array2[0] = 4; // Sets a to 4

请注意int **。 这意味着指向指向int的指针。 如果我们选择,我们可以使用指针typedef。

typedef int* array_of_ints;
array_of_ints *array3 = malloc(sizeof(array_of_ints) * 4);
array3[0] = &a; // [] implicitly dereferences
*array3[0] = 4; // Sets a to 4

看看最后一个声明中只有一个*? 那是因为其中一个是“在typedef中”。 使用最后一个声明,您现在拥有一个大小为4的数组,其中包含4个指向int的指针(int *)。

在这里指出操作者的优先权非常重要。 解引用运算符[]优先于* 1。 所以绝对清楚,我们正在做的是:

*(array3[0]) = 4;

现在,让我们将主题更改为结构和typedef。

struct foo { int a; }; // Declares a struct named foo
typedef struct { int a; } bar; // Typedefs an "ANONYMOUS STRUCTURE" referred to by 'bar'

你为什么要输入一个匿名结构? 好吧,为了便于阅读!

struct foo a; // Declares a variable a of type struct foo
bar b;        // Notice how you don't have to put 'struct' first

声明一个功能......

funca(struct foo* arg1, bar *arg2);

看看我们怎么没有把'struct'放在arg2前面?

现在,我们看到您必须使用的代码以此方式定义结构:

typedef struct { } * foo_pointers;

这类似于我们之前做过指针数组的方式:

typedef int* array_of_ints;

并排比较

typedef struct { } * foo_pointers;
typedef int* array_of_ints;

唯一的区别是一个是struct {}而另一个是int。

使用我们的foo_pointers,我们可以声明一个指向foo的指针数组:

foo_pointers fooptrs[4];

现在我们有一个数组存储4个指向我们无法访问的匿名结构的指针。

主题开关!

对你而言,你的老师犯了一个错误。 如果查看上面类型foo_pointers的sizeof(),就会发现它返回指向该结构的指针的大小,而不是结构的大小。 对于32位平台,这是4个字节,对于64位平台,这是8个字节。 这是因为我们为一个结构键入了一个POINTER,而不是一个结构本身。 sizeof(pStudentRecord)将返回4。

所以你不能以明显的方式为结构本身分配空间! 但是,编译器允许这种愚蠢。 pStudentRecord不是可用于有效分配内存的名称/类型,它是指向匿名“概念”结构的指针,但我们可以将其大小提供给编译器。

pStudnetRecord g_ppRecords [2]; pStudentRecord * record = malloc(sizeof(* g_ppRecords [1]));

更好的做法是这样做:

typedef struct { ... } StudentRecord;  // Struct
typedef StudentRecord* pStudentRecord; // Pointer-to struct

我们现在能够以清晰的方式创建struct StudentRecord,以及使用pStudentRecord指向它们的指针。

虽然你被迫使用的方法是非常糟糕的做法,但目前这不是一个问题。 让我们回到我们使用整数的简化示例。

如果我想制作一个使我的生活变得复杂的typedef但是解释这里发生的概念怎么办? 让我们回到旧的int代码。

typedef int* array_of_ints;
int *array1[4];
int **array2 = malloc(sizeof(int *) * 4); // Equivalent-ish to the line above
array_of_ints *array3 = malloc(sizeof(array_of_ints) * 4);
int a, b, c, d;
*array1[0] = &a; *array1[1] = &b; *array1[2] = &c; *array1[3] = &d;
*array2[0] = &a; *array2[1] = &b; *array2[2] = &c; *array2[3] = &d;
*array3[0] = &a; *array3[1] = &b; *array3[2] = &c; *array3[3] = &d;

如您所见,我们可以在pStudentRecord中使用它:

pStudentRecord array1[4];
pStudentRecord *array2 = malloc(sizeof(pStudentRecord) * 4);

把所有东西放在一起,逻辑上遵循:

array1[0]->firstName = "Christopher";
*array2[0]->firstName = "Christopher";

是等价的。 (注意:不要像我上面那样完成;在运行时将char *指针分配给字符串只有在你知道已经分配了足够的空间时才可以。

这只是最后一点。 我们对malloc所有这些记忆做了什么? 我们如何释放它?

free(array1);
free(array2);

关于指针,匿名结构的typedef和其他东西的深夜课程结束了。

注意pStudentRecordpStudentRecord作为指向结构的指针。 C中的指针只指向内存块的开始,无论该块是包含1个元素(正常的“标量”指针)还是10个元素(“数组”指针)。 因此,例如,以下内容

char c = 'x';
char *pc = &c;

使pc指向一个以字符'x'开头的内存,而以下内容

char *s = "abcd";

使s指向一个以"abcd"开头的内存(后面跟一个空字节)。 类型相同,但它们可能用于不同的目的。

因此,一旦分配,我可以通过执行例如g_ppRecords[1]->firstName来访问g_ppRecordsg_ppRecords[1]->firstName

现在,要分配这个数组:你想使用g_ppRecords = malloc(sizeof(pStudentRecord)*(g_numRecords+1)); (虽然注意sizeof(pStudentRecord*)sizeof(pStudentRecord)是相等的,因为它们都是指针类型)。 这使得未初始化的结构指针数组。 对于数组中的每个结构指针,您需要通过分配新结构为其赋值。 问题的关键在于如何分配单一结构,即

g_ppRecords[1] = malloc(/* what goes here? */);

幸运的是,您实际上可以取消引用sizeof指针:

g_ppRecords[1] = malloc(sizeof(*g_ppRecords[1]));

请注意, sizeof是一个编译器构造。 即使g_ppRecords[1]不是有效指针,该类型仍然有效,因此编译器将计算正确的大小。

数组通常用指向其第一个元素的指针来引用。 如果你为10个学生记录malloc足够的空间然后在g_ppRecords中存储指向该空间开头的指针,g_ppRecords [9]将向前计数9个记录指针长度并取消引用那里的内容。 如果你已经正确管理了你的空间,那么阵列中的最后一条记录会是什么,因为你预留了足够的10个空间。

简而言之,您已经分配了空间,如果它是正确的长度,您可以随心所欲地对待它,包括作为数组。

我不确定你为什么要为g_numRecords + 1记录分配空间。 除非g_numRecords被混淆地命名,否则你的数组中的空间将超过你需要的空间。

这里g_ppRecords应该是一个指向结构的指针数组。 我完全没有理解的是,声明怎么可能* pStudentRecords g_ppRecords; 意味着g_ppRecords是一个数组。 作为数组应定义为

type arrayname [size];

type arrayname[size]; of many ways to define an array in C. 是在C中定义数组的许多方法的

这静态地定义了一个数组,大多数值根据它的定义位置存储在堆栈中,数组的大小必须在编译时知道,尽管在某些现代编译器中可能不再是这种情况。

另一种方法是在运行时动态创建一个数组,所以我们不必知道编译时的大小,这是指针进来的地方,它们是存储动态分配的内存块地址的变量。

一个简单的例子就是这种type *array = malloc(sizeof(type) * number_of_items); malloc返回一个存储在array的内存地址,注意我们不会出于安全原因对返回类型进行类型转换。

回到手头的问题。

typedef struct 
{
    char* firstName;
    char* lastName;
    int id;
    float mark;
}*  pStudentRecord;

pStudentRecord* g_ppRecords;
int g_numRecords = 0;

这个typedef有点不同于大多数注意}*它基本上是一个指向结构的指针,所以这个:

pStudentRecord* g_ppRecords;

实际上是:

struct 
{
    char* firstName;
    char* lastName;
    int id;
    float mark;
}** pStudentRecord;

它指向指针的指针,为什么他们会以这种方式定义typedef ,它超出我的范围,我个人不推荐它,为什么呢?

一个问题是我们如何才能通过其名称获得结构的大小? 简单我们不能! 如果我们使用sizeof(pStudentRecord)我们将获得48具体取决于底层架构,因为这是一个指针,在不知道结构大小的情况下我们无法使用其typedef名称真正动态分配它,那么我们能做些什么呢? ,声明第二个结构如下:

typedef struct 
{
    char* firstName;
    char* lastName;
    int id;
    float mark;
} StudentRecord;

g_ppRecords = malloc(sizeof(StudentRecord) * g_numRecords);

无论哪种方式,您确实需要联系原始创建此代码的人员或维护和提出您的问题的人员。

g_ppRecords=(pStudentRecord) malloc( (sizeof(char*) + 
                                  sizeof(char*) + 
                                  sizeof(int)   + 
                                  sizeof(float)) *(g_numRecords+1));

这似乎是一种可能的方式,遗憾的是,对结构没有任何保证 ,因此它们实际上可以在成员之间包含填充,因此结构的总大小实际上可以大于其组合成员,更不用说地址可能了不同。

编辑

显然,我们可以通过简单地推断它的类型来获得结构的大小

所以:

pStudentRecord g_ppRecords = malloc(sizeof(*g_ppRecords) * g_numRecords);

工作良好!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM