淺談C語言結(jié)構(gòu)體
前言
在C語言中,除了內(nèi)置的許多數(shù)據(jù)類型,C語言還為我們提供了自定義的數(shù)據(jù)類型,其中就包括結(jié)構(gòu)體這一數(shù)據(jù)類型。
今天就讓我們來學(xué)習(xí)一下與結(jié)構(gòu)體相關(guān)的知識吧!
什么是結(jié)構(gòu)體
首先我們要知道,什么是結(jié)構(gòu)體?
在現(xiàn)實(shí)生活中,每一個事物都是復(fù)雜的,擁有許多的屬性,為了表示這些屬性,我們不可能用單一的數(shù)據(jù)類型來表示。
例如:一只貓具有的屬性有:年齡、體重、名字、品種等等。
為了描述這只貓的屬性,我們可以用整型變量來記錄年齡、用浮點(diǎn)型變量來記錄體重,用字符數(shù)組來記錄名字和體重等等。
但是,這就需要我們創(chuàng)建多個變量來表示這只貓。就以上面的栗子來說,如果我們只需要表示一只貓的屬性,就需要創(chuàng)建四次變量,這倒也可以接收,但如果我們要表示100只貓呢?我們就不可能每一只貓都單獨(dú)的創(chuàng)建四次變量了吧?
我們可以知道,每一次貓的屬性基本上都是相同的,無外乎年齡、體重、名字、品種等等,那么我們可不可把這些屬性抽象出來呢?答案是肯定的,這就是我們的結(jié)構(gòu)體!
結(jié)構(gòu)體類型的聲明
結(jié)構(gòu)體聲明的格式如下:
struct tag { member-list; }variable-list;
代碼演示如下
struct Stu { char name[20];//名字 int age;//年齡 char sex[5];//性別 char id[20];//學(xué)號 };//分號不能丟
結(jié)構(gòu)體有時候還可以進(jìn)行特殊的聲明
例如,不完全聲明
//匿名結(jié)構(gòu)體類型 struct { int a; char b; float c; }x; struct { int a; char b; float c; }a[20], *p;
上面的兩個結(jié)構(gòu)在聲明的時候省略掉了結(jié)構(gòu)體標(biāo)簽(tag)。
那么,問題來了?
//在上面代碼的基礎(chǔ)上,下面的代碼合法嗎? p = &x;
答案是否定的。
編譯器會把上面的兩個聲明當(dāng)成完全不同的兩個類型。
所以是非法的。
結(jié)構(gòu)的自引用
在結(jié)構(gòu)中包含一個類型為該結(jié)構(gòu)本身的成員是否可以呢?
//代碼1 struct Node { int data; struct Node next; }; //可行否?
上述代碼是否可行呢?
答案也是否定的,結(jié)構(gòu)體內(nèi)部的成員變量不可包含結(jié)構(gòu)體本身,否則就會無限套娃,死循環(huán)下去了。
當(dāng)我們想要結(jié)構(gòu)體里面還能有一個變量能夠指向一個結(jié)構(gòu)體的話,我們可以利用指針。
正確的自引用方式如下:
//代碼2 struct Node { int data; struct Node* next; };
結(jié)構(gòu)體變量的定義和初始化
有了結(jié)構(gòu)體類型,那如何定義變量,其實(shí)很簡單。
我們可以先聲明結(jié)構(gòu)體類型,然后直接再后面定義結(jié)構(gòu)體。
也可以先聲明結(jié)構(gòu)體類型,然后單獨(dú)的定義結(jié)構(gòu)體。
我們可以再定義結(jié)構(gòu)體的同時對其進(jìn)行初始化。
還可以先單獨(dú)定義一個結(jié)構(gòu)體,然后再單獨(dú)對其初始化。
具體看下面的代碼演示:
struct Point { int x; int y; }p1; //聲明類型的同時定義變量p1 struct Point p2; //定義結(jié)構(gòu)體變量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}; //結(jié)構(gòu)體嵌套初始化 struct Node n2 = {20, {5, 6}, NULL};//結(jié)構(gòu)體嵌套初始化
結(jié)構(gòu)體的使用
結(jié)構(gòu)體的使用方法有兩種:一是結(jié)構(gòu)體直接使用成員變量,二是利用結(jié)構(gòu)體指針來引用成員變量。
具體看下面的代碼演示:
struct Stu { double sco;//分?jǐn)?shù) int age;//年齡 char sex;//性別 };//分號不能丟 int main(){ struct Stu s1; struct Stu* p; p = &s1; //結(jié)構(gòu)體直接訪問成員變量 s1.age = 10; s1.sco = 88.8; s1.sex = 'N'; //通過結(jié)構(gòu)體指針來訪問 p->age = 20; p->sco = 95.5; p->sex = 'W'; return 0; }
結(jié)構(gòu)體內(nèi)存對齊
在我們已經(jīng)掌握了結(jié)構(gòu)體的基本使用之后,我們來探討一個更加深入的問題。
如何計算結(jié)構(gòu)體的大?。?/p>
struct S1 { char c1; int i; char c2; }; printf("%d\n", sizeof(struct S1));
小伙伴們你們認(rèn)為上述代碼的結(jié)構(gòu)是多少呢?
如果你簡單的認(rèn)為是:1+4+1 = 6,那么你就錯啦!
這段代碼在VS編譯器的默認(rèn)情況下的運(yùn)行結(jié)果為:12
為什么會這樣呢?
這是因?yàn)?,在?nèi)存中,存在著結(jié)構(gòu)體對齊!
結(jié)構(gòu)體對齊規(guī)則如下:
第一個成員始終在與結(jié)構(gòu)體變量偏移量為0處。其他成員變量要對齊到對齊數(shù)的整數(shù)倍的地址處。對齊數(shù) = 編譯器默認(rèn)的一個對齊數(shù) 與 該成員變量本身大小的較小值。結(jié)構(gòu)體總大小為最大對齊數(shù)(每個成員變量都有一個對齊數(shù))的整數(shù)倍。如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體對齊到自己的最大對齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整體大小就是所有最大對齊數(shù)(含嵌套結(jié)構(gòu)體的對齊數(shù))的整數(shù)倍。
提示:VS編譯器默認(rèn)的對齊數(shù)為8
我們回看上面那段代碼,并畫圖分析。
注意
我們可以修改編譯器的默認(rèn)對齊數(shù),我們只需要加上這樣一句代碼就可以
//這里的數(shù)字就是我們想要修改稱為的大小 //例如這里我們就修改稱為了4 #pragma pack(4)
以上就是結(jié)構(gòu)體對齊的相關(guān)知識,由于這一部分內(nèi)容比較難,所以小伙伴們一定要多加練習(xí)哦!
結(jié)構(gòu)體傳參
我們先看一段代碼
struct S { int data[1000]; int num; }; struct S s = {{1,2,3,4}, 1000}; //結(jié)構(gòu)體傳參 void print1(struct S s) { printf("%d\n", s.num); } //結(jié)構(gòu)體地址傳參 void print2(struct S* ps) { printf("%d\n", ps->num); } int main() { print1(s); //傳結(jié)構(gòu)體 print2(&s); //傳地址 return 0; }
上面的 print1 和 print2 函數(shù)哪個好些?
答案顯然是printf2函數(shù)。
原因如下:
函數(shù)傳參的時候,參數(shù)是需要壓棧,會有時間和空間上的系統(tǒng)開銷。如果傳遞一個結(jié)構(gòu)體對象的時候,結(jié)構(gòu)體過大,參數(shù)壓棧的的系統(tǒng)開銷比較大,所以會導(dǎo)致性能的下降。
因此
我們在結(jié)構(gòu)體傳參的時候,需要傳結(jié)構(gòu)體的地址。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
基于C語言實(shí)現(xiàn)計算生辰八字五行的示例詳解
生辰八字,簡稱八字,是指一個人出生時的干支歷日期;年月日時共四柱干支,每柱兩字,合共八個字。這篇文章主要介紹了C語言實(shí)現(xiàn)計算生辰八字五行的示例代碼,需要的可以參考一下2023-03-03C++調(diào)用matlab函數(shù)的實(shí)例
這篇文章主要介紹了C++調(diào)用matlab函數(shù)的方法,包括封裝matlab函數(shù),編譯matlab函數(shù)及C++環(huán)境配置,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08C++實(shí)現(xiàn)CreatThread函數(shù)主線程與工作線程交互的方法
這篇文章主要介紹了C++實(shí)現(xiàn)CreatThread函數(shù)主線程與工作線程交互的方法,是Windows應(yīng)用程序設(shè)計中非常實(shí)用的方法,需要的朋友可以參考下2014-10-10C語言實(shí)現(xiàn)簡單餐飲管理與點(diǎn)餐系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)簡單餐飲管理與點(diǎn)餐系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05QT使用QML實(shí)現(xiàn)地圖繪制虛線的示例代碼
QML提供了MapPolyline用于在地圖上繪制線段,這篇文章主要為大家詳細(xì)介紹了QT如何使用QML實(shí)現(xiàn)在地圖上繪制虛線,需要的小伙伴可以參考一下2023-07-07C++&&Opencv實(shí)現(xiàn)控制臺字符動畫的方法
這篇文章主要介紹了C++&&Opencv實(shí)現(xiàn)控制臺字符動畫的方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07解析linux 文件和目錄操作的相關(guān)函數(shù)
以下是對linux中文件和目錄操作的相關(guān)函數(shù)進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過來參考下2013-08-08C語言鏈表實(shí)現(xiàn)工資管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語言鏈表實(shí)現(xiàn)工資管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-02-02