主頁 > 後端開發 > struct結構體大小的計算(記憶體對齊)

struct結構體大小的計算(記憶體對齊)

2021-10-10 06:15:02 後端開發

本次實驗環境

環境1:Win10, QT 5.12

 

一. 背景

        當普通的型別無法滿足我們的需求的時候,就需要用到結構體了,結構體可衍生出結構體陣列,結構體還可以嵌套結構體,這下子資料型別就豐富多彩了,我們可以根據需要定義自己的資料型別,有時需要求結構體的大小,這就涉及到記憶體對齊的知識,概念、理論之類,我沒有深入研究,這里主要是驗證一下計算結構體大小的方法,證明學習到的方法確實有效,關于記憶體對齊,最開始是看了《深入理解計算機系統》中關于“資料對齊”一節,上面輕描淡寫的寫了下求結構體的大小,我沒看明白,看《零基礎入門C語言》中關于計算結構體大小的規則,算是看明白了,

 

二. 前奏

        先說點我覺得有意思的地方,陣列之間是不可以直接賦值的,但是用結構體包裝一下,就達到了這個效果,前者無法做到的事情卻通過結構體做到了,通過代碼來驗證一下,

定義了兩個陣列arr和arr2,第14行代碼,將arr賦值給arr2,編譯時會報錯,提示:14: error: array type 'int [5]' is not assignable,

(陣列名有二義性,一是表示陣列名,相當于陣列的定海神針,二是表示首元素的地址,第14行代碼把一個陣列的首元素的地址賦值給另一個陣列首元素,顯然這是不允許的)

 

將第14行代碼注釋后,定義了一個結構體,里面定義了一個整型陣列,然后定義了兩個結構體變數tt1和tt2,將tt2賦值給了tt1,然后列印變數tt2中陣列里面的每個元素,

 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 struct numArr
 6 {
 7     int m_arr[5];
 8 };
 9 
10 int main()
11 {
12     int arr[5] = {1, 2, 3, 4, 5};
13     int arr2[5] = {0};
14 //    arr2 = arr;
15 
16     struct numArr tt1 = {{1, 2, 3, 4, 5}};
17     struct numArr tt2 = {{0}};
18     tt2 = tt1;
19 
20     for(int i = 0; i < 5; ++i)
21     {
22         cout<<tt2.m_arr[i]<<endl;
23     }
24 
25 
26 
27     return 0;
28 }

運行結果如下

 從結果可以看到,列印結構與tt1中的陣列中的元素一致,也就是說,將結構體變數tt1賦值給tt2后,tt2中的陣列與tt1中的陣列也一樣了,

通過結構體這么一包裝,就產生了陣列"可以"賦值的現象了,挺有意思的,

 

 三. 結構體大小的計算

記憶體對齊,關于這一點,《深入理解計算機系統》這本書用的篇幅蠻少的,兩頁不到,書上是這么介紹的,

許多計算機系統對基本資料型別的合法地址做出了一些限制,要求某種型別物件的地址必須是某個值K(通常是2、4或8)的倍數,這種對齊限制簡化了形成處理器和記憶體系統之間的介面的硬體設計,

前半句能理解,后半句,涉及硬體的東西,懵逼了,

《零基礎入門C語言》這本書,從現象的角度闡述,它先講記憶體不對齊的情況,

一個成員變數需要多個機器周期去讀的現象,稱為記憶體不對齊,為什么要對齊呢?本質是犧牲空間,換取時間的方法,

一般來說,大多數系統,即使不對齊,也沒什么大的問題,只是原本需要一次進行記憶體操作讀或寫,現在需要兩次或多次了,不過這個與硬體有關系,比如有些處理器對于某些指令有特定的要求,否則可能就真的出現例外了,有興趣的話,建議自行去深入學習,

 

對齊規則/計算方法

x86(Linux默認#pragma pack(4), Window默認#pragma pack(8)),Linux最大支持4位元組對齊,

方法:

1) 取pack(n)的值 (n = 1, 2, 4,8......),取結構體中型別最大值為m,兩者取小即為外對齊大小 Y = (m < n ? m: n);

2) 將每一個結構體的成員大小與Y比較取小者為X,作為內對齊的大小;

3) 所謂按X對齊,即為地址(設起始地址為0)能被X整除的地方開始存放資料;

4) 外部對齊原則是依據Y的值(Y的最小整數倍),進行補空操作;

