結構與指標


在〈定義結構〉中示範的 printAcct 函式,參數直接以結構型態宣告,指定結構實例作為引數時,會建立新的實例並複製各個值域。

如果不希望複製的行為發生,可以傳遞位址,例如:

#include <stdio.h>
typedef const char* String;

typedef struct {  
    String id;  
    String name; 
    double balance;
} Account;

void deposit(Account *acct, double amount) {
    if(amount <= 0) {
        puts("必須存入正數");
        return;
    }
    acct->balance += amount;
}

void withdraw(Account *acct, double amount) {
    if(amount > acct->balance) {
        puts("餘額不足");
        return;
    }
    acct->balance -= amount;
}

int main() { 
    Account acct = {"1234-5678", "Justin Lin", 1000};
    deposit(&acct, 500);
    withdraw(&acct, 200);

    // 顯示 Account(1234-5678, Justin Lin, 1300.000000)
    printf("Account(%s, %s, %f)\n", acct.id, acct.name, acct.balance);

    return 0; 
} 

如果使用結構宣告的指標來存取成員,必須使用 -> 運算子,因為傳遞的是結構實例的位址,函式中對實例的變更,就是對原結構實例的變更。

以上範例的 withdrawdeposit 等函式,都是服務於 Account,若想要的話,也可以令函式成為結構的成員之一,這麼組合有點物件導向語言中,物件與方法的概念,例如:

#include <stdio.h>
#include <stdlib.h>
typedef const char* String;

typedef struct Account {  
    String id;  
    String name; 
    double balance;

    char *_to_str;

    void (*deposit)(struct Account*, double);
    void (*withdraw)(struct Account*, double);
    String (*to_str)(struct Account*);
} Account;

void deposit(Account *acct, double amount) {
    if(amount <= 0) {
        puts("必須存入正數");
        return;
    }
    acct->balance += amount;
}

void withdraw(Account *acct, double amount) {
    if(amount > acct->balance) {
        puts("餘額不足");
        return;
    }
    acct->balance -= amount;
}

String to_str(Account *acct) {
    int n = snprintf(NULL, 0,
        "Account(%s, %s, %f)", 
        acct->id, acct->name, acct->balance
    );

    if(acct->_to_str != NULL) {
        free(acct->to_str);
    }

    acct->_to_str = malloc((n + 1) * sizeof(char));
    snprintf(acct->_to_str, n + 1,
        "Account(%s, %s, %f)", 
        acct->id, acct->name, acct->balance
    );

    return acct->_to_str;
}

Account* new_account(String id, String name, double balance) {
    Account *acct = (Account*) malloc(sizeof(Account));
    acct->id = id;
    acct->name = name;
    acct->balance = balance;
    acct->_to_str = NULL;

    acct->deposit = deposit;
    acct->withdraw = withdraw;
    acct->to_str = to_str;
    return acct;
}

void del_account(Account *acct) {
    free(acct->_to_str);
    free(acct);
}

int main() { 
    Account *acct = new_account("1234-5678", "Justin Lin", 1000);
    acct->deposit(acct, 500);
    acct->withdraw(acct, 200);

    printf("%s\n", acct->to_str(acct));

    del_account(acct);

    return 0; 
}