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