沒事看了看《C和指標》,總結一些以前沒注意到得或者疏忽掉的技術盲點。
1.關於結構體的自引用,結構自引用不能引用這個結構本身,但是可以引用結構指標。原因是編譯器在結構的長度確定之前就已經知道了指標的長度,所有這種引用時合法的,比如鏈表初始化的時候,都是用的這種方法添加指標域的。
Code:
- struct self_err2{
- int a;
- struct self_err2 *b;
- int c;
- };
2.關於結構的儲存分配,編譯器是按照成員列表的順序一個接一個的給每個成員分配記憶體的,只有當儲存成員時需要滿足正確的邊界對齊要求時,成員之間才可能出現用於填充的額外記憶體空間。
Code:
- struct ALIGN{
- char a;
- int b;
- char c;
- };
上面的代碼為了滿足邊界對齊,成員a必須儲存一個能夠被4整除的地址,所有這個結構需要12個位元組的記憶體空間。但是重新排列以後,讓那些對邊界要求最嚴格的成員首先出現,對邊界要求最弱的成員最後出現。考慮以下結構
Code:
- struct ALIGN2{
- int b;
- char a;
- char c;
- };
這個結構只佔8個位元組。
3.作為函數參數的結構,這個地方只需要記住一點,如果函數對這個指標的間接訪問次數超過2-3次,那麼使用這個方法所節省的時間將遠高於一條額外指令的時間。基本上,除非結構特別小的時候,給一個函數傳遞結構指標的效率遠大於給函數傳遞一個結構。
4.位段:這個以前幾乎沒有接觸過(記得一次筆試遇到過,直接死掉)。位段必須聲明為int,signed int或unsinged int類型。其次,成員名後面是一個冒號和一個整數。這個整數指定該位段所佔用的位的數目。
Code:
- struct CHAR{
- unsigned ch :7;
- unsigned font :6;
- unsigned size :19
- };
- struct CHAR ch1;
使用位段的好處就是可以把奇數位元組的資料包封裝到一起,這樣可以節省記憶體空間。
5.關於malloc:這個函數和free都是維護一個可用的記憶體池,malloc從線程池取出一個連續的記憶體塊,返回一個未初始化的指標。因此每個malloc返回的指標都要檢查是不是NULL以確定是不是可用記憶體。malloc返回的指標是一個void*類型的。calloc和malloc區別是calloc分配一個初始化為0的指標。realloc用於修改一個原先已經分配的記憶體塊的大小。所以使用realloc以後原先的指標作廢,使用新的指標操作這個記憶體。
Code:
- /* 定義一個不易發生錯誤的分配器 */
- #include <stdlib.h>
-
- #define malloc
- #define MALLOC(num,type)) (type *)alloc((num)*sizeof(type))
- extern void *alloc(size_t size);
Code:
- /* 不易發生記憶體 Clerk的錯誤的實現 */
- #include <stdio.h>
- #include "alloc.h"
- #undef malloc
-
- void *alloc(size_t size)
- {
- void *new_mem;
- new_mem = malloc ( size );
- if( new_mem == NULL){
- printf("out of memory!/n");
- exit(1);
- }
- return new_mem;
- }
Code:
- /* 一個使用很少引用錯誤的記憶體 Clerk的程式 */
- #include "alloc.h"
- void function()
- {
- int *new_memory;
- /* 獲得一串整形數的空間*/
- new_memory = MALLOC(25,int);
- /* ……………………*/
- }
6.常見的動態記憶體分配的錯誤:對NULL進行解引用操作,記憶體進行操作的越界,釋放並非動態分配記憶體,試圖釋放一塊動態分配的記憶體的一部分以及一塊動態記憶體被釋放之後被繼續使用。
7.記憶體泄露:這個問題倒是經常被提到,malloc後不釋放,記憶體不斷地被申請出來,導致記憶體最終被耗盡……
PS:以下這個分配的方法具有很好的可移植性。
Code:
- pi = malloc ( 25* sizeof(int))