C語言數據類型非常豐富,其中結構體的使用非常廣泛,也有一點復雜,這一講我們主要學習結構體的使用方法,同時也會學習到聯合、枚舉以及typedef的使用,因為結構體最為復雜,使用最廣,所以我們主要學習結構體。
struct結構體的定義和初始化
//例: struct student{ char name[100]; int age; }; int main(){ struct student st; //定義了一個student類型的結構體,名字叫做st,存放在棧里邊 st.age =20; strcpy(st.name,”liudehua”); struct student st1 ={“zhzdu”,40}; //定義結構體變量,同時初始化結構體變量 struct student st1 ={.age =40,.name=”zxjiifuhg”}; //定義結構體變量,同時初始化結構體變量,使用這種方式可以改變初始化順序 struct student st1 ={“zhzdu”}; //定義結構體變量,同時初始化結構體變量,不初始化的結構體變量則默認為0 };
結構體的對齊說明
結構體在內存中總是對齊的,一個結構體成員變量總是以最大的那個元素作為對齊單位。
struct A{//8字節,a1后面空着3個字節,a2占4個字節 char a1; int a2; } struct A1{//8字節,a1后面空着一個字節,然后a3占兩個字節,a2占4個字節 char a1; short a3; int a2;
} struct A2{ char a[10]; int b; }
如果結構體出現數組,則以數組中的具體每個成員大小為對齊標准,如果變量結構體中的所有成員都是一種類型,那么結構體在內存中就基本和一個數組類似。結構體變量的地址就是它首元素的地址。
結構體元素的位字段
為了節省內存空間,結構體變量允許使用為聲明,例子如下:
struct A2{//一共占1字節 unsigned char a:2;//a只有兩個bit unsigned char b:4; //b只有4個bit }
結構體數組
struct A2{ char name[20]; unsigned char age; unsigned char sex; } void main(){ struct A2 st[3];//定義一個結構體數組,有3個成員,每個成員都是struct結構體變量 }
CPU處理int相比其它基本數據類型效率是最高的,但是int比char要多占內存.
冒泡排序結構體數組
首先要學會使用冒泡排序,然后根據結構體某個成員大小之間作比較,再根據冒泡排序交換結構體中的各個成員來排序,具體的實現方法可自行實現。
結構體嵌套
結構體內部可以有其它結構體,其本質和結構體沒有太多區別。
struct A{ char a1; short a2; }; struct B{ struct A a;//這里是一個結構體的嵌套 char a3;//上面結構體變量作為一個整體存在,a3 不可能補到結構體A a2的后面去,它一定是一個單獨的對齊單位。 int b; }; struct D{};//D結構體不含有任何結構體變量,這個語法在C語言是不合法的,在C++里是合法的
結構體的賦值其實就是內存的拷貝
struct A{ char a1; short a2; }; struct A st1={s ,s}; struct A st2=st1; //通過指針訪問結構體成員 struct A * p=&st1; //(*p).a1 =12;這種寫法與下面寫法作用相同,但下面寫法更直觀 p->a1 = 12;
通過指針訪問結構體數組
這個和通過指針去訪問數組是類似的,這兒就不詳細介紹了
void main(){ struct A2 st[3]={0}; struct A2 *p=st; p[1].name = “zcxc” }
結構體中的數組成員和指針成員
struct man{ char * name; int age; };
結構體拷貝的時候存在淺拷貝與深拷貝;淺拷貝之間只是成員之間的粗暴賦值,解決不了結構體中存在指針時,兩個指針成員之間只是簡單的地址賦值,當一個結構體變量指針成員釋放空間時,另一個結構體變量指針成員訪問的空間也就消失了,兩結構體變量之間相互影響很大。深拷貝則是存在指針變量時,首先為指針變量各自分配空間,然后再進行拷貝,每個結構體指針變量指向的的空間時相互獨立的。
堆中創建結構體變量
struct man{ char name[20]; int age; }; struct student{ char * name; int age; } int main(){ struct man st;//name在棧里邊 struct man *st1 = malloc(sizeof(struct man));//name在堆里邊 struct man *p = malloc(sizeof(struct student));//name在堆里邊 p->name = malloc(20); strcpy(p->name,”lfsdi”);//申請一個堆空間,st1->name在堆里,但是一個野指針 st1->age = 20; free(p->name); free(p);//如果結構體變量里含有指針,注意free的先后順序,如果先free p,則p堆已經釋放了,就找不到p->name的首地址 }
函數的參數為結構體變量
struct man{ char name[20]; int age; }; printf_student(struct student st){//st是形參,函數調用的時候,在棧里面有一個淺拷貝的過程,如果里邊某個成員為數組較大,會出現一個數組拷貝的過程,會消耗大量時間,不利於優化程序 printf(“%s,%d\n”,st.name,st.age); } printf_student(const struct student *st) {//st =&st //只是一個簡單的結構體地址賦值,效率遠遠高於上面的,形參很少直接用一個結構體變量,一般放結構體指針 printf(“%s,%d\n”,st->name,st->age); }
聯合體
聯合union是一個能在同一個存儲空間存儲不同類型的數據,聯合體所占的內存長度等於其最長成員的長度,所以代碼效率很高。聯合體雖然有多個成員,但同一時間只能存放其中一種。
union A{ int a1; short a2; char a3; char *p; };//只占4個字節 int main(){ union A a; a1 =1; a3 =10; a1 =0; //之后a.a3的值為0 a.p = malloc(10);//假設這塊堆的內存編號為0x12345C a.a1 = 0;//p的值也成了0 free(a.p); return 0; }
枚舉類型
可以使用枚舉聲明代表整數常量的符號名稱,關鍵字enum創建一個新的枚舉類型,實際上enum常量是int類型的,可以增加代碼的可讀性。
struct A2{ char name[20]; unsigned char age; unsigned char sex; } enum color{red,blue,yellow,green,black}; enum sex {man,woman}; void main(){ struct A2 st; enum sex s; s =man; strcpy(st.name,”znfysry”); st.sex = man;//man就是一個整形的常量,不能做左值,常量也不能取地址 st.age = 0; }
每一個枚舉都有默認值0,1, 2,3,4,5,6……… 可以自己設置每個成員的值,enum color{red = 100,blue =12,red = 58,yellow,black,white};100,12,58,59,60,61.....,100 在系統內是由CPU產生的一個立即數,不能取地址, “hello”在內存的常量區里,可以取地址,int a =100;//CPU生成一個立即數,在棧中分配一個4個字節的空間,然后把這個空間的值設置為100,enum在編譯完成后只是一個不存在於內存中的立即數,不能取地址。
typdef數據類型
typdef數據類型是一種高級數據類型,它能使某種類型創建自己的名字。僅限於數據類型,不能是表達式或具體的值。
struct student{ char name[20]; unsigned char age; unsigned char sex; } typedef struct student M;//M就類似於int,就是一種數據類型 typedef unsigned char BYTE;//多了一種數據類型叫byte.可以提高代碼的維護性 int main(){ M m; BYTE a; return 0; }
typdef數據類型不是一種必須使用的數據類型,但是使用typedef主要目的是為了讓程序的可讀性更高,方便代碼的維護,在代碼十分龐大的時候這種數據類型就顯得十分必要。
#ifndef UNICODE //方便維護代碼 typedef wchar_t TCHAR #else typedef char TCHAR #endif void main(){ TCHAR a1; }