C語言結構體(struct)最全的講解(萬字乾貨)

2021-02-19 21ic電子網

本文內容由「嵌入式ARM」整理自網絡

參考公眾號:我贏職場、0基礎學單片機(森林木)、老九學堂(大雄)、嵌入式時代

(整理本文出於傳播相關技術知識,版權歸原作者所有。)


結構體的定義

結構體(struct)是由一系列具有相同類型或不同類型的數據構成的數據集合,也叫結構。結構體和其他類型基礎數據類型一樣,例如int類型,char類型只不過結構體可以做成你想要的數據類型。以方便日後的使用。在實際項目中,結構體是大量存在的。研發人員常使用結構體來封裝一些屬性來組成新的類型。由於C語言無法操作資料庫,所以在項目中通過對結構體內部變量的操作將大量的數據存儲在內存中,以完成對數據的存儲和操作。
在實際問題中有時候我們需要幾種數據類型一起來修飾某個變量。例如一個學生的信息就需要學號(字符串),姓名(字符串),年齡(整形)等等。這些數據類型都不同但是他們又是表示一個整體,要存在聯繫,那麼我們就需要一個新的數據類型。——結構體,它就將不同類型的數據存放在一起,作為一個整體進行處理。
結構體在函數中的作用不是簡便,其最主要的作用就是封裝。封裝的好處就是可以再次利用。讓使用者不必關心這個是什麼,只要根據定義使用就可以了。

結構體的大小不是結構體元素單純相加就行的,因為我們現在主流的計算機使用的都是32Bit字長的CPU,對這類型的CPU取4個字節的數要比取一個字節要高效,也更方便。所以在結構體中每個成員的首地址都是4的整數倍的話,取數據元素時就會相對更高效,這就是內存對齊的由來。每個特定平臺上的編譯器都有自己的默認「對齊係數」(也叫對齊模數)。程式設計師可以通過預編譯命令#pragmapack(n),n=1,2,4,8,16來改變這一係數,其中的n就是你要指定的「對齊係數」。


1、數據成員對齊規則:結構(struct)(或聯合(union))的數據成員,第一個數據成員放在offset為0的地方,以後每個數據成員的對齊按照#pragmapack指定的數值和這個數據成員自身長度中,比較小的那個進行。
2、結構(或聯合)的整體對齊規則:在數據成員完成各自對齊之後,結構(或聯合)本身也要進行對齊,對齊將按照#pragmapack指定的數值和結構(或聯合)最大數據成員長度中,比較小的那個進行。
3、結合1、2可推斷:當#pragmapack的n值等於或超過所有數據成員長度的時候,這個n值的大小將不產生任何效果。
在C語言中,可以定義結構體類型,將多個相關的變量包裝成為一個整體使用。在結構體中的變量,可以是相同、部分相同,或完全不同的數據類型。在C語言中,結構體不能包含函數。在面向對象的程序設計中,對象具有狀態(屬性)和行為,狀態保存在成員變量中,行為通過成員方法(函數)來實現。C語言中的結構體只能描述一個對象的狀態,不能描述一個對象的行為。在C++中,考慮到C語言到C++語言過渡的連續性,對結構體進行了擴展,C++的結構體可以包含函數,這樣,C++的結構體也具有類的功能,與class不同的是,結構體包含的函數默認為public,而不是private。

結構體聲明

//聲明一個結構體 struct book {char title[MAXTITL];char author[MAXAUTL];float value;};

