C語言 -- sizeof總結
sizeof的語法
1:sizeof是C語言的關鍵字,用來計算變量、常量或數據類型在當前系統中占用內存的字節數。?
2:sizeof不是函數,產生這樣的疑問是因為sizeof的書寫確實有點像函數。 在程序中,sizeof有兩種寫法:
1)用于數據類型:sizeof(數據類型)
2)用于變量名:sizeof(變量名);sizeof變置名 //不建議這樣使用
代碼示例:
#include<stdio.h>
int main() {
int i;
char str[20];
printf("sizeof(i) is %d\n", sizeof(i));
printf("sizeof i is %d\n", sizeof i);
printf("sizeof int is %d\n", sizeof(int));
//printf("sizeof int is %d\n", sizeof int);錯誤寫法
printf("sizeof(str) is %d\n", sizeof(str));
printf("sizeof str is %d\n", sizeof str);
printf("sizeof str is %d\n", sizeof (char[20]));
//printf("sizeof str is %d\n", sizeof char[20]);錯誤寫法
printf("sizeof(365) is %d\n", sizeof(365));
printf("sizeof(i love you) is %d\n", sizeof("i love you"));
}
結果:

注:最后一個字符串之所以是11,是因為在C語言中字符串后邊會默認添加一個結束符,也就是'\0'
sizeof與strlen
sizeof是運算符,用來計算變量、常量或數據類型在當前系統中占用內存的字節數
strlen是函數,用于計算字符串的實際長度
代碼示例:
char ch[20];
strcpy(ch, "i love you");
printf("sizeof(ch) is %d\n", sizeof(ch));//地址長度20,不變
printf("strlen(ch) is %d\n", strlen(ch));//字符串的實際長度,也就是i love you的長度,為10
結構體內存對齊
首先看一個例子:
struct S
{
char a;
int b;
char c;
};
我們先來計算一下這個結構體的大小,如果不存在內存對齊這個問題,按理說這個結構體應該占(1+4+1)6個字節;然而事實上它占了12個字節,為什么?我們需要解決下面幾個問題。
1.為什么存在內存對齊
- 平臺原因(移植問題):一些資料上是這樣說的,“不是所有的硬件平臺都能訪問任意地址上的任意數據;某些硬件平臺只能在某些特定地址處取某些特定的數據,否則就會拋出硬件異常”。也就是說在計算機在內存讀取數據時,只能在規定的地址處讀數據,而不是內存中任意地址都是可以讀取的。
- 性能問題:正是由于只能在特定的地址處讀取數據,所以在訪問一些數據時,對于訪問未對齊的內存,處理器需要進行兩次訪問;而對于對齊的內存,只需要訪問一次就可以。