以上就是通常計算結構體大小的方法了,接下來,我們通過一些簡單實驗來驗證一下,

 

首先,定義一個結構體,里面包含了char、short、int型別的變數,

1) 結構體先按照char、short、int的順序定義,然后定義一個結構體變數s1,求結構體的大小,一個是結構體型別的大小(模子),一個是是結構體變數的大小,我們也可以把結構體成員的地址也列印出來,查看它們的偏移量,分析起來會更清晰一些,

 1 #include <stdio.h>
 2 
 3 typedef struct stu
 4 {
 5     char a;
 6     short b;
 7     int c;
 8 } Stu;
 9 
10 int main()
11 {
12     Stu s1 = {'m', 1, 20};
13     printf("sizeof(Stu) = %d\n", sizeof(Stu));
14     printf("sizeof(s1)  = %d\n", sizeof(s1));
15 
16     printf("-----------\n");
17     printf("&s1   = %p\n", &s1);
18     printf("&s1.a = %p\n", &s1.a);
19     printf("&s1.b = %p\n", &s1.b);
20     printf("&s1.c = %p\n", &s1.c);
21     return 0;
22 }

 a) Windows平臺,pack默認為8,先求外對齊大小Y,結構體中型別最大的為int型別,大小為4位元組,4比8小,所以Y值為4.

b) 然后將結構體中的每一個成員與Y進行比較,依次求內對齊的大小,結構體中成員分別為char 1位元組,short 2位元組,int 4位元組,與4(外對齊大小Y)比較,得到內對齊大小分別為 1, 2, 4,

c) 假設起始地址為0x00,0可以被1整除,可以存放a了,a為char型別,大小為1個位元組,接著地址為0x01,但是0x01不能被2整除,然后下一個地址為0x02,0x022可以被2整除,因此b的起始地址為0x02(此時,a與b之間填充了一個位元組),b為short型別,大小為2個位元組,接著地址到了0x04,它可以被4整除,于是可以存放c了,c為int型別,大小為4個位元組,

d) 接著地址到了0x08,(0x08-0x00)它可以被4(外對齊大小Y)整除,滿足外對齊要求,

經分析,結構體大小為1 + 1 + 2 + 4 = 8個位元組,

圖如下圖所示

 代碼運行結果如下

 從列印結果來看,結構體大小為8,與上面的分析結果一致,符合預期,

 

2) 調整結構體中成員的順序,結構體先按照short、char、int的順序定義,列印成員地址的時候也需要調整下a與b的列印順序,代碼其它部分保持不變,

1 typedef struct stu
2 {
3     short b;
4     char a;
5     int c;
6 } Stu;

 a) Windows平臺,pack默認為8,先求外對齊大小Y,結構體中型別最大的為int型別,大小為4位元組,4比8小,所以Y值為4,

b) 然后將結構體中的每一個成員與Y進行比較,依次求內對齊的大小,結構體中成員分別為short 2位元組,char 1位元組,int 4位元組,與4(外對齊大小Y)比較,得到內對齊大小分別為  2,1, 4,

c) 假設起始地址為0x00,0可以被2整除,可以存放b了,b為short型別,大小為2個位元組,接著地址為0x02,2可以被1整除,a為char型別,大小為一個位元組,然后下一個地址為0x03,0x03不可以被4整除,接著地址為0x04(此時,b與c之間填充了一個位元組),0x04可以被4整除,于是可以存放c了,c為int型別,大小為4個位元組,

d) 接著地址到了0x08,(0x08-0x00)它可以被4(外對齊大小Y)整除,滿足外對齊要求,

經分析,結構體大小為2 + 1 + 1 + 4 = 8個位元組,

圖如下圖所示

  代碼運行結果如下.

結構體大小為8,符合預期,

 

3)調整結構體中成員的順序,結構體先按照int、short、char的順序定義,列印成員地址的時候也需要調整為c、b、a的列印順序,代碼的其它部分保持不變,

1 typedef struct stu
2 {
3     int c;
4     short b;
5     char a;
6 
7 } Stu;

a) Windows平臺,pack默認為8,先求外對齊大小Y,結構體中型別最大的為int型別,大小為4位元組,4比8小,所以Y值為4,

