欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C/C++自定義類型結(jié)構(gòu)體全解析

 更新時(shí)間:2025年05月15日 09:45:41   作者:鳳年徐  
c語(yǔ)言有內(nèi)置類型(char?short?int?long?flaot?double?long?double),也有自定義類型—結(jié)構(gòu)體(struct)?枚舉(enum)?聯(lián)合體(union)?本文介紹結(jié)構(gòu)體,感興趣的朋友跟隨小編一起看看吧

前言

集成開發(fā)環(huán)境為vs2022

c語(yǔ)言有內(nèi)置類型(char short int long flaot double long double),也有自定義類型—結(jié)構(gòu)體(struct) 枚舉(enum) 聯(lián)合體(union) 本篇幅介紹結(jié)構(gòu)體

自定義類型:結(jié)構(gòu)體

1.結(jié)構(gòu)體類型的聲明

前?我們?cè)趯W(xué)習(xí)操作符的時(shí)候,已經(jīng)學(xué)習(xí)了結(jié)構(gòu)體的知識(shí),這?稍微復(fù)習(xí)?下。

1.1 結(jié)構(gòu)體回顧

結(jié)構(gòu)是?些值的集合,這些值稱為成員變量。結(jié)構(gòu)的每個(gè)成員可以是不同類型的變量。

1.1.1 結(jié)構(gòu)的聲明

struct tag//標(biāo)簽名
{
 member-list;//成員 1個(gè)或多個(gè)
}variable-list;//變量列表

例如描述?個(gè)學(xué)?:

struct Stu
{
 char name[20];//名字 
 int age;//年齡 
 char sex[5];//性別 
 char id[20];//學(xué)號(hào) 
}; //分號(hào)不能丟 
struct Book b2;//全局變量
int main()
{
 struct Book b1;//局部變量
 return 0;
}

1.1.2 結(jié)構(gòu)體變量的創(chuàng)建和初始化

#include <stdio.h>
struct Stu
{
 char name[20];//名字 
 int age;//年齡 
 char sex[5];//性別 
 char id[20];//學(xué)號(hào) 
};
int main()
{
 //按照結(jié)構(gòu)體成員的順序初始化 
 struct Stu s = { "張三", 20, "男", "20230818001" };
 printf("name: %s\n", s.name);
 printf("age : %d\n", s.age);
 printf("sex : %s\n", s.sex);
 printf("id : %s\n", s.id);
 //按照指定的順序初始化 
 struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex = 
"?" };
 printf("name: %s\n", s2.name);
 printf("age : %d\n", s2.age);
 printf("sex : %s\n", s2.sex);
 printf("id : %s\n", s2.id);
 return 0;
}

1.2 結(jié)構(gòu)的特殊聲明

在聲明結(jié)構(gòu)的時(shí)候,可以不完全的聲明。

?如:

//匿名結(jié)構(gòu)體類型 
struct//這里不寫名字
{
 int a;
 char b;
 float c;
}s;//可以在這初始化
//}s={'x',100.3.14};
int main()
{
    printf("%c %d %lf",s.c,s.i,s.d);
}

匿名結(jié)構(gòu)體也可以重新命名

typedef struct
{
 char c;
 int i;
 double d;
}s;

上?的兩個(gè)結(jié)構(gòu)在聲明的時(shí)候省略掉了結(jié)構(gòu)體標(biāo)簽(tag)。 那么問題來(lái)了?

//在上?代碼的基礎(chǔ)上,下?的代碼合法嗎? 
p = &x;

警告:

編譯器會(huì)把上?的兩個(gè)聲明當(dāng)成完全不同的兩個(gè)類型,所以是非法的。

匿名的結(jié)構(gòu)體類型,如果沒有對(duì)結(jié)構(gòu)體類型重命名的話,基本上只能使??次。

1.3 結(jié)構(gòu)的自引用

在結(jié)構(gòu)中包含?個(gè)類型為該結(jié)構(gòu)本?的成員是否可以呢?

