《C和指針》第十章習題聚焦結構體嵌套、聯合類型節省空間、位域優化的實戰建模。本文詳細解析兩道核心習題:電話號碼結構體層次設計與銷售記錄多態數據建模,展示 C 語言無繼承機制下的組合復用藝術。
// 電話號碼三段式
typedef struct Phone {
char *area_code; // 區號
char *exchange; // 交換台號
char *station; // 站號
} Phone;
// 通話記錄
typedef struct Call {
char *date; // 日期
char *time; // 時間
Phone *used; // 使用者號碼
Phone *self; // 自機號碼
Phone *called; // 對方號碼
} Call;
char* 指向動態分配字符串,支持可變長度Call 持有 Phone*,需明確誰負責 freePhone銷售記錄包含公共字段(姓名、地址、型號)與互斥的交易細節:
// 現金交易細節
typedef struct Cash {
float retail_price;
float actual_price;
float sales_tax;
float licensing_fee;
} Cash;
// 貸款交易細節
typedef struct Loan {
float retail_price;
float actual_price;
float sales_tax;
float licensing_fee;
float down_payment;
float loan_duration;
float interest_rate;
float monthly_payment;
char *bank_name; // 動態字符串
} Loan;
// 租賃交易細節
typedef struct Lease {
float retail_price;
float actual_price;
float down_payment;
float security_deposit;
float monthly_payment;
int lease_term;
} Lease;
// 銷售記錄:公共字段 + 聯合體
typedef struct Sale {
char name[20];
char address[40];
char model[20];
int type; // 標籤:1=Cash, 2=Loan, 3=Lease
union {
Cash cash;
Loan loan;
Lease lease;
} details;
} Sale;
sizeof(Sale) = 公共字段 + max(sizeof(Cash), sizeof(Loan), sizeof(Lease))type 標籤決定解析哪個成員void print_sale(const Sale *s) {
printf("Customer: %s\nModel: %s\n", s->name, s->model);
switch (s->type) {
case 1: // Cash
printf("Cash Sale: $%.2f (tax $%.2f)\n",
s->details.cash.actual_price, s->details.cash.sales_tax);
break;
case 2: // Loan
printf("Loan: $%.2f/mo for %.0f months @ %.2f%%\n",
s->details.loan.monthly_payment,
s->details.loan.loan_duration,
s->details.loan.interest_rate);
break;
case 3: // Lease
printf("Lease: $%.2f/mo for %d months\n",
s->details.lease.monthly_payment,
s->details.lease.lease_term);
break;
default:
printf("Unknown sale type %d\n", s->type);
}
}
// 提取公共交易字段
typedef struct CommonDeal {
float retail_price;
float actual_price;
float sales_tax;
float licensing_fee;
} CommonDeal;
typedef struct SaleV2 {
char name[20];
char address[40];
char model[20];
CommonDeal common; // 所有類型共享
int type;
union {
struct { // Cash 無額外字段
} cash;
struct { // Loan 額外字段
float down_payment;
float loan_duration;
float interest_rate;
float monthly_payment;
char *bank_name;
} loan;
struct { // Lease 額外字段
float down_payment;
float security_deposit;
float monthly_payment;
int lease_term;
} lease;
};
} SaleV2;
// 壓縮標誌位
typedef struct Flags {
unsigned int is_valid : 1;
unsigned int is_paid : 1;
unsigned int is_shipped : 1;
unsigned int reserved : 29;
} Flags; // 僅佔 4 字節 vs 3×bool 可能 12 字節
| 錯誤 | 後果 | 修正 |
|---|---|---|
| 訪問非活躍聯合成員 | 未定義行為(讀取垃圾值) | 嚴格按 type 標籤分支訪問 |
| 未初始化聯合 | 成員內容不定 | 賦值時完整賦值整個聯合或目標成員 |
| 指針成員淺拷貝 | 雙重釋放/洩漏 | 實現深拷貝函數或用引用計數 |
| 聯合體大小誤判 | 緩衝區分配不足 | 用 sizeof(UnionType) 而非手算 |
// 虛函數表模式
typedef struct SaleVTable {
void (*print)(const void *self);
void (*calculate_tax)(const void *self, float *tax);
void (*destroy)(void *self);
} SaleVTable;
typedef struct SaleBase {
const SaleVTable *vtable;
// 公共字段...
} SaleBase;
// 各具體類型實現 vtable 並鑄造
void cash_print(const void *self) { ... }
const SaleVTable cash_vtable = {cash_print, cash_tax, cash_destroy};
// 使用
SaleBase *sale = create_cash_sale(...);
sale->vtable->print(sale);
結構體組合與聯合體互斥共享,構成了 C 語言「組合優於繼承」數據建模的雙支柱。電話簿展示「遞歸組合」建模層次結構;銷售記錄展示「標籤+聯合」實現類型安全的變體記錄。掌握這兩種模式,即可在無類、無繼承的 C 語言中構建任意複雜的領域模型。