b) 然后將結構體中的每一個成員與Y進行比較,依次求內對齊的大小,結構體中成員分別為int 4位元組,short 2位元組,char 1位元組,與4(外對齊大小Y)比較,得到內對齊大小分別為4,  2,1,

c) 假設起始地址為0x00,0x00可以被4整除,可以存放c了,c為int型別,大小為4個位元組,接著地址為0x04,0x04可以被2整除,b為short型別,大小為兩個位元組,然后下一個地址為0x06,0x06可以被1整除,可以存放a,a大小為1個位元組,

d) 接著地址到了0x07,(0x07-0x00)不能被4(外對齊大小Y)整除,為滿足外對齊要求,后面需要填充1個位元組,

經分析,結構體大小為:4 + 2 + 1 + 1 = 8個位元組,

圖示如下

 運行結果如下

結構體大小為8,符合預期,

 

實驗調整

將pack修改為1,在前面三個實驗的基礎上,再驗證一下,看下使用這個規則是否與實際情況一致,在代碼前面添加

1 #pragma pack(1)

 

4) 結構體與1)中一致,按照char、short、int的順序定義,列印成員地址也是按照a、b、c這個次序列印,

1 #pragma pack(1)
2 
3 typedef struct stu
4 {
5     char a;
6     short b;
7     int c;
8 } Stu;

 a) 現在pack為1,先求外對齊大小Y,結構體中型別最大的為int型別,大小為4位元組,1比4小,所以Y值為1,

b) 然后將結構體中的每一個成員與Y進行比較,依次求內對齊的大小,結構體中成員分別為char 1位元組,short 2位元組,int 4位元組,與1(外對齊大小Y)比較,得到內對齊大小分別為 1, 1, 1,

c) 假設起始地址為0x00,0x00可以被1整除,可以存放a了,a為char型別,大小為1個位元組,接著地址為0x01,1可以被1整除,可以存放b了,b的型別為short型別,大小為2個位元組,然后下一個地址為0x03,0x03可以被1整除,可以存放c了,c為int型別,占4個位元組,

d) 接著下一個地址為0x07,(0x07-0x00)可以被1(外對齊大小Y)整除,滿足外對齊的要求,

經分析,結構體大小為1 + 2 + 4 = 7個位元組,

圖如下圖所示

 

 運行結果如下

 結構體大小為7,符合預期,

 

5) 調整結構體中成員的順序,結構體按照short、char、int的順序定義,列印成員地址的時候也需要調整下a與b的列印順序,代碼的其它部分不變,

1 #pragma pack(1)
2 
3 typedef struct stu
4 {
5     short b;
6     char a;
7     int c;
8 } Stu;

a) 現在pack為1,先求外對齊大小Y,結構體中型別最大的為int型別,大小為4位元組,1比4小,所以Y值為1,

b) 然后將結構體中的每一個成員與Y進行比較,依次求內對齊的大小X,結構體中成員分別為short 2位元組,char 1位元組,int 4位元組,分別與1(外對齊大小Y)比較,1較小,得到內對齊大小分別為  1,1,1,

c) 假設起始地址為0x00,0x00可以被1整除,可以存放b了,b為short型別,大小為2個位元組,接著地址為0x02,0x02可以被1整除,可以存入a了,a為char型別,大小為1個位元組,然后下一個地址為0x03,0x03可以被1整除,c的型別為int,c的大小為4個位元組,

d) 在c起始地址的基礎往后4個位元組,現在地址到了0x07,(0x07-0x00)可以被1整除,滿足外對齊要求(不需要填充位元組了),

經分析,結構體大小為2 + 1 + 4 =7個位元組,

圖如下圖所示

  運行結果如下

結構體大小為7,符合預期,

 

6) 調整結構體中成員的順序,結構體按照int、short、char的順序定義,列印成員地址的時候也需要調整為c、b、a的列印順序,代碼的其它部分不變,

1 #pragma pack(1)
2 
3 typedef struct stu
4 {
5     int c;
6     short b;
7     char a;  
8 } Stu;

a) pack現在為1,先求外對齊大小Y,結構體中型別最大的為int型別,大小為4位元組,1比4小,所以Y值為1,

b) 然后將結構體中的每一個成員與Y進行比較,依次求內對齊的大小,結構體中成員分別為int 4位元組,short 2位元組,char 1位元組,分別與1(外對齊大小Y)比較,得到內對齊大小分別為1,  1,1,