?如,定義?個(gè)鏈表的節(jié)點(diǎn)

在這之前先講一下鏈表

數(shù)據(jù)結(jié)構(gòu)–其實(shí)是數(shù)據(jù)在內(nèi)存中的存儲(chǔ)和組織的結(jié)構(gòu) 數(shù)據(jù)有多種

線性數(shù)據(jù)結(jié)構(gòu):順序表,鏈表,棧,隊(duì)列

順序表–數(shù)組

鏈表

//定義一個(gè)鏈表節(jié)點(diǎn)
struct Node
{
 int data;
 struct Node next;
};

上述代碼正確嗎?如果正確,那 sizeof(struct Node) 是多少?

仔細(xì)分析,其實(shí)是不?的,因?yàn)?個(gè)結(jié)構(gòu)體中再包含?個(gè)同類型的結(jié)構(gòu)體變量,這樣結(jié)構(gòu)體變量的? ?就會(huì)?窮的?,是不合理的。

正確的?引??式:

struct Node{
 int data;//數(shù)據(jù)
 struct Node* next;//指針
};

在結(jié)構(gòu)體?引?使?的過程中,夾雜了 typedef 對(duì)匿名結(jié)構(gòu)體類型重命名,也容易引?問題,看看 下?的代碼,可?嗎?

typedef struct
{
 int data;
 Node* next;
}Node;

答案是不?的,因?yàn)镹ode是對(duì)前?的匿名結(jié)構(gòu)體類型的重命名產(chǎn)?的,但是在匿名結(jié)構(gòu)體內(nèi)部提前使 ?Node類型來(lái)創(chuàng)建成員變量,這是不?的。

匿名結(jié)構(gòu)體類型不能實(shí)現(xiàn)結(jié)構(gòu)體的自引用

解決?案如下:定義結(jié)構(gòu)體不要使用匿名結(jié)構(gòu)體了

typedef struct Node
{
 int data;
 struct Node* next;
}Node;
//上述代碼等價(jià)于下邊代碼
struct Node
{
 int data;
 struct Node* next;
}
typedef struct Node Node;

2.結(jié)構(gòu)體內(nèi)存對(duì)齊

我們已經(jīng)掌握了結(jié)構(gòu)體的基本使?了。

現(xiàn)在我們深?討論?個(gè)問題:計(jì)算結(jié)構(gòu)體的??。

這也是?個(gè)特別熱?的考點(diǎn): 結(jié)構(gòu)體內(nèi)存對(duì)?

2.1 對(duì)?規(guī)則

?先得掌握結(jié)構(gòu)體的對(duì)?規(guī)則:

1.結(jié)構(gòu)體的第1個(gè)成員對(duì)?到和結(jié)構(gòu)體變量起始位置偏移量為0的地址處

2.從第2個(gè)成員變量開始,都要對(duì)?到某個(gè)對(duì)?數(shù)的整數(shù)倍的地址處。

對(duì)?數(shù)=編譯器默認(rèn)的?個(gè)對(duì)?數(shù)與該成員變量??的較?值。

VS 中默認(rèn)的值為 8

Linux中g(shù)cc沒有默認(rèn)對(duì)?數(shù),對(duì)?數(shù)就是成員??的??

3.結(jié)構(gòu)體總大小為最大對(duì)齊數(shù)(結(jié)構(gòu)體中每個(gè)成員變量都有?個(gè)對(duì)?數(shù),所有對(duì)?數(shù)中最?的)的 整數(shù)倍。

4.如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體成員對(duì)?到??的成員中最?對(duì)?數(shù)的整數(shù)

//練習(xí)1 
struct S1
{       //   默認(rèn)   對(duì)齊數(shù)
 char c1;// 1 8     1
 int i;//   4 8     4
 char c2;// 1 8     1
};
printf("%d\n", sizeof(struct S1));
//練習(xí)2 
struct S2
{
 char c1;
 char c2;
 int i;
};
printf("%d\n", sizeof(struct S2));
//練習(xí)3 
struct S3
{
 double d;
 char c;
 int i;
};
printf("%d\n", sizeof(struct S3));
//練習(xí)4-結(jié)構(gòu)體嵌套問題 
struct S4
{
 char c1;
 struct S3 s3;
 double d;
};
printf("%d\n", sizeof(struct S4));

