一、結(jié)構(gòu)體基本概念
1.1 什么是結(jié)構(gòu)體?
結(jié)構(gòu)體是一種自定義數(shù)據(jù)類型,允許將不同類型的數(shù)據(jù)組合在一起。
1.2 結(jié)構(gòu)體定義語法
struct TypeName {
// 成員變量列表
};
示例:聯(lián)系人結(jié)構(gòu)體
struct Contact {
int id;
char name[16];
char phone[16];
};
二、結(jié)構(gòu)體的使用
2.1 結(jié)構(gòu)體變量定義
// 1. 定義結(jié)構(gòu)體變量
struct Contact c; // C語言寫法
Contact c; // C++寫法(推薦)
// 2. 定義結(jié)構(gòu)體數(shù)組
Contact contacts[4];
// 3. 定義結(jié)構(gòu)體指針
Contact *pc = &c;
2.2 結(jié)構(gòu)體在函數(shù)中的使用
// 作為函數(shù)參數(shù)
void print_contact(Contact c);
void modify_contact(Contact *c);
// 作為返回值類型
Contact create_contact();
三、結(jié)構(gòu)體初始化
3.1 多種初始化方式
#include <stdio.h>
#include <string.h>
struct Contact {
int id;
char name[16];
char phone[16];
};
int main() {
// 方式1:先定義后賦值
Contact c1;
c1.id = 201501;
strcpy(c1.name, "John");
strcpy(c1.phone, "15913245635");
// 方式2:定義時(shí)完全初始化
Contact c2 = {
201502,
"Jennifer",
"13810022334"
};
// 方式3:部分初始化(剩余成員自動(dòng)清零)
Contact c3 = {
201503,
"Alice"
// phone 自動(dòng)初始化為空字符串
};
// 方式4:清零初始化
Contact c4 = {0}; // 所有成員設(shè)為0或空
return 0;
}
3.2 結(jié)構(gòu)體賦值
Contact a = {20141003, "John", "15913245635"};
Contact b = a; // 結(jié)構(gòu)體支持直接賦值(成員逐個(gè)拷貝)
四、嵌套結(jié)構(gòu)體
4.1 結(jié)構(gòu)體包含結(jié)構(gòu)體
struct Score {
float chinese;
float english;
float math;
};
struct Student {
int id;
char name[16];
struct Score score; // 嵌套結(jié)構(gòu)體
};
int main() {
Student s;
s.id = 1001;
strcpy(s.name, "Tom");
s.score.chinese = 88.5; // 訪問嵌套成員
s.score.english = 92.0;
s.score.math = 95.5;
return 0;
}
4.2 結(jié)構(gòu)體包含結(jié)構(gòu)體指針
struct Student {
int id;
char name[16];
Score *pscore; // 指向Score的指針
};
int main() {
Score student_score = {88.0, 90.0, 98.0};
Student student;
student.pscore = &student_score;
printf("數(shù)學(xué)成績(jī): %.1f\n", student.pscore->math);
return 0;
}
注意:結(jié)構(gòu)體定義需要先定義后使用
五、結(jié)構(gòu)體數(shù)組
5.1 定義和初始化結(jié)構(gòu)體數(shù)組
struct Contact contacts[4] = {
{201501, "John", "18601011223"},
{201502, "Jennifer", "13810022334"},
{201503, "AnXi", "18600100100"},
{201504, "Unnamed", "18601011223"}
};
// 訪問數(shù)組元素
contacts[0].id = 201505;
strcpy(contacts[1].name, "Mike");
5.2 遍歷結(jié)構(gòu)體數(shù)組
void print_contacts(const Contact contacts[], int count) {
for (int i = 0; i < count; i++) {
printf("ID: %d, Name: %s, Phone: %s\n",
contacts[i].id, contacts[i].name, contacts[i].phone);
}
}
六、結(jié)構(gòu)體指針
6.1 結(jié)構(gòu)體指針的使用
Contact person = {20141003, "John", "15913245635"};
Contact *p = &person;
// 使用箭頭運(yùn)算符訪問成員
printf("ID: %d\n", p->id);
printf("Name: %s\n", p->name);
// 修改成員
p->id = 20141004;
strcpy(p->name, "David");
6.2 等價(jià)訪問方式
p->id // 推薦:簡(jiǎn)潔清晰
(*p).id // 等價(jià),但不常用
七、結(jié)構(gòu)體作為函數(shù)參數(shù)
7.1 傳值 vs 傳地址
#include <stdio.h>
// 方式1:傳值(不推薦 - 效率低)
void print_contact_by_value(Contact c) {
printf("ID: %d, Name: %s\n", c.id, c.name);
}
// 方式2:傳地址(推薦 - 高效)
void print_contact_by_pointer(const Contact *p) {
printf("ID: %d, Name: %s\n", p->id, p->name);
}
// 方式3:修改結(jié)構(gòu)體內(nèi)容
void update_contact(Contact *p, int new_id, const char *new_name) {
p->id = new_id;
strcpy(p->name, new_name);
}
int main() {
Contact person = {1001, "Alice", "123456789"};
print_contact_by_value(person); // 傳值:數(shù)據(jù)拷貝
print_contact_by_pointer(&person); // 傳地址:無拷貝
update_contact(&person, 1002, "Bob"); // 修改原結(jié)構(gòu)體
return 0;
}
7.2 為什么推薦傳地址?
- 空間效率:避免復(fù)制整個(gè)結(jié)構(gòu)體
- 時(shí)間效率:避免拷貝大量數(shù)據(jù)
- 修改能力:可以直接修改原結(jié)構(gòu)體
八、結(jié)構(gòu)體作為函數(shù)返回值
8.1 返回結(jié)構(gòu)體
// 方式1:返回結(jié)構(gòu)體(可能產(chǎn)生拷貝)
Contact create_contact(int id, const char *name, const char *phone) {
Contact c;
c.id = id;
strcpy(c.name, name);
strcpy(c.phone, phone);
return c;
}
// 方式2:通過指針參數(shù)返回(推薦)
void create_contact2(int id, const char *name, const char *phone, Contact *result) {
result->id = id;
strcpy(result->name, name);
strcpy(result->phone, phone);
}
int main() {
// 方式1使用
Contact c1 = create_contact(1001, "Tom", "111111111");
// 方式2使用
Contact c2;
create_contact2(1002, "Jerry", "222222222", &c2);
return 0;
}
九、匿名結(jié)構(gòu)體
9.1 匿名結(jié)構(gòu)體的使用
// 匿名結(jié)構(gòu)體:只定義變量,不定義類型名
struct {
char guid[128];
int user_id;
} global_info; // 直接定義變量global_info
int main() {
global_info.user_id = 8780087;
strcpy(global_info.guid, "{cfff140d5-af72-44ba-a763-c861304b46f8}");
return 0;
}
注意:匿名結(jié)構(gòu)體無法重用,通常用于一次性場(chǎng)景
十、結(jié)構(gòu)體編程規(guī)范
10.1 正確的結(jié)構(gòu)體定義位置
// ? 推薦:在頭文件中定義
// contact.h
#ifndef CONTACT_H
#define CONTACT_H
struct Contact {
int id;
char name[16];
char phone[16];
};
#endif
10.2 避免的錯(cuò)誤寫法
// ? 不推薦:在函數(shù)內(nèi)部定義結(jié)構(gòu)體
int main() {
struct Contact { // 作用域僅限于本函數(shù)
int id;
char name[16];
};
return 0;
}
// ? 不推薦:混合定義變量
struct Contact {
int id;
char name[16];
} a, b; // 同時(shí)定義變量a,b - 不清晰
十一、結(jié)構(gòu)體內(nèi)存對(duì)齊
11.1 什么是內(nèi)存對(duì)齊?
struct Example {
char a; // 1字節(jié)
int b; // 4字節(jié)
};
printf("結(jié)構(gòu)體大小: %zu\n", sizeof(struct Example)); // 輸出8,不是5!
11.2 內(nèi)存布局說明
內(nèi)存地址: 0 1 2 3 4 5 6 7
成員: [a] [填充] [ b ]
說明: char占1字節(jié),但后面3字節(jié)被填充以滿足對(duì)齊
11.3 對(duì)齊規(guī)則
- 每個(gè)成員的起始地址必須是其類型大小的整數(shù)倍
- 結(jié)構(gòu)體總大小是其最大成員大小的整數(shù)倍
- 編譯器會(huì)自動(dòng)插入填充字節(jié)保證對(duì)齊
11.4 為什么要內(nèi)存對(duì)齊?
- 性能優(yōu)化:CPU訪問對(duì)齊的數(shù)據(jù)更快
- 硬件要求:某些CPU只能訪問對(duì)齊的地址
- 跨平臺(tái)兼容:保證在不同系統(tǒng)上行為一致
十二、最佳實(shí)踐總結(jié)
12.1 結(jié)構(gòu)體使用原則
- 明確用途:為相關(guān)數(shù)據(jù)創(chuàng)建結(jié)構(gòu)體
- 合理命名:使用有意義的類型名和成員名
- 頭文件定義:在頭文件中定義可重用的結(jié)構(gòu)體
- 傳址優(yōu)先:函數(shù)參數(shù)使用指針傳遞結(jié)構(gòu)體
- const保護(hù):只讀參數(shù)加上const修飾
12.2 性能優(yōu)化建議
// ? 高效寫法
void process_data(const DataStruct *input, DataStruct *output) {
// 使用指針,避免拷貝
}
// ? 低效寫法
void process_data(DataStruct input) {
// 傳值,產(chǎn)生數(shù)據(jù)拷貝
}
12.3 代碼示例模板
// contact.h
#ifndef CONTACT_H
#define CONTACT_H
#define NAME_LENGTH 16
#define PHONE_LENGTH 16
typedef struct {
int id;
char name[NAME_LENGTH];
char phone[PHONE_LENGTH];
} Contact;
// 函數(shù)聲明
void contact_init(Contact *contact, int id, const char *name, const char *phone);
void contact_print(const Contact *contact);
int contact_compare(const Contact *a, const Contact *b);
#endif
掌握結(jié)構(gòu)體是C語言編程的重要里程碑,它讓你能夠組織復(fù)雜數(shù)據(jù),編寫更結(jié)構(gòu)化的代碼!