?
🥰歡迎關注?輕松拿捏C語言系列,來和 小哇 一起進步!?
🎉創作不易,請多多支持🎉
🌈感謝大家的閱讀、點贊、收藏和關注💕
🌹如有問題,歡迎指正
1. 結構體類型的聲明
結構體是一些值的集合
這些值叫做 成員或 分量或 域或 項。
結構的每個成員變量可以是不同的類型
例如描述一個學生:
struct Stu
{char name[20];//名字int age;//年齡char sex[5];//性別char id[20];//學號
}; //分號不能丟
這是結構體的聲明
struct 是結構體關鍵字,struct Stu 是這個結構體類型名?
2. 結構體變量的創建和初始化
#include <stdio.h>
struct Stu
{char name[20];//名字int age;//年齡char sex[5];//性別char id[20];//學號
};
int main()
{//按照結構體成員的順序初始化struct Stu s = { "張三", 20, "男", "20230818001" };printf("name: %s\n", s.name);printf("age : %d\n", s.age);printf("sex : %s\n", s.sex);printf("id : %s\n", s.id);//按照指定的順序初始化struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex =
"?" };printf("name: %s\n", s2.name);printf("age : %d\n", s2.age);printf("sex : %s\n", s2.sex);printf("id : %s\n", s2.id);return 0;
}
在進行結構體聲明時,也可以進行不完全聲明:
//匿名結構體類型
struct
{int a;char b;float c;
}x;struct
{int a;char b;float c;
}a[20], *p;
上?的兩個結構在聲明的時候省略掉了結構體標簽
上?的兩個聲明是完全不同的兩個類型。
匿名的結構體類型,如果沒有對結構體類型重命名的話,基本上只能使??次 。
在結構中包含?個類型為該結構本?的成員是不可以的
例如 :
struct Node
{int data;struct Node next;
};
這是錯誤的用法。
因為?個結構體中再包含?個同類型的結構體變量,這樣結構體變量的大小就會?窮的?,是不合理的?
正確自引用方式:
struct NOde
{int data;struct Node* next;//定義一個結構體類型指針
};
在結構體自引用使用的過程中,夾雜了 typedef 對匿名結構體類型重命名,也容易引入問題,比如:
typedef struct
{int data;Node* next;
}Node;
?這是錯誤用法,因為Node是對前面的匿名結構體類型的重命名產?的,但是在匿名結構體內部提前使用Node類型來創建成員變量,這是不行的
所以定義結構體不要使用匿名結構體了
typedef struct Node
{int data;struct Node* next;
}Node;
3. 結構體內存對齊
結構體成員在內存中存在對齊現象
內存對齊的原因:
? ? ? ? 1、平臺原因(移植原因):
不是所有的硬件平臺都能訪問任意地址上的任意數據的;某些硬件平臺只能在某些地址處取某些特定類型的數據,否則拋出硬件異常
? ? ? ? 2、性能原因:
數據結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內存,處理器需要作兩次內存訪問;而對齊的內存訪問僅需要?次訪問。
所以結構體的內存對齊是拿空間來換取時間的做法,提高程序運行效率,節省空間
對齊規則:
????????1. 結構體的第?個成員對齊到和結構體變量起始位置偏移量為0的地址處
????????2. 其他成員變量的首地址與結構體首地址的偏移量要為 該成員對齊數的整數倍
? ? ? ? ? ? ? ? 對齊數 = 編譯器默認的?個對齊數 與 該成員變量大小的較小值
????????????????VS 中默認的值為 8 ,?Linux中 gcc 沒有默認對齊數,對齊數就是成員自身的大小
? ? ? ? 3、結構體總大小為最大對齊數(結構體中每個成員變量都有?個對齊數,所有對齊數中最大的)的 整數倍
? ? ? ? 4、?如果嵌套了結構體的情況,嵌套的結構體成員對齊到自己的成員中最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數(含嵌套結構體中成員的對齊數)的整數倍。
如果我們把讓占用空間小的成員盡量集中在?起,更節省空間,比如:
struct S1
{char c1;int i;char c2;
};
struct S2
{char c1;char c2;int i;
};
S1中結構體大小為 12字節,S2結構體大小為8,因為S2中兩個char類型的變量放在了一起(放在int類型的前后都一樣,都是8).
我們可以使用#pragma 這個預處理指令,可以改變編譯器的默認對齊數
#include <stdio.h>
#pragma pack(1)//設置默認對?數為1
struct S
{char c1;int i;char c2;
};
int main()
{printf("%d\n", sizeof(struct S));return 0;
}
這里結果為6,因為默認對齊系數改為了1,所以是1+4+1?
可以用#pragma pack()//取消設置的對齊數,還原為默認
4. 結構體傳參
結構體傳參的時候,要傳結構體的地址
struct S
{int data[1000];int num;
};
struct S s = {{1,2,3,4}, 1000};
//結構體傳參
void print1(struct S s)
{printf("%d\n", s.num);
}
//結構體地址傳參
void print2(struct S* ps)
{printf("%d\n", ps->num);
}
int main()
{print1(s); //傳結構體print2(&s); //傳地址return 0;
}
print1和print2這兩個函數中,print2函數更好,
因為函數傳參的時候,參數是需要壓棧,會有時間和空間上的系統開銷。
如果傳遞?個結構體對象的時候,結構體過大,參數壓棧的的系統開銷比較大,所以會導致性能的下降。?
5. 結構體實現位段
位段的聲明和結構是類似的,有兩個不同:
1. 位段的成員必須是 int、unsigned int 或signed int 或者是 char 等類型,在C99中位段成員的類型也可以選擇其他類型。(一般就是int家族)
2. 位段的成員名后邊有?個冒號和?個數字。
例如:
struct A
{int _a:2;int _b:5;int _c:10;int _d:30;
};
?這里位段A所占內存大小為8字節
位段的空間上是按照需要以4個字節( int )或者1個字節( char )的方式來開辟的
位段涉及很多不確定因素,位段是不跨平臺的,注重可移植的程序應該避免使用位段
跟結構體相比,位段可以達到同樣的效果,并且可以很好的節省空間,但是有跨平臺的問題存在:
????????1. int 位段被當成有符號數還是無符號數是不確定的。
???????? 2. 位段中最大位的數目不能確定。(16位機器最大16,32位機器最大32,寫成27,在16位機器會出問題。
????????3. 位段中的成員在內存中從左向右分配,還是從右向左分配,標準尚未定義。
???????? 4. 當?個結構包含兩個位段,第?個位段成員比較大,無法容納于第?個位段剩余的位時,是舍棄剩余的位還是利用,這是不確定的。
位段的幾個成員共有同?個字節,這樣有些成員的起始位置并不是某個字節的起始位置,那么這些位 置處是沒有地址的。
內存中每個字節分配?個地址,?個字節內部的bit位是沒有地址的。
所以不能對位段的成員使用&操作符,這樣就不能使用scanf直接給位段的成員輸入值,只能是先輸入放在?個變量中,然后賦值給位段的成員。
例如:
struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};
int main()
{struct A sa = {0};scanf("%d", &sa._b);//這是錯誤的//正確的?范int b = 0;scanf("%d", &b);sa._b = b;return 0;
}
?🎉🎉🎉本文內容結束啦,希望各位大佬多多指教!
🌹🌹感謝大家三連支持
💕敬請期待下篇~💥