2.2 為什么存在內(nèi)存對(duì)齊?

?部分的參考資料都是這樣說的:

1. 平臺(tái)原因(移植原因):

不是所有的硬件平臺(tái)都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺(tái)只能在某些地址處取某些特定 類型的數(shù)據(jù),否則拋出硬件異常。

2.性能原因

數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在?然邊界上對(duì)?。原因在于,為了訪問未對(duì)?的內(nèi)存,處理器需要 作兩次內(nèi)存訪問;?對(duì)?的內(nèi)存訪問僅需要?次訪問。假設(shè)?個(gè)處理器總是從內(nèi)存中取8個(gè)字節(jié),則地 址必須是8的倍數(shù)。如果我們能保證將所有的double類型的數(shù)據(jù)的地址都對(duì)?成8的倍數(shù),那么就可以 ??個(gè)內(nèi)存操作來(lái)讀或者寫值了。否則,我們可能需要執(zhí)?兩次內(nèi)存訪問,因?yàn)閷?duì)象可能被分放在兩 個(gè)8字節(jié)內(nèi)存塊中。

總體來(lái)說:結(jié)構(gòu)體的內(nèi)存對(duì)?是拿空間來(lái)?yè)Q取時(shí)間的做法。

例如

struct S
{
  char c;//1
  int i;//4 
};

那在設(shè)計(jì)結(jié)構(gòu)體的時(shí)候,我們既要滿?對(duì)?,?要節(jié)省空間,如何做到:

讓占?空間?的成員盡量集中在?起

//例如: 
struct S1
{
 char c1;
 int i;
 char c2;
};
struct S2
{
 char c1;
 char c2;
 int i;
};

S1 和 S2 類型的成員?模?樣,但是 S1 和 S2 所占空間的??有了?些區(qū)別。

2.3 修改默認(rèn)對(duì)?數(shù)

#pragma 這個(gè)預(yù)處理指令,可以改變編譯器的默認(rèn)對(duì)?數(shù)。

#include <stdio.h>
#pragma pack(1)//設(shè)置默認(rèn)對(duì)?數(shù)為1  一般是2的次方數(shù) linux中不能改
struct S
{
 char c1;
 int i;
 char c2;
};
#pragma pack()//取消設(shè)置的對(duì)?數(shù),還原為默認(rèn) 
int main()
{
 //輸出的結(jié)果是什么? 6
 printf("%d\n", sizeof(struct S));
 return 0;
}

結(jié)構(gòu)體在對(duì)??式不合適的時(shí)候,我們可以??更改默認(rèn)對(duì)?數(shù)。

3. 結(jié)構(gòu)體傳參

struct S
{
 int data[1000];//4000字節(jié)
 int num;
};
struct S s = {{1,2,3,4}, 1000};
//結(jié)構(gòu)體傳參 
void print1(struct S s)//s先拷貝,占用內(nèi)存很大
{
    //for循環(huán)打印數(shù)組
 printf("%d\n", s.num);
}
//結(jié)構(gòu)體地址傳參 
void print2(const struct S* ps)
{
 printf("%d\n", ps->num);
 printf("%d\n",ps->data[i]);
}
int main()
{
 print1(s); //傳結(jié)構(gòu)體 
 print2(&s); //傳地址 
 return 0;
}

上?的 print1 和 print2 函數(shù)哪個(gè)好些?

答案是:首選print2函數(shù)

原因:

函數(shù)傳參的時(shí)候,參數(shù)是需要壓棧,會(huì)有時(shí)間和空間上的系統(tǒng)開銷。
如果傳遞?個(gè)結(jié)構(gòu)體對(duì)象的時(shí)候,結(jié)構(gòu)體過?,參數(shù)壓棧的的系統(tǒng)開銷?較?,所以會(huì)導(dǎo)致性能的下降。
結(jié)論:結(jié)構(gòu)體傳參的時(shí)候,要傳結(jié)構(gòu)體的地址。