這個聲明描述了一個由兩個字符數組和一個float變量組成的結構體。但是注意,它並沒有創建一個實際的數據對象,而是描述了一個組成這類對象的元素。因此,我們有時候也將結構體聲明叫做模板,因為它勾勒出數據該如何存儲,並沒有實例化數據對象。1、首先使用關鍵字struct,它表示接下來是一個結構體。2、後面是一個可選的標誌(book),它是用來引用該結構體的快速標記。struct book library;//把library設為一個可以使用book結構體的結構體變量,則library這個變量就包含了其book結構體中的所有元素3、接下來就是一個花括號,括起了結構體成員列表,及每個成員變量,使用的都是其自己的聲明方式來描述,用分號來結束描述;例如:char title[MAXTITL];字符數組就是這樣聲明的,用分號結束;注意:其中每個成員可以使用任何一種C數據結構甚至是其他的結構體,也是可以的;4、在結束花括號後的分號表示結構體設計定義的結束。關於其struct聲明的位置,也就是這段代碼要放到哪裡。同樣這也是具有作用域的。這種聲明如果放在任何函數的外面,那麼則可選標記可以在本文件中,該聲明的後面的所有函數都可以使用。如果這種聲明在某個函數的內部,則它的標記只能在內部使用,並且在其聲明之後;關於我們不斷說的,標記名是可選的,那麼我們什麼時候可以省略,什麼時候一定不能省略呢?如果是上面那種聲明定義的方法,並且想在一個地方定義結構體設計,而在其他地方定義實際的結構體變量,那麼就必須使用標記;可以省略,設計的同時就創建該結構體變量,但是這種設計是一次性的。struct 結構體名(也就是可選標記名){    成員變量;};//使用分號表示定義結束。
C語言結構體定義的三種方式
#include <stdio.h>struct student //結構體類型的說明與定義分開。聲明{int age;  float score; char sex;   };int main (){struct student a={ 20,79,'f'}; printf("年齡:%d 分數:%.2f 性別:%c\n", a.age, a.score, a.sex );return 0;

#include <stdio.h>struct student /*聲明時直接定義*/{int age;  float score;  char sex;   /*這種方式不環保,只能用一次*/} a={21,80,'n'};int main (){printf("年齡:%d 分數:%.2f 性別:%c\n", a.age, a.score, a.sex );

#include <stdio.h>struct   //直接定義結構體變量,沒有結構體類型名。這種方式最爛{int age;float score;char sex;} t={21,79,'f'};int main (){printf("年齡:%d 分數:%f 性別:%c\n", t.age, t.score, t.sex);return 0;}return 0;}}


定義結構體變量

之前我們結構體類型的定義(結構體的聲明)只是告訴編譯器該如何表示數據,但是它沒有讓計算機為其分配空間。我們要使用結構體,那麼就需要創建變量,也就是結構體變量;看到這條指令,編譯器才會創建一個結構體變量library,此時編譯器才會按照book模板為該變量分配內存空間,並且這裡存儲空間都是以這個變量結合在一起的。這也是後面訪問結構體變量成員的時候,我們就要用到結構體變量名來訪問。在結構體聲明中,struct book所起到的作用就像int,,,,等基礎數據類型名作用一樣。定義兩個struct book結構體類型的結構體變量,還定義了一個指向該結構體的指針,其ss指針可以指向s1,s2,或者任何其他的book結構體變量。
struct book{ char … …. ….. }librar;

現在還是回到剛才提及的那個問題,可選標誌符什麼時候可以省略;
struct{    char title[MAXTITL];     char author[MAXAUTL];float value;}library;


//注意這裡不再是定義聲明結構體類型,而是直接創建結構體變量了,這個編譯器會分配內存的;
//這樣的確可以省略標識符也就是結構體名,但是只能使用一次;因為這是;聲明結構體的過程和定義結構體變量的過程和在了一起;並且個成員變量沒有初始化的;
//如果你想多次使用一個結構體模塊,這樣子是行不通的;用typedef定義新類型名來代替已有類型名,即給已有類型重新命名;

typedef int Elem; typedef struct{    int date;        }STUDENT;STUDENT stu1,stu2;

struct 結構體名{成員列表;}變量名列表;struct book{char title[MAXTITL];char author[MAXAUTL];float value;}s1,s2

直接定義結構體類型變量,就是第二種中省略結構體名的情況;這種方式不能指明結構體類型名而是直接定義結構體變量,並且在值定義一次結構體變量時適用,無結構體名的結構體類型是無法重複使用的。也就是說,後面程序不能再定義此類型變量了,除非再寫一次重複的struct。


對於結構體變量的初始化

int a = 0;int array[4] = {1,2,3,4};

