在程式設計時,最重要的步驟之一就是選擇表示資料的方法。在許多的情況下,簡單變數甚至是陣列還不足以表示一個事物的屬性。為此,C語言提供了結構體變數(structure variable)提高表示資料的能力。
結構體是一些值的集合,這些值稱為成員變數,把一些基本型別的資料組合在一起,形成一個新的複雜資料型別。結構體中的每個成員可以是不同型別的變數。
掌握:
1、為結構體建立一個格式或樣式
2、宣告一個合適的結構體變數
3、存取結構變數的各個部分
結構體宣告常用形式:
struct tag
{
member-list; //成員變數,
}variable-list; //;不可省略
定義結構體的三種形式 (推薦使用第一種)
例如:描述一本書(包括:書名、作者、價格等)
第一種:只定義一個新的資料型別,沒有定義變數
struct book //struct book 所起的作用相當於一般宣告中的 int 或 float
{
char title[30];
char author[30];
float price;
}; //分號不能省略,沒有定義變數
struct book Jane_Eyre; //在使用時再定義結構體變數 Jane_Eyre(英國文學名著《簡·愛》作家夏洛蒂·勃朗特)
第二種:定義新的資料型別同時定義變數
struct book
{
char title[30];
char author[30];
float price;
}Jane_Eyre; //分號不能省略,同時定義變數
//宣告的右花括號跟變數名
第三種:不完全宣告的結構體
struct //匿名結構體型別,省略了結構體標籤book
{
char title[30];
char author[30];
float price;
}Jane_Eyre; //分號不能省略,同時定義變數
但是,如果打算多次使用結構模板,就要使用帶標記的形式;或者,使用typedef函數。
我們知道初始化變數和陣列的方法:
int sum =0;
int fibo[ 5 ] = { 1,1,2,3,5};
結構變數能不能這樣初始化呢?答案是 :yes
初始化結構變數與初始化陣列的語法語法類似:
方法一:定義變數的同時賦初始值
struct book
{
char title[30];
char author[30];
float price;
};
struct book Jane_Eyre = {"Jane_Eyre","Charlotte Bronte",35.5};
***************************************************************
struct book
{
char title[30];
char author[30];
float price;
} Jane_Eyre = {"Jane_Eyre","Charlotte Bronte",35.5};
***********************************************************************
使用一對{ }來對結構體進行初始化,各個初始化項用逗號隔開。
為了讓初始化項與結構體體中的各個成員關聯更加明顯,我們可以讓每個成員單獨佔一行。這樣做的目的只是為了提高可讀性,對於編譯器本身而言並沒有區別;
struct book masterwork ={
"the Old Man and the Sea",
"Hemingway",
32.4
};
方法二:逐個成員賦值
struct book Jane_Eyre = {
.title = "Jane_Eyre",
.price = 35.5;
.author="Charlotte Bronte"
};
*********************************************************
struct book Jane_Eyre = {
.price = 35.5;
};
1. 逐個成員賦值時,可以只初始化其中的某一個成員;
2. 逐個成員賦值時,賦值的順序可以是任意的;
3. 如果有多次賦值,最後一次賦值才是該成員變數真實的值;
方法三:整體賦值
struct book Jane_Eyre = {
.title = "Jane_Eyre",
.price = 35.5;
.author="Charlotte Bronte"
};
//定義了一本書:烏托邦
struct book Utopia = Jane_Eyre;
1)相同資料型別結構體變數可以直接賦值
1)凡是基本資料型別,既可以定義時初始化,也可以先定義,再賦值
2)凡是構造型別,要麼在定義時初始化(定義的時候可以整體賦值);
不可以,先定義再以初始化的方式初始化;(如果定義完之後,只能單個的賦值)
在講述結構體成員的存取之前,我們先來講述一下結構體的指標,因為在結構體成員存取時也可以通過結構體指標來存取;
喜歡用指標的人一般都樂於使用結構體指標,因為:
1. 就像指向陣列的指標比陣列本身更容易操控;
2. 傳遞給函數時,傳遞結構體指標效率更高;
3. 在表示資料的結構體中可能包含指向其他結構的指標
//可以宣告的同時定義
struct book
{
char title[30];
char author[30];
float price;
} * book1,book2;
//也可以先宣告,後定義
struct book * book3 = & book2
和陣列不同,結構名並不是結構體的地址,因此要在結構名前加上 & 運運算元
結構體的存取有兩種形式,一種是:結構體變數名 . 成員名 ;二種是:指標變數名 ->成員名
方法一:結構體變數名 . 成員名
struct book
{
char title[30];
char author[30];
float price;
};
int main()
{
struct book book2 = {"Jane_Eyre","Charlotte Bronte",35.5};
printf("%s\n",book2.title);
printf("%s\n",book2.author);
printf("%f\n",book2.price);
return 0;
}
方法二:指標變數名 ->成員名
int main()
{
struct book * book1 = &book2;
printf("%s\n",book1->title);
printf("%s\n",book1->author);
printf("%f\n",book1->price);
return 0;
}
// book1->title == (*book1) . title == book2.title
第二種方法是最為常用的方法:使用 針變數名 ->成員名 ,在計算機內部會被轉化成 (*針變數名).成員名 的方式來執行,所以 book1->title 等價於 (*book1) . title 等價於 book2.title;
所以說這兩種方式是等價的
首先掌握結構體記憶體的對齊規則:
知道了理論,下面開始舉例
以下面為例:
struct S1
{
char c;
int i;
double d;
};
第一步:第一個成員在與結構體變數偏移為0的地址處;![在這裡插入圖片描述](https://i
第二步:其他成員變數要對齊到 對齊數的整數倍;(對齊數:編譯器預設的對齊數與該成員大小的較少值)
第三步: 結構體總大小為最大對齊數(每個成員變數都有一個對齊數)的整數倍
如果巢狀了結構體的情況,巢狀的結構體對齊到自己最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數的整數倍(包括巢狀結構體的對齊數)。
記憶體對齊的意義:
1、平臺原因(移植原因):各個硬體平臺對儲存空間的處理上有很大的不同。一些平臺對某些特定型別的資料只能從某些特定地址開始存取。比如有些架構的CPU在存取一個沒有進行對齊的變數的時候會發生錯誤,那麼在這種架構下程式設計必須保證位元組對齊
2、效能原因: 資料結構(尤其是棧)應該儘可能地在自然邊界上對齊。 按照適合其平臺要求對資料存放進行對齊,會在存取效率上帶來損失。比如有些平臺每次讀都是從偶地址開始,如果一個int型(假設為32位元系統)如果存放在偶地址開始的地方,那 麼一個讀週期就可以讀出這32bit,而如果存放在奇地址開始的地方,就需要2個讀週期,並對兩次讀出的結果的高低位元組進行拼湊才能得到該32bit資料。顯然在讀取效率上下降很多。
設計結構體變數時:將佔用空間小的成員集中在一起,在一定程度可以避免記憶體的浪費。
可見,相同的成員變數,不同的順序,也會影響結構體的大小;
位元欄的宣告和結構體的宣告基本相同,但有兩個不同;
1、位元欄的成員必須是整形家族的(int 、unsigned int、singned int、char)
2、位元欄的成員後有一個冒號和一個數位;
struct s1
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
}
一個int 型別佔用4個位元組(共32個位元位),int _a:2 申請了一個int型的變數,:6 表示儲存這個資料的空間只佔用了6個位元位。所以位元欄的使用可以使得結構體的空間變小,但伴隨而來的就是每個成員變數儲存空間的改變,和平臺可移植性的問題。
正常情況下每個int型變數可以儲存的資料是2^32個,但int _a:2實際上只使用了兩個位元位,實際可以表示的資料為2^2個;
位元欄的跨平臺問題:
1、int位元欄被當成有符號數還是無符號數是不確定的;
2、位元欄中最大位的數目不能確定(16位元機器最大16,32位元機器最大32,如果在32位元機器上上使用一個超過16位元的資料,移植到16位元機器中時,就有可能會出現問題)
3、位元欄中的成員在記憶體中是從左往右分配,還是從右往左分配沒有一個標準的定義(因此只畫出記憶體使用位元位數的示意圖,具體使用情況視平臺而定)
4、當一個結構博包含兩個位元欄,第二個位元欄成員比較大時,無法容納於第一個位元欄剩餘位時,剩餘的位是捨棄還是利用,是不確定的。
跟結構體相比,位元欄可以達到相同的效果。可以在一定程度上節省空間,但是會有跨平臺的問題出現。