淺談C語言結構體
前言
在C語言中,除了內置的許多數據類型,C語言還為我們提供了自定義的數據類型,其中就包括結構體這一數據類型。
今天就讓我們來學習一下與結構體相關的知識吧!

什么是結構體
首先我們要知道,什么是結構體?
在現(xiàn)實生活中,每一個事物都是復雜的,擁有許多的屬性,為了表示這些屬性,我們不可能用單一的數據類型來表示。
例如:一只貓具有的屬性有:年齡、體重、名字、品種等等。
為了描述這只貓的屬性,我們可以用整型變量來記錄年齡、用浮點型變量來記錄體重,用字符數組來記錄名字和體重等等。
但是,這就需要我們創(chuàng)建多個變量來表示這只貓。就以上面的栗子來說,如果我們只需要表示一只貓的屬性,就需要創(chuàng)建四次變量,這倒也可以接收,但如果我們要表示100只貓呢?我們就不可能每一只貓都單獨的創(chuàng)建四次變量了吧?
我們可以知道,每一次貓的屬性基本上都是相同的,無外乎年齡、體重、名字、品種等等,那么我們可不可把這些屬性抽象出來呢?答案是肯定的,這就是我們的結構體!
結構體類型的聲明
結構體聲明的格式如下:
struct tag
{
member-list;
}variable-list;
代碼演示如下
struct Stu
{
char name[20];//名字
int age;//年齡
char sex[5];//性別
char id[20];//學號
};//分號不能丟
結構體有時候還可以進行特殊的聲明
例如,不完全聲明
//匿名結構體類型
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], *p;
上面的兩個結構在聲明的時候省略掉了結構體標簽(tag)。
那么,問題來了?
//在上面代碼的基礎上,下面的代碼合法嗎? p = &x;
答案是否定的。
編譯器會把上面的兩個聲明當成完全不同的兩個類型。
所以是非法的。
結構的自引用
在結構中包含一個類型為該結構本身的成員是否可以呢?
//代碼1
struct Node
{
int data;
struct Node next;
};
//可行否?
上述代碼是否可行呢?
答案也是否定的,結構體內部的成員變量不可包含結構體本身,否則就會無限套娃,死循環(huán)下去了。
當我們想要結構體里面還能有一個變量能夠指向一個結構體的話,我們可以利用指針。
正確的自引用方式如下:
//代碼2
struct Node
{
int data;
struct Node* next;
};
結構體變量的定義和初始化
有了結構體類型,那如何定義變量,其實很簡單。
我們可以先聲明結構體類型,然后直接再后面定義結構體。
也可以先聲明結構體類型,然后單獨的定義結構體。
我們可以再定義結構體的同時對其進行初始化。
還可以先單獨定義一個結構體,然后再單獨對其初始化。
具體看下面的代碼演示:
struct Point
{
int x;
int y;
}p1; //聲明類型的同時定義變量p1
struct Point p2; //定義結構體變量p2
//初始化:定義變量的同時賦初值。
struct Point p3 = {x, y};
struct Stu //類型聲明
{
char name[15];//名字
int age; //年齡
};
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = {10, {4,5}, NULL}; //結構體嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//結構體嵌套初始化
結構體的使用
結構體的使用方法有兩種:一是結構體直接使用成員變量,二是利用結構體指針來引用成員變量。
具體看下面的代碼演示:
struct Stu
{
double sco;//分數
int age;//年齡
char sex;//性別
};//分號不能丟
int main(){
struct Stu s1;
struct Stu* p;
p = &s1;
//結構體直接訪問成員變量
s1.age = 10;
s1.sco = 88.8;
s1.sex = 'N';
//通過結構體指針來訪問
p->age = 20;
p->sco = 95.5;
p->sex = 'W';
return 0;
}
結構體內存對齊
在我們已經掌握了結構體的基本使用之后,我們來探討一個更加深入的問題。
如何計算結構體的大小?
struct S1
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S1));
小伙伴們你們認為上述代碼的結構是多少呢?
如果你簡單的認為是:1+4+1 = 6,那么你就錯啦!
這段代碼在VS編譯器的默認情況下的運行結果為:12
為什么會這樣呢?
這是因為,在內存中,存在著結構體對齊!
結構體對齊規(guī)則如下:
第一個成員始終在與結構體變量偏移量為0處。其他成員變量要對齊到對齊數的整數倍的地址處。對齊數 = 編譯器默認的一個對齊數 與 該成員變量本身大小的較小值。結構體總大小為最大對齊數(每個成員變量都有一個對齊數)的整數倍。如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍。
提示:VS編譯器默認的對齊數為8
我們回看上面那段代碼,并畫圖分析。

注意
我們可以修改編譯器的默認對齊數,我們只需要加上這樣一句代碼就可以
//這里的數字就是我們想要修改稱為的大小 //例如這里我們就修改稱為了4 #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 函數哪個好些?
答案顯然是printf2函數。
原因如下:
函數傳參的時候,參數是需要壓棧,會有時間和空間上的系統(tǒng)開銷。如果傳遞一個結構體對象的時候,結構體過大,參數壓棧的的系統(tǒng)開銷比較大,所以會導致性能的下降。
因此
我們在結構體傳參的時候,需要傳結構體的地址。
總結
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注腳本之家的更多內容!
相關文章
C++實現(xiàn)CreatThread函數主線程與工作線程交互的方法
這篇文章主要介紹了C++實現(xiàn)CreatThread函數主線程與工作線程交互的方法,是Windows應用程序設計中非常實用的方法,需要的朋友可以參考下2014-10-10

