方城網(wǎng)站制作網(wǎng)絡(luò)營銷專業(yè)課程
結(jié)構(gòu)體
1 結(jié)構(gòu)體的聲明
1.1 結(jié)構(gòu)的基礎(chǔ)知識
結(jié)構(gòu)是一些值的集合,這些值稱為成員變量。結(jié)構(gòu)的每個成員可以是不同類型的變量。
1.2 結(jié)構(gòu)的聲明
struct tag//struct是結(jié)構(gòu)體的標(biāo)志,tag是標(biāo)簽;名字。
{member-list;//成員變量
}variable-list;//變量列表
例如描述一個學(xué)生:
struct Stu
{char name[20];//名字int age;//年齡char sex[5];//性別char id[20];//學(xué)號
}; //分號不能丟
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
struct Stu
{//成員變量char name[20];//名字int age;//年齡char sex[5];//性別char id[20];//學(xué)號}s4,s5,s6;//分號不能丟 //全局變量
int main()
{struct Stu s1;//局部變量struct Stu s2;struct Stu s3;return 0;
}
這里的是s1,s2,s3,s4,s5,s6基本都是一樣的,唯一不同的是一個是全局變量,另一個是局部變量。
1.3 特殊的聲明
在聲明結(jié)構(gòu)的時候,可以不完全的聲明。
比如:
//匿名結(jié)構(gòu)體類型
struct
{int a;char b;float c;
}x;
struct
{int a;char b;float c;
}a[20], *p;
上面的兩個結(jié)構(gòu)在聲明的時候省略掉了結(jié)構(gòu)體標(biāo)簽( tag )。那么問題來了?
//在上面代碼的基礎(chǔ)上,下面的代碼合法嗎?
p = &x;
警告:編譯器會把上面的兩個聲明當(dāng)成完全不同的兩個類型。(即使它們的成員變量相同且都是匿名結(jié)構(gòu)體類型)所以是非法的。
1.4 結(jié)構(gòu)的自引用?
在結(jié)構(gòu)中包含一個類型為該結(jié)構(gòu)本身的成員是否可以呢?
?
//代碼1
struct Node
{int data;struct Node next;
};
//可行否?
如果可以,那sizeof(struct Node)是多少?
?不行,因為已經(jīng)有個data的數(shù)據(jù)了,你再加個next的數(shù)據(jù),就亂套了,沒有下一個地址接收,所以會一直循環(huán)下去。大小無法求。
正確的自引用方式:
//代碼2
struct Node
{int data;struct Node* next;
};
struct Node
{int data;//4struct Node* next;//4/8
};int main()
{struct Node n1;struct Node n2;n1.next = &n2;return 0;
}
補(bǔ):
typedef struct
{int data;char c;
} S;
這里用typedef struct中typedef的使用:這個結(jié)構(gòu)體的S是該結(jié)構(gòu)體的類型名。沒有typedef的話,該S則是一個變量。
//代碼3
typedef struct
{int data;Node* next;
}Node;
//這樣寫代碼,可行否?
//不行 因為都還沒有完整的Node就直接在后面Node 前后矛盾了
//解決方案:
typedef struct Node
{int data;struct Node* next;
}Node;
1.5 結(jié)構(gòu)體變量的定義和初始化
有了結(jié)構(gòu)體類型,那如何定義變量?
struct Point
{int x;int y;
}p1; //聲明類型的同時定義變量p1
struct Point p2; //定義結(jié)構(gòu)體變量p2
//初始化:定義變量的同時賦初值。
struct Point p3 = {x, y};
struct Stu ? ? ? ?//類型聲明
{char name[15];//名字int age; ? ? ?//年齡
};
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{int data;struct Point p;struct Node* next;
}n1 = {10, {4,5}, NULL}; //結(jié)構(gòu)體嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//結(jié)構(gòu)體嵌套初始化
(.)操著直接訪問
?結(jié)構(gòu)體里面定義指針
struct S
{char name[100];int* ptr;
};
int main()
{struct S s = { "abcdef",NULL };return 0;
}
1.6 結(jié)構(gòu)體內(nèi)存對齊
我們已經(jīng)掌握了結(jié)構(gòu)體的基本使用了。現(xiàn)在我們深入討論一個問題:計算結(jié)構(gòu)體的大小。這也是一個特別熱門的考點: 結(jié)構(gòu)體內(nèi)存對齊。
?為什么上圖的s1,s2,s3的打印結(jié)果是8,12,12? 為什么不是按照類型大小打印?為什么不是打印5,6,7? 往下看......
結(jié)構(gòu)體存放內(nèi)存是要對齊的!!?
考點如何計算 ?首先得掌握結(jié)構(gòu)體的對齊規(guī)則:1. 第一個成員在與結(jié)構(gòu)體變量偏移量為 0 的地址處。2. 其他成員變量要對齊到某個數(shù)字(對齊數(shù))的整數(shù)倍的地址處。對齊數(shù) = 編譯器默認(rèn)的一個對齊數(shù) 與 該成員大小的 較小值 。VS 中默認(rèn)的值為 83. 結(jié)構(gòu)體總大小為最大對齊數(shù)(每個成員變量都有一個對齊數(shù))的整數(shù)倍。4. 如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體對齊到自己的最大對齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整體大小就是所有最大對齊數(shù)(含嵌套結(jié)構(gòu)體的對齊數(shù))的整數(shù)倍。
//練習(xí)1 struct S1 {char c1;int i;char c2; }; printf("%d\n", sizeof(struct S1)); //練習(xí)2 struct S2 {char c1;char c2;int i; }; printf("%d\n", sizeof(struct S2)); //練習(xí)3 struct S3 {double d;char c;int i; }; printf("%d\n", sizeof(struct S3));
![]()
?
為什么存在內(nèi)存對齊 ?大部分的參考資料都是如是說的:1. 平臺原因 ( 移植原因 ) :不是所有的硬件平臺都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。2. 性能原因 :數(shù)據(jù)結(jié)構(gòu) ( 尤其是棧 ) 應(yīng)該盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問;而對齊的內(nèi)存訪問僅需要一次訪問。總體來說:結(jié)構(gòu)體的內(nèi)存對齊是拿 空間 來換取 時間 的做法。
那在設(shè)計結(jié)構(gòu)體的時候,我們既要滿足對齊,又要節(jié)省空間,如何做到:讓占用空間小的成員盡量集中在一起。//例如: struct S1 {char c1;int i;char c2; }; struct S2 {char c1;char c2;int i; };
S1 和 S2 類型的成員一模一樣,但是 S1 和 S2 所占空間的大小有了一些區(qū)別。
?1.7 修改默認(rèn)對齊數(shù)
之前我們見過了 #pragma 這個預(yù)處理指令,這里我們再次使用,可以改變我們的默認(rèn)對齊數(shù)。?
#include <stdio.h>
#pragma pack(8)//設(shè)置默認(rèn)對齊數(shù)為8
struct S1
{char c1;int i;char c2;
};
#pragma pack()//取消設(shè)置的默認(rèn)對齊數(shù),還原為默認(rèn)
#pragma pack(1)//設(shè)置默認(rèn)對齊數(shù)為1
struct S2
{char c1;int i;char c2;
};
#pragma pack()//取消設(shè)置的默認(rèn)對齊數(shù),還原為默認(rèn)
int main()
{//輸出的結(jié)果是什么?printf("%d\n", sizeof(struct S1));printf("%d\n", sizeof(struct S2));
return0;
}
?
結(jié)論:結(jié)構(gòu)在對齊方式不合適的時候,我么可以自己更改默認(rèn)對齊數(shù)。
?1.8 結(jié)構(gòu)體傳參
直接上代碼:
struct S
{int data[1000];int num;
};
struct S s = {{1,2,3,4}, 1000};
//結(jié)構(gòu)體傳參
void print1(struct S s)
{printf("%d\n", s.num);
}
//結(jié)構(gòu)體地址傳參
void print2(struct S* ps)
{printf("%d\n", ps->num);
}
int main()
{print1(s); ?//傳結(jié)構(gòu)體print2(&s); //傳地址return 0;
}
上面的 print1 和 print2 函數(shù)哪個好些?答案是:首選 print2 函數(shù)。原因:函數(shù)傳參的時候,參數(shù)是需要壓棧,會有時間和空間上的系統(tǒng)開銷。如果傳遞一個結(jié)構(gòu)體對象的時候,結(jié)構(gòu)體過大,參數(shù)壓棧的的系統(tǒng)開銷比較大,所以會導(dǎo)致性能的 下降。
結(jié)論:結(jié)構(gòu)體傳參的時候,要傳結(jié)構(gòu)體的地址。
THE END
? ? ? ? 這是今日份關(guān)于自定義函數(shù)的一些分享,希望可以幫助到大家!如果有什么不足的地方也請家人們給小葉一些好的建議,我會不斷優(yōu)化文章的!那就讓我們一起加油吧!哈哈哈哈哈