| 檔案版本 | 更新時間 | 更新內容 |
|---|---|---|
| v1.0 | 2020-09-14 | 初稿完成 |
文章目錄
- 一、typedef關鍵詞
- 二、結構體(重點)
- 1. 為什么需要結構體
- 2. 什么是結構體
- 3. 如何定義結構體
- 4. 如何使用結構體
- 4.1. 賦值和初始化
- 4.2. 訪問每個成員
- 4.3. 結構體變數的運算
- 4.4. 結構體作為函式傳遞問題
- 5. 結構體記憶體對齊問題(面試常考)
- 5.1. 問題描述
- 5.2. 問題原因
- 5.3. 問題分析
- 6. 結構體陣列
- 6.1. 靜態陣列
- 6.2. 動態陣列
- 三、列舉體
- 1. 什么是列舉體
- 2. 如何定義列舉體
- 3. 如何使用列舉體
- 四. 共用體
- 1. 什么叫做共用體
- 2. 為什么需要共用體
- 3. 如何定義共用體
- 4. 如何使用共用體
一、typedef關鍵詞
typedef用來給資料型別起別名,用法如下:
typedef <已有資料型別> <新名稱>;
比如:
typedef unsingned char uint8_t;
typedef unsingned int uint16_t;
二、結構體(重點)
1. 為什么需要結構體
為了表示一些復雜的事物,普通資料型別無法滿足要求,
2. 什么是結構體
把一些基本資料型別組合在一起而形成的一個新的資料型別,叫做結構體,
3. 如何定義結構體
定義結構體有四種方法:
① 只定義資料型別,不定義變數:
struct student_st {
char name[20]; /* 學生名稱 */
char sex; /* 學生性別 */
float score; /* 學生成績 */
};
② 定義資料型別,同時定義一個變數:
struct student_st {
char name[20]; /* 學生名稱 */
char sex; /* 學生性別 */
float score; /* 學生成績 */
} st1;
③ 定義資料型別,同時定義一個變數,但結構體型別匿名:
struct {
char name[20]; /* 學生名稱 */
char sex; /* 學生性別 */
float score; /* 學生成績 */
} st1;
④ 定義資料型別,不定義變數,同時為新的型別起個別名(多用):
typedef struct student_st {
char name[20]; /* 學生名稱 */
char sex; /* 學生性別 */
float score; /* 學生成績 */
} student_t;
4. 如何使用結構體
4.1. 賦值和初始化
結構體變數只能在定義的同時賦初值:
student_t st1 = {"mculover666", 'M', 66.6};
一旦定義完成,將無法整體賦值,只能單個賦值,
4.2. 訪問每個成員
訪問結構體中的成員有兩種方法:
- 通過結構體變數訪問其中的成員;
- 通過指向結構體變數的指標訪問其中的成員;
① 通過結構體變數訪問:
printf("name:%s sex:%c score:%.1f\r\n", st1.name, st1.sex, st1.score);
② 通過指向結構體變數的指標訪問:
student_t* st_ptr = &st1;
printf("name:%s sex:%c score:%.1f\r\n", st_ptr->name, st_ptr->sex, st_ptr->score);
4.3. 結構體變數的運算
結構體變數之間不能相加、相減、也不能相互乘除,但是結構體變數之間可以相互賦值,
student_t st1 = {"mculover666", 'M', 66.6};
student_t st2;
st2 = st1;
printf("name:%s sex:%c score:%.1f\r\n", st2.name, st2.sex, st2.score);
4.4. 結構體作為函式傳遞問題
如果要將結構體作為函式引數傳遞,兩種方法如下:
- 直接傳遞結構體變數:堆疊的開銷大,而且函式中無法做到對結構體的修改;
- 直接傳遞結構體指標:只是四個位元組(或者8個位元組)的指標,并且函式中可以對結構體修改,
5. 結構體記憶體對齊問題(面試常考)
5.1. 問題描述
執行如下代碼:
printf("%d + %d + %d = %d", sizeof(st1.name), sizeof(st1.sex), sizeof(st1.score), sizeof(st1));
運行結果為:
20 + 1 + 4 = 28
很神奇,結構體不應該占用25個位元組嗎?為什么會是28個位元組?
將st1每個成員的地址列印出來便知:
printf("st1: %p\nst1.name: %p\nst1.sex: %p\nst1.score:%p\n", &st1, &st1.name, &st1.sex, &st1.score);
列印結果為:
st1: 000000000061FDF0
st1.name: 000000000061FDF0
st1.sex: 000000000061FE04
st1.score:000000000061FE08
不難發現,sex變數是char型別,本來應該占用 1 個位元組,實際卻占用了 4 個位元組!
5.2. 問題原因
編譯器給結構體中的變數分配空間時有記憶體對齊規則:
① 結構體變數的起始地址能夠被其最寬的成員大小整除;
② 結構體每個成員相對于起始地址的偏移,能夠被其自身大小整除,如果不能,則在前一個成員后面補充空白位元組;
③ 結構體總體大小能夠被最寬的成員的大小整除,否則將在后面補充空白位元組,
5.3. 問題分析
第2個成員sex是char型別,只占用一個位元組,所以當后面的成員score定義時,其自身大小是4,結果發現000000000061FE04+1不能滿足對齊規則,所以補充空白位元組,使前面有20+4=24個位元組才行,地址變為000000000061FE08,
如果我們手動補充上這些空白位元組,則整個結構體大小還會是28個位元組,
typedef struct student_st {
char name[20]; /* 學生名稱 */
char sex; /* 學生性別 */
char reserve_bytes[3]; /* 保留位元組 */
float score; /* 學生成績 */
} student_t;
6. 結構體陣列
6.1. 靜態陣列
student_t st[100];
6.2. 動態陣列
#define STUDENT_NUM 100
/* 申請動態記憶體 */
student_t* st_ptr = (student_t *)malloc(STUDENT_NUM * sizeof(student_t));
/* 使用... */
/* 使用完畢之后釋放 */
free(st_ptr);
st_ptr = NULL;
三、列舉體
1. 什么是列舉體
列舉體是將一個型別的變數所有可能的值都羅列出來,并且此型別變數不能賦其它值,
2. 如何定義列舉體
上述結構體中有一個成員是sex(性別),適合使用列舉體,
定義列舉體方法有二,
① 只定義列舉型別:
enum student_sex_en {
MALE = 'M',
FAMALE = 'F',
};
② 定義列舉型別的同時,起個新名字,方便使用:
typedef enum student_sex_en {
MALE = 'M',
FAMALE = 'F',
} student_sex_t;
3. 如何使用列舉體
首先優化之前我們定義的結構體:
typedef struct student_st {
char name[20]; /* 學生名稱 */
student_sex_t sex; /* 學生性別 */
char reserve_bytes[3]; /* 保留位元組 */
float score; /* 學生成績 */
} student_t;
接著在給sex變數賦值的時候使用:
student_t st1 = {"mculover666", MALE, 66.6};
四. 共用體
1. 什么叫做共用體
共用體就是多個變數共用同一段記憶體空間,共用體的大小是最大變數占用的記憶體空間,
2. 為什么需要共用體
最典型的一個用法就是:結構體無差異化遍歷,
比如現在有一個結構體:
typedef struct num_st {
int a;
int b;
int c;
int d;
} num_t;
使用情況如下:
- 賦值時要求能在一個回圈中遍歷賦值,忽略成員的名字不同,這就叫做無差異化遍歷;
- 使用時要求能按照成員名字訪問;
顯然,第一個需求適合使用陣列,第二個需求適合使用結構體,所以就使用共用體:讓陣列和結構體共用同一個記憶體空間,
3. 如何定義共用體
按照上述需求定義共用體,并起個新名字,這個共用體占用的空間為4個int的大小:
typedef union num_un {
num_t num;
int n[4];
} num_u;
4. 如何使用共用體
在賦值時按照陣列來用,直接回圈遍歷,滿足第一個需求:
num_u num1;
for (int i = 0; i < 4; i++) {
num1.n[i] = i;
}
在訪問資料時按照結構體來用,按照名字取值,滿足第二個需求:
printf("a = %d, b = %d, c = %d, d = %d\r\n", num1.num.a, num1.num.b, num1.num.c, num1.num.d);
最終運行結果為:
a = 0, b = 1, c = 2, d = 3
CSDN認證博客專家
嵌入式軟體開發
IoT全堆疊開發
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/64788.html
標籤:其他
上一篇:用C++實作多個資料冒泡排序