c) 假設起始地址為0x00,0x00可以被4整除,可以存放c了,c為int型別,大小為4個位元組,接著地址為0x04,0x04可以被1整除,b為short型別,大小為兩個位元組,然后下一個地址為0x06,0x06可以被1整除,可以存放a了,a為char型別,a大小為1個位元組,

d) 接著地址到了0x07,(0x07-0x00)可以被1(外對齊大小Y)整除,滿足外對齊要求(不需要填充位元組),

經分析,結構體大小為:4 + 2 + 1 = 7個位元組,

圖示如下

 運行結果如下

 結構體大小為7,符合預期,

 

四.例題演示

有人可能會說了,你舉的這幾個例子太簡單了,有沒有復雜的例子可以看下呢?比如結構體中包含陣列的情況,這個計算方法是否適用呢?好的,我們就拿《深入理解計算機系統》這本書的習題來驗證一下,

書中練習題 3.44 有5道題目,均是求結構體大小與成員的偏移量,我們將其作為案例,按照上述方法驗證一下,

對下面的每個結構體宣告,確定每個欄位的偏移量、結構體總的大小、以及在x86-64下它的對齊要求,

(注:因為書中的案例中K值為8,所以下面的代碼示例中將pack均顯式設定為了8)
1) 

1 struct P1{int i; char c; int j; char d;}

a) K現在為8,先求外對齊大小Y,結構體中型別最大的為int型別,大小為4位元組,4比8小,所以Y值為4,

b) 然后將結構體中的每一個成員與Y進行比較,依次求內對齊的大小,取較小者,結構體中成員分別為int 4位元組,char 1位元組,int 4位元組,char 1位元組,分別與4(外對齊大小Y)比較,得到內對齊大小分別為4,1,4, 1,

c) 假設起始地址為0x00,0x00可以被4整除,可以存放i了,i為int型別,大小為4個位元組,接著地址為0x04,0x04可以被1整除,可以存盤c,c為char型別,大小為1個位元組,然后下一個地址為0x05,0x05不可以被4整除,最近的能被4整除的地址就是0x08了,需要填充3個位元組,于是從0x08開始存放j,j為int型別,j大小為4個位元組,下一個地址為0x0c,

d) 0x0c可以被1整除, d為char型別,大小為1個位元組,接著地址到了0x0d,(0x0d-0x00)不可以被4(外對齊大小Y)整除,為滿足外對齊要求,需要填充3個位元組,直到0x10,(0x10-0x00)是Y的整數倍,滿足外對齊,

經分析,結構體大小為:4 + 1 + 3 + 4 + 1 + 3 = 16個位元組,

i 偏移量 0

c 偏移量 4

j 偏移量 8

d 偏移量 12

圖示如下

 代碼如下,為了避免編譯器告警,與前面相比,作了調整,進行了強制型別轉換,

 1 #pragma pack(8)
 2 
 3 #include <stdio.h>
 4 
 5 typedef struct P1{int i; char c; int j; char d;} PP1;
 6 
 7 int main()
 8 {
 9     PP1 p;
10     printf("sizeof(P1) = %d\n", (int)sizeof(PP1));
11     printf("sizeof(p)  = %d\n", (int)sizeof(p));
12 
13     printf("&p   = %p\n", (void *)&p);
14     printf("&p.i = %p\n", (void *)&p.i);
15     printf("&p.c = %p\n", (void *)&p.c);
16     printf("&p.j = %p\n", (void *)&p.j);
17     printf("&p.d = %p\n", (void *)&p.d);
18 
19     return 0;

運行結果如下

 i,c, j,d的偏移量分別為0, 4, 8, 12,符合預期,

 

2)

1 struct P2 {int i; char c; char d; long j;};

a) K現在為8,先求外對齊大小Y,結構體中型別最大的為long型別,我這臺電腦上long大小為4位元組,4比8小,所以Y值為4,

b) 然后將結構體中的每一個成員與Y進行比較,依次求內對齊的大小,結構體中成員分別為int 4位元組,char 1位元組,char 1位元組,long 4位元組,分別與4(外對齊大小Y)比較,取較小者,得到內對齊大小分別為4,1,1, 4,