也是使用花括號括起來,用逗號分隔的初始化好項目列表,注意每個初始化項目必須要和要初始化的結構體成員類型相匹配。
struct book s1={//對結構體初始化         "yuwen",        "guojiajiaoyun",        22.5        };//要對應起來,用逗號分隔開來,與數組初始化一樣;

加入一點小知識;關於結構體初始化和存儲類時期的問題;如果要初始化一個具有靜態存儲時期的結構體,初始化項目列表中的值必須是常量表達式;

注意如果在定義結構體變量的時候沒有初始化,那麼後面就不能全部一起初始化了;意思就是:

/////////這樣是可以的,在定義變量的時候就初始化了;struct book s1={//對結構體初始化         "guojiajiaoyun",          "yuwen",          22.5    };/////////這種就不行了,在定義變量之後,若再要對變量的成員賦值,那麼只能單個賦值了;struct book s1;    s1={          "guojiajiaoyun",          "yuwen",          22.5  };
只能;
s1.title = "yuwen";...

訪問結構體成員

結構體就像一個超級數組,在這個超級數組內,一個元素可以是char類型,下個元素就可以是flaot類型,再下個還可以是int數組型,這些都是存在的。在數組裡面我們通過下標可以訪問一個數組的各個元素,那麼如何訪問結構體中的各個成員呢?注意,點其結合性是自左至右的,它在所有的運算符中優先級是最高的;例如,s1.title指的就是s1的title部分,s1.author指的就是s1的author部分,s1.value指的就是s1的value部分。然後就可以像字符數組那樣使用s1.title,像使用float數據類型一樣使用s1.value;注意,s1;雖然是個結構體,但是s1.value卻是float型的。因此s1.value就相當於float類型的變量名一樣,按照float類型來使用;例如;printf(「%s\n%s\n%f」,s1.title,s1.author,s1.value);//訪問結構體變量元素注意scanf(「%d」,&s1.value); 這語句存在兩個運算符,&和結構成員運算符點。按照道理我們應該將(s1。value括起來,因為他們是整體,表示s1的value部分)但是我們不括起來也是一樣的,因為點的優先級要高於&。如果其成員本身又是一種結構體類型,那麼可以通過若干個成員運算符,一級一級的找到最低一級成員再對其進行操作;
struct date{    int year;    int month;    int day;};struct student{    char name[10];    struct date birthday;}student1;//若想引用student的出生年月日,可表示為;student.brithday.year;brithday是student的成員;year是brithday的成員;

可以將一個結構體變量作為一個整體賦值給另一相同類型的結構體變量,可以到達整體賦值的效果;這個成員變量的值都將全部整體賦值給另外一個變量;不能將一個結構體變量作為一個整體進行輸入和輸出;在輸入輸出結構體數據時,必須分別指明結構體變量的各成員;小結:除去「相同類型的結構體變量可以相互整體賦值」外,其他情況下,不能整體引用,只能對各個成員分別引用;

結構體長度
16位編譯器

char :1個字節
char*(即指針變量): 2個字節
short int : 2個字節
int:  2個字節
unsigned int : 2個字節
float:  4個字節
double:   8個字節
long:   4個字節
long long:  8個字節
unsigned long:  4個字節

32位編譯器


char :1個字節
char*(即指針變量): 4個字節(32位的尋址空間是2^32, 即32個bit,也就是4個字節。同理64位編譯器)
short int : 2個字節
int:  4個字節
unsigned int : 4個字節
float:  4個字節
double:   8個字節
long:   4個字節
long long:  8個字節
unsigned long:  4個字節
typedef struct{  char addr;  char name;  int  id;}PERSON;

通過printf("PERSON長度=%d字節\n",sizeof(PERSON));可以看到結果:


結構體字節對齊

char ss[20]={0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29};

printf("0x%02x,0x%02x,0x%02x\n",ps->addr,ps->name,ps->id);printf("PERSON長度=%d字節\n",sizeof(PERSON));

可以看到addr和name都只佔一個字節,但是未滿4位元組,跳過2位元組後才是id的值,這就是4位元組對齊。結構體成員有int型,會自動按照4位元組對齊。
typedef struct{  char addr;  int  id;  char name;}PERSON;

typedef struct{    int  id;  char addr;  char name;}PERSON;

typedef struct{    char addr;  char name;    char  id;}PERSON;

typedef struct{    char addr;  char name;    int  id;}PERSON;typedef struct{    char age;  PERSON  ps1;}STUDENT;

先定義結構體類型PERSON,再定義結構體STUDENT,PERSON作為它的一個成員。
STUDENT *stu=(STUDENT *)ss;

printf("0x%02x,0x%02x,0x%02x,0x%02x\n",stu->ps1.addr,stu->ps1.name,stu->ps1.id,stu->age);printf("STUDENT長度=%d字節\n",sizeof(STUDENT));

typedef struct{    PERSON  ps1;  char age;}STUDENT;

結構體嵌套其實沒有太意外的東西,只要遵循一定規律即可:
//對於「一錘子買賣」,只對最終的結構體變量感興趣,其中A、B也可刪,不過最好帶著  struct A{            struct B{               int c;           }  b;  }  a;  //使用如下方式訪問:a.b.c = 10;

