5

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

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

pStudentRecord* g_ppRecords;
int g_numRecords =0;

这里 g_ppRecords 应该是一个指向结构的指针数组。我完全无法理解的是,该语句如何pStudentRecords *g_ppRecords;意味着g_ppRecords是一个数组,因为数组应该定义为

type arrayname[size];

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

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

4 回答 4

3

编辑:更新了“大错误”部分。

关于 C 风格(不同于 C++!)类型定义的快速课程,以及它为何如此,以及如何使用它。

首先,一个基本的 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 *) 的指针。

在这里指出操作员优先级很重要。取消引用运算符 [] 优先于 * 之一。所以要绝对清楚,我们正在做的是:

*(array3[0]) = 4;

现在,让我们将主题更改为结构和类型定义。

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

为什么你会 typedef 一个匿名结构?好吧,为了可读性!

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 个字节。这是因为我们定义了一个指向结构的指针,而不是结构本身。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 和其他内容的深夜课程结束了。

于 2012-09-15T04:48:27.273 回答
2

观察它pStudentRecord是 typedef'd 作为指向结构的指针。C 中的指针只是指向内存块的开头,无论该块包含 1 个元素(普通的“标量”指针)还是 10 个元素(“数组”指针)。因此,例如,以下

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

pc指向以字符开头的一段记忆,'x'而以下

char *s = "abcd";

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

g_ppRecords因此,一旦分配,我可以通过例如访问元素g_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]不是有效指针,类型仍然有效,因此编译器将计算正确的大小。

于 2012-09-15T03:42:28.873 回答
0

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

简而言之,你已经分配了空间,如果它的长度合适,你可以随意对待它,包括作为一个数组。

我不确定您为什么要为 g_numRecords + 1 记录分配空间。除非 g_numRecords 的命名令人困惑,否则数组中的空间比您需要的多一个。

于 2012-09-15T03:50:47.313 回答
-1

这里 g_ppRecords 应该是一个指向结构的指针数组。我完全不明白的是,声明 *pStudentRecords g_ppRecords; 意味着 g_ppRecords 是一个数组。作为一个数组应该定义为

类型数组名[大小];

umm type arrayname[size];是在 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);

工作正常!

于 2012-09-15T04:14:08.040 回答