簡體   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