c) 假設起始地址為0x00,0x00可以被4整除,可以存放i了,i為int型別,大小為4個位元組,接著地址為0x04,0x04可以被1整除,可以存盤c,c為char型別,大小為1個位元組,然后下一個地址為0x05,0x05可以被1整除,可以存盤d,d的型別為char,大小為1個位元組,接下來地址為0x06,0x06不能被4整除,最近能被4整除的地址為0x08,需要填充2個位元組,才能到0x08,j為long型別,j大小為4個位元組,

d) 接著地址到了0x0c,(0x0c-0x00)可以被4(外對齊大小Y)整除,滿足外對齊,

經分析,結構體大小為:4 + 1 + 1 + 2 + 4 = 12個位元組,

i 偏移量 0

c 偏移量 4

d 偏移量 5

j 偏移量 8

圖示如下:

 代碼如下

 1 #include <stdio.h>
 2 
 3 #pragma pack(8)
 4 
 5 typedef struct P2
 6 {
 7     int i;
 8     char c;
 9     char d;
10     long j;
11 } PP2;
12 
13 int main()
14 
15 {
16     printf("sizeof(long) = %d\n", (int)sizeof(long));
17 
18     PP2 p;
19     printf("sizeof(P1) = %d\n", (int)sizeof(PP2));
20     printf("sizeof(p)  = %d\n", (int)sizeof(p));
21 
22     printf("&p   = %p\n", (void *)&p);
23     printf("&p.w = %p\n", (void *)&p.i);
24     printf("&p.c = %p\n", (void *)&p.c);
25     printf("&p.c = %p\n", (void *)&p.d);
26     printf("&p.c = %p\n", (void *)&p.j);
27 
28     return 0;
29 }

運行結果如下

  i,c,d,j的偏移量分別為0,4,5,8,符合預期,

 

3)若結構體中有陣列,如何整?

1 struct P3{short w[3]; char c[3]};

a) K現在為8,先求外對齊大小Y,若有陣列,取陣列中元素的型別,結構體中型別最大的是short型別,大小為2位元組,2比8小,所以Y值為2,

b) 然后將結構體中的每一個成員與Y進行比較,依次求內對齊的大小,若結構體成員為陣列,取成員整體的大小,結構體中成員分別為w 6位元組,c 3位元組,分別與2(外對齊大小Y)比較,得到內對齊大小分別為2,2,

c) 假設起始地址為0x00,0x00可以被2整除,可以存放w了,w為short陣列型別,大小為6個位元組,接著地址為0x06,可以被3整除,可以存盤c,c為char陣列型別,大小為3個位元組,

d) 接著地址到了0x09,(0x09-0x00)不可以被2(外對齊大小Y)整除,為滿足外對齊要求,需要填充1個位元組,直到0x0a,(0x0a-0x00)是Y的整數倍,滿足外對齊,

經分析,結構體大小為:6 + 3 + 1 = 10個位元組,

w偏移量 0

c偏移量 6

 通過代碼驗證一下,代碼如下

 1 #include <stdio.h>
 2 
 3 #pragma pack(8)
 4 
 5 typedef struct P3{short w[3]; char c[3];} PP3;
 6 
 7 int main()
 8 
 9 {
10     PP3 p;
11     printf("sizeof(P1) = %d\n", (int)sizeof(PP3));
12     printf("sizeof(p)  = %d\n", (int)sizeof(p));
13 
14     printf("&p   = %p\n", (void *)&p);
15     printf("&p.i = %p\n", (void *)&p.w);
16     printf("&p.c = %p\n", (void *)&p.c);
17 
18     return 0;
19 }

運行結果如下

 w,c的偏移量分別為0,6,符合預期,

 

4) 如果來個指標陣列呢?

1 struct P4{short w[5]; char *c[3];};

a) K現在為8,先求外對齊大小Y,若有陣列,取陣列中元素的型別,結構體中型別最大的是指標型別,大小為8位元組,8與8相等,所以Y值為8,

b) 然后將結構體中的每一個成員與Y進行比較,依次求內對齊的大小,若結構體成員為陣列,取成員整體的大小,結構體中成員分別為w 10位元組,c 24位元組,分別與8(外對齊大小Y)比較,得到內對齊大小分別為8,8,

c) 假設起始地址為0x00,0x00可以被8整除,可以存放w了,w為short陣列型別,大小為10個位元組,接著地址為0x0a,不可以被8整除,需要填充6個位元組,地址到了0x10,這才可以存盤c,c為指標陣列型別,大小為24個位元組,

