C語言學(xué)習(xí)4-結(jié)構(gòu)體

一、結(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ī)則

  1. 每個(gè)成員的起始地址必須是其類型大小的整數(shù)倍
  2. 結(jié)構(gòu)體總大小是其最大成員大小的整數(shù)倍
  3. 編譯器會(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)體使用原則

  1. 明確用途:為相關(guān)數(shù)據(jù)創(chuàng)建結(jié)構(gòu)體
  2. 合理命名:使用有意義的類型名和成員名
  3. 頭文件定義:在頭文件中定義可重用的結(jié)構(gòu)體
  4. 傳址優(yōu)先:函數(shù)參數(shù)使用指針傳遞結(jié)構(gòu)體
  5. 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)化的代碼!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容