HOWTO · C
在 C 語言中初始化結構體陣列
在 C 語言中用大括號清單、執行時配置,或函數加迴圈來初始化結構體陣列,並附記憶體釋放與安全字串複製的說明。
本頁內容
當陣列的值在宣告處已知時,使用大括號初始化器。如果陣列的大小或值來自執行時資料,則配置儲存空間並逐個元素賦值,或者把陣列傳給一個填滿它的函數。在每種情況下,初始化器中的值都遵循成員的宣告順序。
在 C 語言中,struct 把相關欄位歸到一個型別之下。這裡,每個 Student 都有姓名、年齡和 GPA。
結構體與陣列初始化基礎
先宣告型別,再宣告該型別的陣列。關鍵字 struct 引入記錄型別,結構體初始化器按宣告順序列出其欄位:name,然後 age,然後 gpa。用於姓名的字元陣列會儲存一個結尾的空字元,普通字串字面值會自動包含它。
用初始化清單初始化固定大小的陣列
使用巢狀的大括號,為陣列的每個元素提供一個 Student 初始化器。這個完整範例使用原始碼中已知的值,並印出每條初始化後的記錄。
執行此範例:
#include <stdio.h>
struct Student {
char name[50];
int age;
float gpa;
};
int main(void) {
struct Student students[3] = {
{"Alice", 20, 3.5f},
{"Bob", 22, 3.8f},
{"Charlie", 19, 3.2f}
};
for (int i = 0; i < 3; ++i) {
printf("%s: age %d, GPA %.2f\n",
students[i].name, students[i].age, students[i].gpa);
}
return 0;
}
Alice: age 20, GPA 3.50
Bob: age 22, GPA 3.80
Charlie: age 19, GPA 3.20
在執行時配置並初始化陣列
當元素數量在程式執行期間確定時,為該數量的結構體配置足夠的儲存空間。malloc 預留儲存空間,但不會給欄位賦予有意義的值,因此在讀取每個元素之前先填滿它。檢查傳回的指標,然後在陣列不再需要時用 free 釋放它。
這個完整範例在執行時選擇數量,並逐條賦值記錄。
#include <stdio.h>
#include <stdlib.h>
struct Student {
char name[50];
int age;
float gpa;
};
int main(void) {
size_t count = 3;
struct Student *students = malloc(count * sizeof *students);
if (students == NULL) {
fputs("Memory allocation failed\n", stderr);
return 1;
}
students[0] = (struct Student){"Alice", 20, 3.5f};
students[1] = (struct Student){"Bob", 22, 3.8f};
students[2] = (struct Student){"Charlie", 19, 3.2f};
for (size_t i = 0; i < count; ++i) {
printf("%s: age %d, GPA %.2f\n",
students[i].name, students[i].age, students[i].gpa);
}
free(students);
return 0;
}
Alice: age 20, GPA 3.50
Bob: age 22, GPA 3.80
Charlie: age 19, GPA 3.20
用於單個賦值的複合字面值需要 C99 或更高版本。如果配置失敗,程式會報告失敗,並在存取陣列之前結束。
用函數和迴圈初始化陣列元素
當把重複的初始化移到函數中能讓呼叫程式碼更清晰時,就這樣做。對於固定大小的字串,在複製之前檢查來源的長度:strlen(source) < sizeof destination 會為結尾的空字元留出空間。下面的函數會報告過長的姓名並且不複製它;發生這種情況時呼叫者停止。
這個完整範例從一組平行的姓名、年齡和 GPA 初始化每條記錄。
#include <stdio.h>
#include <string.h>
struct Student {
char name[50];
int age;
float gpa;
};
static int initialize_student(struct Student *student,
const char *source,
int age,
float gpa) {
char destination[50];
size_t length = strlen(source);
if (length >= sizeof destination) {
fputs("Name is too long\n", stderr);
return 0;
}
memcpy(destination, source, length + 1);
strcpy(student->name, destination);
student->age = age;
student->gpa = gpa;
return 1;
}
int main(void) {
const char *names[] = {"Alice", "Bob", "Charlie"};
const int ages[] = {20, 22, 19};
const float gpas[] = {3.5f, 3.8f, 3.2f};
struct Student students[3];
for (size_t i = 0; i < 3; ++i) {
if (!initialize_student(&students[i], names[i], ages[i], gpas[i])) {
return 1;
}
}
for (size_t i = 0; i < 3; ++i) {
printf("%s: age %d, GPA %.2f\n",
students[i].name, students[i].age, students[i].gpa);
}
return 0;
}
Alice: age 20, GPA 3.50
Bob: age 22, GPA 3.80
Charlie: age 19, GPA 3.20
長度測試使用 >= 作為拒絕條件,這等價於要求在複製之前滿足 strlen(source) < sizeof destination。memcpy 只有在這個檢查之後,才複製所測量的字元及其終止符。
選擇合適的初始化方法
- 當陣列大小和值在原始碼中已知時,選擇大括號初始化器。當你想稍後再賦給元素值時,用
struct Student students[3];宣告陣列。 - 當元素數量在程式執行期間獲得時,選擇執行時配置。記住,配置只提供儲存空間:初始化欄位,並在用完後呼叫
free。 - 當初始化邏輯需要重用,或要與主流程分開保存時,選擇函數加迴圈。在把輸入字串複製到固定大小的成員之前先驗證它們。
具有自動儲存期的陣列,在其所在作用域結束時被釋放。用 malloc 獲得的陣列會一直保持配置狀態,直到把它傳給 free;此後不要再存取其元素。這些選擇回答了關於宣告陣列、執行時初始化、在函數中組織設定、以及釋放已配置儲存空間的常見問題。