d) 接著地址到了0x28,(0x28-0x00)可以被8(外對齊大小Y)整除,滿足外對齊,

w 偏移量 0

c 偏移量 16

經分析,結構體大小為:10 + 6 + 24 = 40個位元組,這個圖有些大,就不放圖片了,

通過代碼驗證一下,代碼如下

 1 #include <stdio.h>
 2 
 3 #pragma pack(8)
 4 
 5 typedef struct P4
 6 {
 7     short w[5];
 8     char *c[3];
 9 } PP4;
10 
11 int main()
12 
13 {
14     PP4 p;
15     printf("sizeof(P1) = %d\n", (int)sizeof(PP4));
16     printf("sizeof(p)  = %d\n", (int)sizeof(p));
17 
18     printf("&p   = %p\n", (void *)&p);
19     printf("&p.w = %p\n", (void *)&p.w);
20     printf("&p.c = %p\n", (void *)&p.c);
21 
22     return 0;
23 }

運行結果如下

 w,c的偏移量分別為0, 16,符合預期,

 

5)假如結構體中嵌套結構體,這個方法還適用嗎?那再來驗證一波,

1 typedef struct P5
2 {
3     struct P3 a[2];
4     struct P2 t;
5 } PP5;

看起來有點復雜,不過還是用同樣的方法,

a) K現在為8,先求外對齊大小Y,若有陣列或結構體,取陣列或結構體中成員的型別,結構體中型別最大的是long型別,大小為4位元組,4比8比小,所以Y值為4,

b) 然后將結構體PP5中的每一個成員與Y進行比較,依次求內對齊的大小,若結構體成員為陣列或結構體,取成員整體的大小,前面已經求出,結構體中成員分別為a 20位元組,t 12位元組,分別與4(外對齊大小Y)比較,得到內對齊大小分別為4,4,

c) 假設起始地址為0x00,0x00可以被4整除,可以存放a了,a為結構體陣列,大小為20個位元組,接著地址為0x14,0x14可以被4整除,可以存盤t,t為結構體型別,大小為12個位元組,

d) 接著地址到了0x20,(0x20-0x00)可以被4(外對齊大小Y)整除,滿足外對齊,

a 偏移量 0

t  偏移量 20

經分析,結構體大小為:20 + 12 = 32個位元組,

通過代碼驗證一下,代碼如下

 1 #include <stdio.h>
 2 
 3 #pragma pack(8)
 4 
 5 struct P2
 6 {
 7     int i;
 8     char c;
 9     char d;
10     long j;
11 };
12 
13 struct P3
14 {
15     short w[3];
16     char c[3];
17 };
18 
19 typedef struct P5
20 {
21     struct P3 a[2];
22     struct P2 t;
23 } PP5;
24 
25 int main()
26 
27 {
28     printf("sizeof(long) = %d\n", (int)sizeof(long));
29 
30     PP5 p;
31     printf("sizeof(PP5) = %d\n", (int)sizeof(PP5));
32     printf("sizeof(p)   = %d\n", (int)sizeof(p));
33 
34     printf("&p   = %p\n", (void *)&p);
35     printf("&p.a = %p\n", (void *)&p.a);
36     printf("&p.t = %p\n", (void *)&p.t);
37 
38     return 0;
39 }

運行結果如下

 a,t的偏移量分別為0, 20,符合預期,

 

五.結語

  目前,已經驗證了結構體中包含基本資料型別,結構體中包含陣列,結構體中包含結構體(結構體嵌套)的情形,可能出現的情況都驗證完了,也證實了書中的方法的確有效,感謝前輩們總結的經驗,

 注:如果你在做《深入理解計算機系統》書上練習題時,會發現書中提供的3.44 中B和E兩題練習題答案與我上面的結果不一致,不要慌,我對比了下,原因很可能是作者的機器中long型別是8位元組,而我的機器中long型別是4位元組,依據之一請參見書中P27頁"字資料大小"一節,依據二,根據兩種不同大小的long型別,分別進行計算與驗證,這里就不贅述了,

 

參考材料

1.《深入理解計算機系統》布萊恩特,奧哈拉倫

2.《零基礎入門C語言》  王桂林

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/307107.html

標籤:其他

上一篇:Python股價翻番,人生贏家,Python爬取基金,篩選股票

下一篇:C語言 fread 函式 - C語言零基礎入門教程

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more