課程內容#
結構體#
- 關鍵字:struct
- 聲明結構體變量時,需要加上 struct,而不能只用結構體名字
- struct person Yu;
- 聲明結構體變量時,需要加上 struct,而不能只用結構體名字
- 赋值、访问:
- '.' 直接引用 —— '.' 前面是結構體變量
- '->' 間接引用 ——'->' 前面是指向結構體變量的指針
- 舉例:a. 自己回家、b. 告訴朋友自己家地址,來我家
- 聲明結構體變量時的佔用空間
- 定義結構體不佔用空間
- 各變量的累加之和
- 但要考慮對齊原則
- ① 成員所佔字節數的最大值的整數倍
- ② 成員相對於結構體起始地址的偏移能夠被其自身大小整除
- 如果不能,則在前一個成員後面補充字節
- 見下面示例:
-
-
- 變量 a、b、c 並不能在一起,b 的偏移需要是 4 的倍數,所以 b 的偏移為 4,c 的偏移為 8 了
- 參考結構體對齊詳解——cnblogs
- 省空間做法:將同一類型的變量放在一起定義
- 可以使用預定義的宏強行更改對齊的字節數
- 參考#pragma pack () 用法- 簡書
- 匿名結構體:struct {...;} var;
- 只在初始化的時候使用一次
- var 就是聲明的結構體變量了。定義結構體同時聲明變量
共用體(聯合體)#
-
關鍵字:union
- 共用一片空間
- 空間大小按共用體內佔用內存最大的字段來劃分
-
unsigned char 的意義
-
-
首先我們通常意義上理解,byte 沒有什麼符號位之說,更重要的是如果將 byte 的值賦給 int,long 等(而不是)數據類型時,系統會做一些額外的工作
-
如果是char,那麼系統認為最高位是符號位,而 int 可能是 16 或者 32 位,那麼會對最高位進行擴展,賦給 unsigned int 也一樣
-
而如果是 unsigned char,那麼不會擴展
-
-
內存佔用結構圖
-
⭐指針與地址#
-
學會對數據的本源去操作
-
取地址符:&
- 取的是首地址:第 0 個字節的地址
-
每一個字節都會對應一個地址標號
-
64 位 / 32 位:地址的尋址範圍
- 32 位最多匹配 4GB 的內存條
- 2^30 Byte = 1 GB 👉 32 位最多支持 4 GB
- 32 位最多匹配 4GB 的內存條
-
指針變量
- 用來存儲地址,地址一般表現為 16 進制
- 不管指針什麼類型(int、char),它們的地位一樣,都是存儲地址
- 佔用空間大小:64 位 - 8 字節;32 位 - 4 字節(64 位代表地址的位數是 64)
- int *p 可以傳給 char *q 嗎?完全可以,都是佔用 8 個字節(64 位)
- 區分類型的意義見下:等價形式轉換
- int *p;
- 定義的時候 * 用來表示 p 是指針變量
- 定義完後,p 代表指針,*p 的 * 代表取值操作
- 指針變量也是變量!
- 可以用指針 Q 存儲指針 p 的地址
-
scanf 函數說明
- 要修改變量的值,所以必須用地址傳值
- 傳地址的參數,可以叫傳出參數,因為操作也會在函數外體現
- 而函數的值傳遞,其操作只在作用域裡生效
- 要修改變量的值,所以必須用地址傳值
-
等價形式轉換
-
- 取值操作
- +1 的 1 的偏移量取決於p 表示的類型的字節數(區分類型的意義)
- 前提:指針 p 指向結構體變量 a
函數指針#
-
變量:為了避免歧義,函數變量聲明應該是這樣
-
- 將 * add(add 為函數名)括起來
-
-
類型:typedef 可以將變量提升為類型
-
- 可以用類型定義變量
-
-
typedef 的兩種用法
- 內建類型、結構體類型的重命名
- typedef long long lint;
- typedef char * pcahr; // char * → pchar
- 有點像 #define,但還是有微妙的區別,詳見代碼演示
- typedef struct __node { int x,y;} Node, *PNode;
- 同時定義了兩個別名
- PNode p; // 可以定義 Node 結構體類型的指針變量
- 提升為類型
- typedef int (*func)(int);
- 可以定義無數多個函數指針變量
- typedef int (*func)(int);
- 內建類型、結構體類型的重命名
-
main 函數參數
-
-
帶參數的話,傳參數的是誰呢?操作系統
-
return 0; // 返回給系統
-
"echo $?" 可以用來判斷系統上次命令運行是否成功
- 0:成功
- 非 0:不成功
-
參數解讀
- argc:參數個數
- argv:對應二維數組,接收的是 argc 個字符串,一維數組接收的是一行字符串
- *arr [] 對應 arr [][]
- env:環境變量
- env 是指向 char * 的指針
- env 是一個地址,指向一個字符數組的地址
- env 這個地址還能往後走,所以實際對應一個二維字符數組
- ❓**env 與 * env [] 的定義方式有什麼不同?在這裡是沒區別的!
- env 是指向 char * 的指針
- ❓❗不能用 int ** 表示二維數組
- 需要用 malloc 定義?
- 因為靜態數組的話好像無法保證連續性
- 這裡需要去網上細品!
- 參考為什麼不能用二級指針直接指向二維數組-CSDN
-
問:澤哥,我定義一個 char str [10][10] 的二維數組,不能用 char** 接收。而 main 函數的 **env 可以接收字符串數組,我該怎麼理解,字符串數組不算二維數組?
答:後者是通過 malloc 出來的,是動態數組
隨堂練習#
一:結構體字節對齊#
-
-
定義順序不一樣
-
左:8 字節;右:12 字節
二:共用體內存圖#
👇
三:ip 轉整數#
-
-
192.168.1.2
- 每一段,最高 255,即可以用 1 字節來表示,對應 unsigned char
- 總共就是 4 字節,對應 int 類型
-
代碼
-
* union中struct必須聲明變量 * 可以不用sscanf?如果不用,則不方便讀取xxx.xxx.xxx.xxx中每一段的值 * 大端、小端機 * 小→數字低位→低地址 * 大→數字低位→高地址
ip(192.168.0.1)按每段順序存儲在小端機裡 | ||||
---|---|---|---|---|
int 數字字節地址編號 | 0 | 1 | 2 | 3 |
ip 存儲 | 1 | 0 | 168 | 192 |
-
-
- 一般的電腦都是小端機
- 判斷是否是小端機:定義 int 類型 num = 1,判斷其最高位的值
- int 類型地址強轉為 char 類型地址,則可以取第 0 號字節地址上的值
- 直接讀數字會按照本機字節序從高 / 低位開始讀是嗎?是的
- 本端、對端字節序不匹配怎麼辦
- 過程:本機字節序→網絡字節序(統一標準)→對端字節序
- 只有讀取的時候,才必須區分字節序,其他情況都不用考慮
- 如果沒有細化到字節,應該是體會不到字節序的
- 參考理解字節序- 阮一峰
- 輸出
-
-
附加:用盡可能多的形式表示 a [1].x#
【至少有 30 種,利用前面提到的等價形式、套娃!】
-
-
代碼
-
亮點筆記#
代碼演示#
代碼#
演示結構偏移量計算、宏定義類型和 typedef 區別、主函數參數詳解#
-
-
-
字符串要以 '\0' 結尾,才方便結束讀取的判斷(num 的低位為 0 即可)
-
地址對應的是 long int 類型(64 位 - 8 字節,32 位 - 4 字節)
-
被註釋掉的 offset () 宏定義有什麼不足?
- 還需要定義變量,會佔用空間
- 所以可以借用空地址(0 或 NULL)強轉
-
定義類型別名時,宏定義只是簡單替換,沒有 tpydef 省心
-
main 函數輸入參數
- 根據空格劃分,可以用雙引號整合多個字符串
-
程序輸出
-
附加知識點#
- 在 C++ 的面向對象中,結構體是一個 class
- 引用效率比指針高,指針多了傳遞
- 如何根據一個已知的內存地址,讀取值
- 將地址傳給一個指針,再用取值符 *
- 語法糖
- 指計算機語言中添加的某種語法,對語言的功能沒有影響,但是更方便程序員使用
- 讓程序更加簡潔,有更高的可讀性
思考點#
Tips#
-
重點在於指針與地址的應用關係
-
記筆記,很重要
- typora 記錄
-
簡歷
- 可以用ShareLaTeX做
- 計算機方向的簡歷:黑白,注意縮進,感覺踏實
-
參考工具書第 9、10 章