 struct A{         struct B{                  int c;          }b;           struct B sb;   }a;  

a.b.c = 11;  printf("%d\n",a.b.c);  a.sb.c = 22;  printf("%d\n",a.sb.c);

但是如果嵌套的結構體B是在A內部才聲明的,並且沒定義一個對應的對象實體b,這個結構體B的大小還是不算進結構體A中。(結構體長度、結構體字節對齊、結構體嵌套內容來源於公眾號「0基礎學單片機」,作者:森林木,感謝原作者的分享)

struct結構體,在結構體定義的時候不能申請內存空間,不過如果是結構體變量,聲明的時候就可以分配——兩者關係就像C++的類與對象,對象才分配內存(不過嚴格講,作為代碼段,結構體定義部分「.text」真的就不佔空間了麼?當然,這是另外一個範疇的話題)。結構體的大小通常(只是通常)是結構體所含變量大小的總和,下面列印輸出上述結構體的size:
printf("size of struct man:%d\n",sizeof(struct man));  printf("size:%d\n",sizeof(Huqinwei));

結果毫無懸念,都是28:分別是char數組20,int變量4,浮點變量4.   

對於結構體中比較小的成員,可能會被強行對齊,造成空間的空置,這和讀取內存的機制有關,為了效率。通常32位機按4位元組對齊,小於的都當4位元組,有連續小於4位元組的,可以不著急對齊,等到湊夠了整,加上下一個元素超出一個對齊位置,才開始調整,比如3+2或者1+4,後者都需要另起(下邊的結構體大小是8bytes),相關例子就多了,不贅述。
struct s  {  char a;  short b;  int c;  }

相應的,64位機按8位元組對齊。不過對齊不是絕對的,用#pragma pack()可以修改對齊,如果改成1,結構體大小就是實實在在的成員變量大小的總和了。和C++的類不一樣,結構體不可以給結構體內部變量初始化,。
#include<stdio.h>  //直接帶變量名struct stuff{  //      char job[20] = "Programmer";  //      char job[];  //      int age = 27;  //      float height = 185;  };

C++的結構體變量的聲明定義和C有略微不同,說白了就是更「面向對象」風格化,要求更低。

如果函數的參數比較多,很容易產生「重複C語言代碼」,例如:

int get_video(char **name, long *address, int *size, time_t *time, int *alg){    ...}int handle_video(char *name, long address, int size, time_t time, int alg){    ...}int send_video(char *name, long address, int size, time_t time, int alg){    ...}

上述C語言代碼定義了三個函數:get_video() 用於獲取一段視頻信息,包括:視頻的名稱,地址,大小,時間,編碼算法。
然後 handle_video() 函數根據視頻的這些參數處理視頻,之後 send_video() 負責將處理後的視頻發送出去。下面是一次調用:

char *name = NULL;long address;int size, alg;time_t time;
get_video(&name, &address, &size, &time, &alg);handle_video(name, address, size, time, alg);send_video(name, address, size, time, alg);

從上面這段C語言代碼來看,為了完成視頻的一次「獲取」——「處理」——「發送」操作,C語言程序不得不定義多個變量,並且這些變量需要重複寫至少三遍。

雖說C語言程序的代碼風格因人而異,但是「重複的代碼」永遠是應盡力避免的,原因本專欄已經分析多次。不管怎麼說,每次使用這幾個函數,都需要定義很多臨時變量,總是非常麻煩的。所以,這種情況下,完全可以使用C語言的結構體語法:

struct video_info{    char *name;    long address;    int size;    int alg;    time_t time;};

定義好 video_info 結構體後,上述三個C語言函數的參數可以如下寫,請看:

int get_video(struct video_info *vinfo){    ...}int handle_video(struct video_info *vinfo){    ...}int send_video(struct video_info *vinfo){    ...}

修改後的C語言代碼明顯精簡多了,在函數內部,視頻的各個信息可以通過結構體指針 vinfo 訪問,例如:

printf("video name: %s\n", vinfo->name);long addr = vinfo->address;int size = vinfo->size;

事實上,使用結構體 video_info 封裝視頻信息的各個參數後,調用這幾個修改後的函數也是非常簡潔的:

struct video_info vinfo = {0};
get_video(&vinfo);handle_video(&vinfo);send_video(&vinfo);

從上述C語言代碼可以看出,使用修改後的函數只需定義一個臨時變量,整個代碼變得非常精簡。

讀者應該注意到了,修改之前的 handle_video() 和 send_video() 函數原型如下:

int handle_video(char *name, long address, int size, time_t time, int alg);int send_video(char *name, long address, int size, time_t time, int alg);

根據這段C語言代碼,我們知道 handle_video() 和 send_video() 函數只需要讀取參數信息,並不再修改參數,那為什麼使用結構體 video_info 封裝數據,修改後的 handle_video() 和 send_video() 函數參數是 struct video_info  *指針型呢?

int handle_video(struct video_info *vinfo);int send_video(struct video_info *vinfo);

既然 handle_video() 和 send_video() 函數只需要讀取參數信息,那我們就無需再使用指針型了呀?的確如此,這兩個函數的參數直接使用 struct video_info 型也是可以的:

int handle_video(struct video_info vinfo){    ...}int send_video(struct video_info vinfo){    ...}

似乎這種寫法和使用  struct video_info  *指針型 參數的區別,無非就是函數內部訪問數據的方式改變了而已。但是,如果讀者能夠想到我們之前討論過的C語言函數的「棧幀」概念,應該能夠發現,使用指針型參數的 handle_video() 和 send_video() 函數效率更好,開銷更小。嵌入式開發中,C語言位結構體用途詳解
在嵌入式開發中,經常需要表示各種系統狀態,位結構體的出現大大方便了我們,尤其是在進行一些硬體層操作和數據通信時。但是在使用位結構體的過程中,是否深入思考一下它的相關屬性?是否真正用到它的便利性,來提高系統效率?
下面將進行一些相關實驗(這裡以項目開發中的實際代碼為例):
[cpp] view plain copy print?//data structure except for number structure  typedef struct symbol_struct  {  uint_32 SYMBOL_TYPE     :5;  uint_32 reserved_1      :4;  
uint_32 SYMBOL_NUMBER :7; uint_32 SYMBOL_ACTIVE :1;   uint_32 SYMBOL_INDEX :8; uint_32 reserved_2     :8;  
}SYMBOL_STRUCT,_PTR_ SYMBOL_STRUCT_PTR;