4.結(jié)構(gòu)體實(shí)現(xiàn)位段

4.1 什么是位段

位段的聲明和結(jié)構(gòu)是類似的,有兩個(gè)不同:

1.位段的成員必須是 int、unsigned int 或signed int ,在C99中位段成員的類型也可以 選擇其他整型家族類型,?如:char。

2.位段的成員名后邊有?個(gè)冒號(hào)?個(gè)數(shù)字。

?如:

struct A
{
 int _a:2;//只占兩個(gè)bit位
 int _b:5;
 int _c:10;
 int _d:30;
};
struct s
{
  int _a;//4字節(jié) 32bit 可以節(jié)省30個(gè)字節(jié)
  int _b;
  int _c;
  int _d;
    //00   0
    //01   1
    //10   2
    //11   3
}

A就是?個(gè)位段類型。

位段是專門用來(lái)節(jié)省內(nèi)存的

那位段A所占內(nèi)存的??是多少?

    // %zd            8字節(jié)
printf("%d\n", sizeof(struct A));//

4.2 位段的內(nèi)存分配

1.位段的成員可以是 int unsigned int signed int 或者是 char 等類型

2.位段的空間上是按照需要以**4個(gè)字節(jié)( int )或者1個(gè)字節(jié)( char )**的?式來(lái)開辟的。

3.位段涉及很多不確定因素,位段是不跨平臺(tái)的,注重可移植的程序應(yīng)該避免使?位段。

//?個(gè)例? 
struct S
{
  char a:3;
  char b:4;
  char c:5;
  char d:4;
};
struct S s = {0};
s.a = 10;//00001010
s.b = 12;//00001100
s.c = 3;//00000011
s.d = 4;//00000100
//空間是如何開辟的? 

在這之前我們先要了解一下內(nèi)存的使用順序

1.申請(qǐng)到的一塊內(nèi)存中,從左向右使用,還是從右向左使用,是不確定的 vs是從右向左

2.剩余空間,不是下一個(gè)成員使用的時(shí)候,是浪費(fèi)呢?還是繼續(xù)使用? vs是浪費(fèi)

4.3 位段的跨平臺(tái)問題

  • int位段被當(dāng)成有符號(hào)數(shù)還是?符號(hào)數(shù)是不確定的。
  • 位段中最?位的數(shù)?不能確定。(16位機(jī)器最?16,32位機(jī)器最?32,寫成27,在16位機(jī)器會(huì)出問題。
  • 位段中的成員在內(nèi)存中從左向右分配,還是從右向左分配,標(biāo)準(zhǔn)尚未定義。
  • 當(dāng)?個(gè)結(jié)構(gòu)包含兩個(gè)位段,第?個(gè)位段成員?較?,?法容納于第?個(gè)位段剩余的位時(shí),是舍棄剩余的位是利?,這是不確定的。
  • 總結(jié):
  • 跟結(jié)構(gòu)相?,位段可以達(dá)到同樣的效果,并且可以很好的節(jié)省空間,但是有跨平臺(tái)的問題存在。

4.4 位段的應(yīng)用

下圖是?絡(luò)協(xié)議中,IP數(shù)據(jù)報(bào)的格式,我們可以看到其中很多的屬性只需要?個(gè)bit位就能描述,這? 使?位段,能夠?qū)崿F(xiàn)想要的效果,也節(jié)省了空間,這樣?絡(luò)傳輸?shù)臄?shù)據(jù)報(bào)??也會(huì)較??些,對(duì)?絡(luò) 的暢通是有幫助的。

4.5 位段使用的注意事項(xiàng)

