1. 程式人生 > >【C語言】結構體

【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 = {203.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.當一個結構包含兩個位段,第二個位段成員比較大,無法容忍於第一個位段剩餘的位時,時捨棄剩餘的位還是利用,這是不確定的。 位段與結構體相比,可以達到相同的效果,但是可以節省空間,並有跨平臺問題存在。