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;此後不要再存取其元素。這些選擇回答了關於宣告陣列、執行時初始化、在函數中組織設定、以及釋放已配置儲存空間的常見問題。