分析:這裡定義了一個位結構體類型SYMBOL_STRUCT,那麼用該類型定義的變量都哪些屬性呢?

WORDS是定義的另一個外層類型定義封裝,可以把它當作變量來看待。WORDS變量裡前5個數據域的地址都是0x1ffff082c,而reserved_2的地址0x1fff0830,緊接著的PressureState變量是0x1fff0834。

開始以為:reserved_1和SYMBOL_TYPE不在一個地址上,因為他們5+4共9位,超過了1個字節地址,但實際他們共用首地址了;而且reserved_2隻定義了8位,竟然實際佔用了4個字節(0x1fff0834 - 0x1fff0830),我本來是想讓他佔用1個字節的。WORDS整體佔了8個字節(0x1fff0834 - 0x1fff082c),設計時分析佔用5個字節(SYMBOL_TYPE 1個;reserved_1 1個;SYMBOL_NUMBER+SYMBOL_ACTIVE 1個;SYMBOL_INDEX 1個;reserved_2 1個)。uint_32  reserved_2   : 8;  佔用4個字節,估計是uint_32在起作用,而這裡寫的8位,只是我使用的有效位數,另外24位空閒,如果在下面再定義一個uint_32 reserved_3   : 8,地址也是一樣的,都是以uint_32為單位取地址。同理,上面的5個變量,共用一個地址就不足為奇了。而且有效位的分配不是連續進行的,例如SYMBOL_TYPE+reserved_1 共9位,超過了一個字節,索性系統就分配兩個字節給他們,每人一個;SYMBOL_NUMBER+SYMBOL_ACTIVE 共8位,一個字節就能搞定。
[cpp] view plain copy print?//data structure except for number structure  typedef struct symbol_struct  {  uint_8 SYMBOL_TYPE    :5;  //data type,have the affect on "data display type"  uint_8 reserved_1     :4;  
uint_8 SYMBOL_NUMBER :7; //effective data number in one element uint_8 SYMBOL_ACTIVE :1; //symbol active status
uint_8 SYMBOL_INDEX :8; //data index in norflash,result is related to "xxx_BASE_ADDR" uint_8 reserved_2 :8; }SYMBOL_STRUCT,_PTR_ SYMBOL_STRUCT_PTR;