看上面的圖,因為規定只能在特定地址處讀數據,所以我們假設只能在4的倍數的地址處讀數據,那么可以訪問的地址分別為0、4、8、12、16等,這時我們給出剛才的結構體,在處理器訪問第一個結構體元素(char) a 時,它會從0號下標的地址處訪問a,而下個結構體元素 b 為int型,它占了4個字節,很顯然,要訪問它時需要先從0號下標的地址開始訪問,之后還得取它后面3個字節的內容作為一部分,之后再從4號下標的地址處訪問1個字節(因為第一個字節被第一個元素占用了),共同的內容組成了元素 b ,很明顯,訪問 b 時要進行兩次訪問,并且很麻煩。這時如果存放數據時內存對齊了,則只需訪問一次就可以讀取到 b 的內容。第二幅圖就是內存對齊的情況,在訪問每個數據時,都可以一次性訪問完畢。
2.內存對齊后,怎么計算結構體的大小
1. 第一個成員在與結構體變量偏移量為0的地址處。
2. 其他成員變量要對齊到某個數字(對齊數)的整數倍的地址處。對齊數=編譯器默認的一個對齊數與該成員大小的較小值,在VS環境下默認值為8,在Linux環境下默認值為4。
3. 結構體的總大小為最大對齊數(每個成員變量都有一個對齊數)的整數倍。
4. 如果嵌套了結構體的情況,被嵌套的結構體對齊到其自身對齊數的整數倍處(結構體的對齊數就是其內部成員中最大的對齊數),此時結構體的整體大小就是所有最大對齊數(含被嵌套結構體的對齊數)的整數倍。
此時再看上邊的圖:
利用這四個規則就可以計算結構體的大小。這時參照上面的圖中第二種情況來計算,對于上面的結構體,先定義了char類型的 a ,規則1說第一個元素在偏移量為0的地址處,即它對齊到0號地址處的位置,其占一個字節;第二個元素要對齊到自己對齊數的整數倍處,因為第二個元素的大小為int型,占4個字節,而VS默認對齊數是8,取最小的數,所以 b 的對齊數為4,規則2說要對其自身對齊數(4)的整數倍,而此時 a 占了一個字節,下個地址依次為2、 3 、4等,當走到4號地址處時,此時4能被4整除,所以第二個元素對齊在4號下標的地址處,占用4個字;接下來,第三個元素為char類型的 c ,c的大小為1,和8比較取小的數,所以 c 的對齊數為1,而此時 b 存放完之后的下個地址為9號地址,根據規則2,9是1的整數倍,所以 c 直接存放在9號開頭的地址處。接下來,計算它們的大小,分別為1(char)+3(偏移量)+4(int)+1(char)=9,而正確的結果為12,原因在于規則3,規則3規定結構體的總大小為最大對齊數的整數倍,上面的3個元素中,它們的對齊數分別為0、4、1。所以最大對齊數為4,而結構體大小要整除最大對齊數,剛才計算出結構體的大小為9,很明顯不能整除,所以結構體的大小為1(char)+3(偏移量)+4(int)+1(char)+3(偏移量)=12。
再來看第二個例子:
struct S1
{
char c1;
char c2;
int i;
};
struct S2
{
char c1;
struct S1 s3;
double d;
};
首先計算出結構體S1的大小為1(char)+1(char)+2(偏移量)+4(int)=8,S1的對齊數就是其結構體成員中最大的對齊數,即4,在結構體S2中,對齊數分別是0、4、8;首先 c1 對齊在0號地址的位置,其占用1個字節,根據規則4,嵌套的結構體S1要對齊到自身對齊數4的整數倍處,所以此時結構體對其在4號地址處,其大小是8個字節,之后,d 的對齊數為8,所以它要對齊到16號地址處,所以此時計算出S2的大小是1(char)+3(偏移量)+8(struct S1)+4(偏移量)+8(double)=24,結構體S2中個成員的對齊數分別為0、4、8,因為24是8的倍數,符合規則4。所以S2的總大小就是24個字節。
#pragma pack(n) 表示設置為n字節對齊。
sizeof易錯點
先看一段代碼:
#include<stdio.h>
#include<string.h>
struct student {
char name[20];
int age;
int ID;
char add[300];
};
int main() {
char str[21];
struct student stu;
memset(str, 0, sizeof(str));
memset(&stu, 0, sizeof(stu));
}
上邊代碼主要就是對一個字符數組和一個結構體進行初始化,但是有時候我們可能需要將初始化那兩行代碼給封裝起來,如下:
struct student {
char name[20];
int age;
int ID;
char add[300];
};
//初始化數據
void InitData(char *str,struct student *stu) {
memset(str, 0, sizeof(str));
memset(&stu, 0, sizeof(stu));
}
int main() {
char str[21];
struct student stu;
InitData(str, &stu);
}
但是這樣是有問題的
我們可以走一個測試,在main函數里輸出字符串和結構體的長度和在初始化函數里輸出字符串和結構體的長度,如下:
struct student {
char name[20];
int age;
int ID;
char add[300];
};
//初始化數據
void InitData(char *str,struct student *stu) {
//memset(str, 0, sizeof(str));
//memset(&stu, 0, sizeof(stu));
printf("sizeof(str) is %d\n", sizeof(str));
printf("sizeof(stu) is %d\n", sizeof(stu));
}
int main() {
char str[21];
struct student stu;
printf("sizeof(str) is %d\n", sizeof(str));
printf("sizeof(stu) is %d\n", sizeof(stu));
printf("\n");
InitData(str, &stu);
}
輸出:

可以看到,在main函數里輸出的分別是21和328,也就是字符串str和結構體student的長度,但是在初始化函數里輸出的都是8,這是為什么呢?
我們可以回到sizeof的定義,它是用來計算變量、常量或數據類型在當前系統中占用內存的字節數,在main函數里計算的就是字符串類型的結構類型的字節數,而在初始化函數里計算的都是指針類型所占的字節數,在64位操作系統中,指針都是占8位,所以輸出的都是8,如果出現這樣的錯誤后果會非常嚴重,可能會造成內存泄漏的問題。
那么我們怎么在初始化函數中初始化我們的成員呢?
首先,在處理字符串這種情況,我們可以在main函數中將字符串的長度傳進去,如果只傳一個字符串的地址,那么子函數不知道該字符串的長度,所以就無法完成初始化
那么對于結構體如何處理的呢?這里可以養成一個良好習慣,就是對結構體進行初始化時候,直接用數據類型,不要用變量名。
代碼示例如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct student {
char name[20];
int age;
int ID;
char add[300];
};
void InitData(char *pstr, int length, struct student *stu);
int main() {
char str[21];
struct student stu;
printf("sizeof(str) is %d\n", sizeof(str));
printf("sizeof(stu) is %d\n", sizeof(stu));
printf("\n");
InitData(str, sizeof(str), &stu);
}
//初始化數據
void InitData(char *pstr, int length, struct student *stu) {
memset(pstr, 0, length);
memset(stu, 0, sizeof(struct student));
}
這樣就可以調用初始化函數,將主函數的字符串和結構體進行初始化。

浙公網安備 33010602011771號