【C語言】結構體
1.結構體
a.概念:結構是一些值得集合,這些值成為成員變數。結構的每個成員可以是不同型別的變數。
b.宣告: eg宣告一個學生資訊的結構體。
struct Stu
{
char name[20];
short int age;
char sex[5];
}stu1,stu2;//stu1,stu2為變數列表,並且為全域性變數,分好不能丟
int main()
{
stu1.age=10;
printf("%d\n",stu1.age);
return 0;
}
為了使用方便,並且避免全域性變數的使用,一般如下使用:
typedef struct Stu
{
char name[20];
short int age;
char sex[5];
}Stu;//分號不能丟
int main()
{
Stu stu1;
printf("%d\n",stu1.age);
return 0;
}
宣告結構體時並未佔用記憶體空間,當使用Stu時才會佔用記憶體空間。(即例項化時才會佔用記憶體)。
匿名結構體(無型別名)
struct
{
char name[20];
short int age;
char sex[5];
}S;//分號不能丟
struct
{
char name[20 ];
short int age;
char sex[5];
}*pS;//分號不能丟
雖然成員內容相同,但是編譯器會把上面的兩個聲明當做完全不同的兩個型別,即程式碼pS=&S;不合法。
c.結構成員的訪問
.的訪問方式 (針對結構體變數)
struct Stu
{
char name[20];
short int age;
char sex[5];
};//分號不能丟
int main()
{
struct Stu s;
strcpy(s.name."fangqi");
s.age=29;
strcpy(s.sex,"男");
}
->的方式 (針對結構體指標,找它指向物件的成員)
struct Stu
{
char name[20];
short int age;
char sex[5];
};//分號不能丟
int main()
{
struct Stu s;
struct Stu* ps=&s;
strcpy(s.name."fangqi");
s.age=29;
strcpy(s.sex,"男");
printf("%s\",ps->name);
printf("%d\n",ps->age);
}
d.結構體的自引用
struct Node
{
int data;
struct Node* n;
};
e.結構體的不完整宣告
struct A;//先定義一個不完整宣告,解決下面問題。
struct A
{
int a;
struct B* b;
};
struct B
{
int b;
struct A* a;
};
f.結構體的定義與初始化
struct S
{
int a;
double a;
char a[10];
}s1;//建立型別時直接定義,s1為全域性變數
//struct S s2;該s2也為全域性變數
int main()
{
struct S s2 = {20,3.14,"hehe"};//型別建立完成再定義,s2為區域性變數,{}用來初始化
return 0;
}
g.結構體記憶體對齊
1.第一個成員在與結構體變數偏移量為0的地址處。 2.其他成員呢變數要對齊到某個數字(對齊數)的整數倍的地址處。對齊數=編譯器預設的一個對齊數與該成員大小的較小值。 (vs中預設的值為8 Linux中的預設值為4) 3.結構體總大小為最大的對齊數(每個成員變數都有一個對齊數)的整數倍。 4.如果嵌套了結構體的情況,巢狀的結構體對齊到自己的最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數(含巢狀結構體的對齊數)的整數倍。
//offsetof();可以測試偏移量 標頭檔案#include
//define offsetof(s,m) (size_t)&(((s*)0)->m)
struct s1
{
int a;
char c1;
char c2;
};
int main()
{
printf("%d ", offsetof(struct S, a));
printf("%d ", offsetof(struct S, c1));
printf("%d ", offsetof(struct S, c2));
system("pause");
return 0;
}
執行結果為:0 4 5
練習:
1.
struct s1
{
int a;//4
char c1;//1
char c2;//1
};
int main()
{
printf("%d",sizeof(struct s1);//輸出為8
}
由記憶體對齊規則第一點得0~3為a的記憶體儲存,由第二點c1對齊數為(8與1)的較小值為1,得4為c1的儲存,c2同c1,由第三點得結構體大小必須為成員最大對齊數整數倍即為4的整數倍,得該結構體位元組為8。 2.
struct S1
{
double d;//0~7
char c;//8
int i;//9~12
};
struct S2
{
char c1;//0
struct S1 s1;//8~23
double d;24~31
};
int main()
{
printf("%d",sizeof(struct S2);//輸出為32
}
由記憶體對齊規則第一點得0為c1的記憶體儲存,由第四點得S1的最大對齊數為8則S1為8~23,由第二點得d為24~31,所以S2位元組為32。
為什麼存在記憶體對齊?
1.移植原因(平臺原因) 不是所有的硬體平臺都能訪問任意地址上的任意資料;某些硬體平臺只能在某些地址處取某些特定型別的資料,否則丟擲硬體異常。 2.效能原因 資料結構(尤其是棧)應該儘可能地在自然邊界上對齊。 原因在於,為了訪問未對齊的記憶體,處理器需要做兩次記憶體訪問;而對齊的記憶體訪問僅需要一次訪問。(犧牲空間換取時間) 所以設計結構體的時候儘量將空間小的成員集中在一起。
設定預設對齊數
#pragma pack(設定對齊數)
結構體
#pragma pack()
eg:
#pragma pack(4)
struct S
{
char c;
double b;
char f;
};
#pragma pack()
int main()
{
printf("%d ", sizeof(struct S));
system("pause");
return 0;
}
h.結構體傳參
比較值傳遞與地址傳遞 GetTickCount();函式是#include
#include <stdio.h>
#include <stdlib.h>
#include<Windows.h>
struct S
{
char name[20];
int age;
};
void print(struct S s)
{
printf("%s %d\n", s.name, s.age);
}
void print2(const struct S* ps)
{
printf("%s %d\n", ps->name, ps->age);
}
int main()
{
struct S stu = { "zhang", 20 };
int i = 0;
int start1 = 0;
int end1 = 0;
int start2 = 0;
int end2 = 0;
start1 = GetTickCount();
for (i = 0; i < 1000;i++)
print(stu);
end1 = GetTickCount();
start2 = GetTickCount();
for (i = 0; i < 1000; i++)
print2(&stu);//效率高
end2 = GetTickCount();
printf("%d\n", end2 - start2);
printf("%d\n", end1 - start1);
system("pause");
return 0;
}
執行結果為:傳值為438毫秒,傳地址為203毫秒所以選擇地址傳參
i位段
1.位段的成員必須是 int 、unsigned int或者 signed int. 2.位段的成員名後面有一個冒號和一個數字
eg:
struct A
{
int _a:2;//2就是位指的是2個位元位
int _b:5;
int _c:10;
int _d:30;
};
A就是一個位段型別。 A的大小是8個位元組Beacuse:為a建立4個位元組32個位元位,使用2個還有30個,b使用5個,c使用10個,d不夠再建立4個位元組。c語言未規定是否使用上面剩餘的還是浪費,(vs是浪費的)所以位段不跨平臺。
位段的記憶體分配 1.位段的成員可以是 int, unsigned int,signed int或者是char(屬於整型家族)型別 2.位段的空間上是按照需要以4個位元組(int)或者1個位元組(char)的方法來開闢的 3.位段涉及很多不確定因素,位段是不跨平臺的,注重可移植的程式應該避免使用位段。
位段的跨平臺問題 1.int位段被當成有符號數還是無符號數是不確定的。 2.位段中最大位的數目不能確定。(16位機器最大16,32為機器最大32,寫成27,在16位機器會出問題) 3.位段中的成員在記憶體中從左向右分配,還是從右向左分配標準尚未定義。 4.當一個結構包含兩個位段,第二個位段成員比較大,無法容忍於第一個位段剩餘的位時,時捨棄剩餘的位還是利用,這是不確定的。 位段與結構體相比,可以達到相同的效果,但是可以節省空間,並有跨平臺問題存在。