當換成uint_8後,可以看到地址空間佔用大大減小,reserved_2隻佔用1個字節(0x1fff069f - 0x1fff069e),其他變量也都符合上面的結論猜想。但是,注意看上面黃色和紅色的語句,總感覺有些勉強,那麼我又會想,前兩個變量數據域是9位,那麼他們實際上是不是真正的獨立呢?雖然在uint_8上面他們是不同的地址,在uint_32的時候是不是也是不同的地址空間呢?

本來假設: 由前2次試驗的結論,一共佔用8個字節,節空間佔用:(2+4)+(4+4)+(2+2+4)+(2+2)+(6)。可是,實際效果並不是想的那樣。實際只佔用了4個字節,系統並沒有按照預想的方式,為RESERVED變量分配4個字節。這些數據域,整體相加一共32位,佔用4個字節(不考慮數據對齊問題)。而實際確實是佔用了4個字節,唯一的原因就是:這些數據域以緊湊的方式連結,沒有任何空閒位。實際是不是這樣呢?
這裡為了驗證是否緊湊連結,用到了一個union數據,後面會講到用union不會對數據組織方式有任何影響,看實際與上次的一樣,也能分析出來。主要是分析第2和第3個數據域是否緊密連結的。OBJECT_ACTIVE_PRE賦值0b00001111,NUMBER_ACTIVE賦值0b00000101,其他變量都是0,看到WORD數值0b1011111000000。分析WORD數據,可以看到這款MCU還是小端格式(高位數據在高端,低位數據在低端,這裡不對大小端進行討論),斷開數據變成(0)10111 11000000,正好是0101+1111,OBJECT_ACTIVE_PRE數據域,跨越了兩個字節,並不是剛開始設想的那樣。這就印證了上面的緊密連結的結論,也符合數據結果輸出。
可以看到,RESERVED數據域已經不再屬於4個地址空間內了(0x1fff0518 - 0x1fff051b),但是他們整體加起來還是32個位域。這說明數據中間肯定有「空隙」存在了,空隙在哪?看一下NUMBER_STATE,如果緊密的話它應該跟NUMBER_ACTIVE在同一個字節地址上,可是他們並不在一塊,「空隙」就存在這裡。這兩個結構體有什麼不一樣?數據類型不一致,一個是uint_32,一個是uint_8。綜上所述:數據類型影響的是編譯器在分配物理空間時的大小單位,uint_32是以4個字節為單位,而後面的位域則是指在已經分配好的物理空間內部再緊湊的方式分配數據位,當物理空間不能滿足位域時,那麼系統就再次以一定大小單位進行物理空間分配,這個單位就是上面提到的uint_8或者uint_32。舉例:上面uint_32時,這些位域不管是不是在一個字節地址上,如果能夠緊湊的分配在一個4位元組空間大小上,就直接緊湊分配。如果不能則繼續分配(總空間超過4位元組),則再次以4位元組空間分配,並把新的位域建立在新的地址空間上(條目1上的就是)。當uint_8時,很明顯如果位域不能緊湊的放在一個字節空間上,那麼就從新分配新的1位元組空間大小,道理是一樣的。
可以看到,系統並沒有因為位結構體上面有uint_4的4位元組變量或者共用體類型,就改變分配策略把位域都擠到4位元組之內,看來他們是沒有什麼實質性聯繫的。這裡把uint_32改成uint_8,或者把位結構體也替換掉,經我試驗證明,都是沒有任何影響的。
1、在操作位結構體時,要關注變量的位域是否在一個變量類型(uint_32或者uint_8)上,判斷佔用空間大小2、除了位域,還要關注變量定義類型,因為編譯器空間分配始終是按類型分配的,位域只是指出了有效位(小於類型佔用空間),而且如果位域大於類型空間,編譯器直接報錯(如 uint_8  test  :15,可自行實驗)。3、這兩個因素都影響變量佔用空間大小,具體可以結合調試窗口,通過地址分配分析判斷4、最重要的一點:上面的所有結果,都是基於我自己的CodeWarrior10.2和MQX3.8分析出來的,不同的編譯環境和作業系統,都可能會有不同的結果;而且即便是環境相同,編譯器的配置和優化選項都有可能影響系統處理結果。結論並不重要,主要想告訴大家這一塊隱藏陷阱,在以後處理類似問題時,要注意分析避讓並掌握方法。