**位段的?個(gè)成員共有同?個(gè)字節(jié),這樣有些成員的起始位置并不是某個(gè)字節(jié)的起始位置,那么這些位置處是沒有地址的。內(nèi)存中每個(gè)字節(jié)分配?個(gè)地址,?個(gè)字節(jié)內(nèi)部的bit位是沒有地址的。
以不能對(duì)位段的成員使?&操作符,這樣就不能使?scanf直接給位段的成員輸?值,**只能是先輸?放在?個(gè)變量中,然后賦值給位段的成員。

一個(gè)字節(jié)一個(gè)地址

struct A
{
 int _a : 2;
 int _b : 5;
 int _c : 10;
 int _d : 30;
};
int main()
{
 struct A sa = {0};
 scanf("%d", &sa._b);//這是錯(cuò)誤的 
 //正確的?范 
 int b = 0;
 scanf("%d", &b);
 sa._b = b;
 return 0;
}

到此這篇關(guān)于C/C++自定義類型:結(jié)構(gòu)體的文章就介紹到這了,更多相關(guān)C++結(jié)構(gòu)體內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 構(gòu)建mfc窗體的簡(jiǎn)單示例

    構(gòu)建mfc窗體的簡(jiǎn)單示例

    這篇文章主要介紹了構(gòu)建mfc窗體的簡(jiǎn)單示例,需要的朋友可以參考下
    2014-04-04
  • C++產(chǎn)生隨機(jī)數(shù)的實(shí)現(xiàn)代碼

    C++產(chǎn)生隨機(jī)數(shù)的實(shí)現(xiàn)代碼

    本篇文章是對(duì)C++中產(chǎn)生隨機(jī)數(shù)的實(shí)現(xiàn)代碼進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C++ 標(biāo)準(zhǔn)模板庫(kù) STL 順序容器詳解

    C++ 標(biāo)準(zhǔn)模板庫(kù) STL 順序容器詳解

    這篇文章主要介紹了C++ 標(biāo)準(zhǔn)模板庫(kù) STL 順序容器詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-05-05
  • C語(yǔ)言二叉樹層序遍歷

    C語(yǔ)言二叉樹層序遍歷

    這篇文章主要介紹了C語(yǔ)言二叉樹層序遍歷,文章基于C語(yǔ)言的相關(guān)資料展開詳細(xì)的文章內(nèi)容,具有一定的參考價(jià)值,需要的小伙伴可以參考一下,希望對(duì)你的學(xué)習(xí)有所幫助
    2022-04-04
  • C++中的string庫(kù)函數(shù)常見函數(shù)的作用和使用方法

    C++中的string庫(kù)函數(shù)常見函數(shù)的作用和使用方法

    這篇文章主要介紹了C++中的string庫(kù)函數(shù)常見函數(shù)的作用和使用方法,庫(kù)函數(shù)的靈活應(yīng)用是程序員的一大重要技能,本文通過實(shí)例實(shí)例代碼給大家講解的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的停車場(chǎng)管理系統(tǒng)

    C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的停車場(chǎng)管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的停車場(chǎng)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • VSCode無(wú)法打開源文件及無(wú)法打開鏈接庫(kù)文件的解決方法

    VSCode無(wú)法打開源文件及無(wú)法打開鏈接庫(kù)文件的解決方法

    本文主要介紹了VSCode無(wú)法打開源文件及無(wú)法打開鏈接庫(kù)文件的解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • C++ DFS算法實(shí)現(xiàn)走迷宮自動(dòng)尋路

    C++ DFS算法實(shí)現(xiàn)走迷宮自動(dòng)尋路

    這篇文章主要為大家詳細(xì)介紹了C++ DFS算法實(shí)現(xiàn)走迷宮自動(dòng)尋路,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • 一文詳解Qt的QObject類

    一文詳解Qt的QObject類

    Qt的QObject類是Qt框架中的基類,它是所有Qt對(duì)象的父類,本文主要介紹了Qt的QObject類,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-09-09
  • C++ 單鏈表的基本操作(詳解)

    C++ 單鏈表的基本操作(詳解)

    下面小編就為大家?guī)?lái)一篇C++ 單鏈表的基本操作(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧
    2016-12-12

最新評(píng)論