-END-

相關焦點

  • C語言結構體(Struct)
    在C語言中,可以使用結構體(Struct)來存放一組不同類型的數據。結構體的定義形式為:struct 結構體名{    結構體所包含的變量或數組};結構體是一種集合,它裡面包含了多個變量或數組,它們的類型可以相同,也可以不同,每個這樣的變量或數組都稱為結構體的成員(Member)。
  • C語言結構體(struct)詳解
    最直觀的,指針可以改指向,數組不可以,因為數組佔用的每一個內存地址都用來保存變量或者對象,而指針佔用的內存地址保存的是一個地址,數組沒有單獨的保存指向地址的這樣一個結構。數組的位置是固定的,正如指針變量自身的位置也是固定的,改的是指針的值,是指向的目標地址,而因為數組不存儲目標地址,所以改不了指向。企圖把地址強制賦值給數組的話,也只是說把指針賦值給數組,類型不兼容。
  • C語言之結構體(struct)
    long、unsigned int 、short、char (相當於各種文件類型,比如 .txt、.c、.h)這些關鍵字是否很熟悉?這都是 C 語言定義好的數據類型,直接拿來用就行了。但是我想自定義一個別的類型的數據怎麼辦?就靠 struct 了。
  • C語言結構體(struct)常見使用方法
    A{ struct B{ int c; } b; } a; a.b.c = 10; 特別的,可以一邊定義結構體B,一邊就使用上:
  • C語言結構體、枚舉以及位域的講解
    ,信息量很大,建議收藏【置頂】十大排序算法,來這看看-基本思想+動畫演示+C語言實現【置頂】C語言數據結構課程設計-停車場管理【附源碼】【置頂】程式設計師喜歡的 5 款最佳最牛代碼比較神器,必須安利!【置頂】一個好玩的小遊戲(純C語言編寫)【置頂】如果是初學C語言請看完,如何學好C語言絕對精品【必讀】乾貨丨程式設計師必定會愛上的十款軟體【必讀】10分鐘,快速掌握C語言指針【必讀】8個基礎且實用的C語言經典實例【附源碼】【必讀】C語言實現圖書管理系統源碼,已驗證可以直接運行【必讀】
  • C語言編程 — 結構體與位域
    數組類型顯然無法滿足這一需求,此時可以使用結構體(Struct)來存放一組不同類型的數據。C 語言中,像 int、float、char 等是由 C 語言本身提供的數據類型,不能再進行分拆,我們稱之為基本數據類型。
  • C語言中的結構體和聯合體
    【推薦閱讀】C語言指針-從底層原理到花式技巧,用圖文和代碼幫你講解透徹嵌入式工程師常用的宏定義
  • C語言之結構體最全面總結
    定義我們剛剛申請了一個名叫Info的結構體類型,那麼理論上我們可以像聲明其他變量的操作一樣,去聲明我們的結構體操作,但是C語言中規定,聲明結構體變量的時候,struct關鍵字是不可少的。學號:20191102姓名:Joy入學時間:2019/9/8學制:5畢業時間:2024傳遞指向結構體變量的指針早期的C語言是不允許直接將結構體作為參數直接傳遞進去的。主要是考慮到如果結構體的內存佔用太大,那麼整個程序的內存開銷就會爆炸。不過現在的C語言已經放開了這方面的限制。
  • C語言結構體常見方法
    struct B{               int c;          }          b;  }  a;  //使用如下方式訪問:  a.b.c = 10;   特別的,可以一邊定義結構體B,一邊就使用上:[cpp] view plain copy 在CODE
  • 剖析c語言結構體的高級用法(二)
    (還有vs,這兩個編譯裡不能申明一個空的結構體,必須要有一個結構體成員來才行)寫成c語言程序空結構體的話,它會報錯,在新一點的編譯器裡面就不會報錯(比如dev,gcc)。說明(這裡是c++裡才這樣,在c語言裡輸出的結果不一樣的):
  • C 語言中的結構體和共用體(聯合體)
    在 C 語言中,結構體(struct)是一個或多個變量的集合,這些變量可能為不同的類型,為了處理的方便而將這些變量組織在一個名字之下。由於結構體將一組相關變量看作一個單元而不是各自獨立的實體,因此結構體有助於組織複雜的數據,特別是在大型的程序中。
  • C語言中的結構體和共用體(聯合體)
    在 C 語言中,結構體(struct)是一個或多個變量的集合,這些變量可能為不同的類型,為了處理的方便而將這些變量組織在一個名字之下。由於結構體將一組相關變量看作一個單元而不是各自獨立的實體,因此結構體有助於組織複雜的數據,特別是在大型的程序中。
  • C/C+語言struct深層探索
    1. struct的巨大作用  面對一個人的大型C/C++程序時,只看其對struct的使用情況我們就可以對其編寫者的編程經驗進行評估。因為一個大型的C/C++程序,勢必要涉及一些(甚至大量)進行數據組合的結構體,這些結構體可以將原本意義屬於一個整體的數據組合在一起。從某種程度上來說,會不會用struct,怎樣用struct是區別一個開發人員是否具備豐富開發經歷的標誌。
  • 【C語言經典編程技術】C語言中union與struct的區別及sizeof的計算方式
    C語言中union與struct的區別在C語言中結構體和聯合具有細微差別,特別是使用sizeof()對其求大小時,許多剛剛接觸
  • C語言結構體常見寫法及用法
    關注+星標公眾號,不錯過精彩內容作者
  • C語言系列(九):結構體
    這就需要一種新的數據類型,能夠將具有內在聯繫的不同類型的數據組成一個整體,在C語言中,這種數據類型就是結構體。在C語言中,結構體屬於構造數據類型,它由若干成員組成,成員的類型既可以是基本數據類型,也可以是構造數據類型,而且可以互不相同。struct 結構體類型名{ 類型1 成員名1; 類型2 成員名2; ...
  • C語言結構體變量
    如定義一個學生的信息:struct student{int num;char name[20];int score;};struct為結構體關鍵字,student就是這結構體的類型名,而 num,name, score就是該結構體的成員,他們可以是不同類型的,注意在定義類型的時候不要對結構體成員num,name, score賦初值。
  • 為什麼C語言中的結構體的size,並不等於它所有成員size之和?
    結構體在C語言程序開發中,是不可或缺的語法。不過,相信不少C語言初學者遇到過這樣的問題:為什麼結構體的 size 有時不等於它的所有成員的 size 之和呢?舉例來說,假設有結構體,它的C語言代碼如下,請看:struct x{short s; int i; char c;};我們繼續編寫C語言代碼
  • ...也可以面向對象面層,使用「函數指針結構體」為C語言找個「對象」
    函數指針結構體稍微思考一下,應該能夠想到C語言中的普通數據類型不僅可以用於定義數組,還可以用來定義結構體,例如:struct cn{char c;int i;double d;};那麼可以看作「普通數據類型」的函數指針也可以定義結構體嗎?
  • struct和typedef struct
    莫非C語言不允許在結構中包含指向它自己的指針嗎?請你先猜想一下,然後看下文說明:typedef struct tagNode{ char *pItem; pNode pNext;} *pNode;答案與分析:1、typedef的最簡單使用typedef long byte_4;給已知數據類型long起個新名字,叫byte_4。