C語言結(jié)結(jié)構(gòu)體與聯(lián)合體

結(jié)構(gòu)體

在C語言中,可以使用結(jié)構(gòu)體(Struct)來存放一組不同類型的數(shù)據(jù)。結(jié)構(gòu)體的定義形式為:

struct 結(jié)構(gòu)體名{
    結(jié)構(gòu)體所包含的變量或數(shù)組
};

結(jié)構(gòu)體是一種集合,它里面包含了多個變量或數(shù)組,它們的類型可以相同,也可以不同,每個這樣的變量或數(shù)組都稱為結(jié)構(gòu)體的成員(Member)。請看下面的一個栗子:

struct stu{
    char *name;  //姓名
    int num;  //學號
    int age;  //年齡
    char group;  //所在學習小組
    float score;  //成績
};


stu 為結(jié)構(gòu)體名,它包含了 5 個成員,分別是 name、num、age、group、score。結(jié)構(gòu)體成員的定義方式與變量和數(shù)組的定義方式相同,只是不能初始化。

注意大括號后面的分號“;”不能少哦~

結(jié)構(gòu)體也是一種數(shù)據(jù)類型,它由我們自己來定義,可以包含多個其他類型的數(shù)據(jù)。
像int、float、char 等是由C語言本身提供的數(shù)據(jù)類型,不能再進行分拆,我們稱之為基本數(shù)據(jù)類型;而結(jié)構(gòu)體可以包含多個基本類型的數(shù)據(jù),也可以包含其他的結(jié)構(gòu)體。

結(jié)構(gòu)體變量

既然結(jié)構(gòu)體是一種數(shù)據(jù)類型,那么就可以用它來定義變量。例如:

struct stu stu1, stu2;

定義了兩個變量 stu1 和 stu2,它們都是 stu 類型,都由 5 個成員組成。注意關(guān)鍵字struct不能少。

還可以在定義結(jié)構(gòu)體的同時定義結(jié)構(gòu)體變量:

struct stu{
    char *name;  //姓名
    int num;  //學號
    int age;  //年齡
    char group;  //所在學習小組
    float score;  //成績
} stu1, stu2;

如果只需要 stu1、stu2 兩個變量,后面不需要再使用結(jié)構(gòu)體名定義其他變量,那么在定義時也可以不給出結(jié)構(gòu)體名,如下所示:

struct{  //沒有寫 stu
    char *name;  //姓名
    int num;  //學號
    int age;  //年齡
    char group;  //所在學習小組
    float score;  //成績
} stu1, stu2;

這樣的寫法很簡單,但是因為沒有結(jié)構(gòu)體名,后面就沒法用該結(jié)構(gòu)體定義新的變量了。

理論上講結(jié)構(gòu)體的各個成員在內(nèi)存中是連續(xù)存儲的,和數(shù)組非常類似,例如上面的結(jié)構(gòu)體變量 stu1、stu2 的內(nèi)存分布如下圖所示,共占用 4+4+4+1+4 = 17 個字節(jié)。但是在編譯器的具體實現(xiàn)中,各個成員之間可能會存在空隙,C語言中,結(jié)構(gòu)體大小的內(nèi)存分配,參考于這片文章:C語言中結(jié)構(gòu)體大小計算即存儲分配

這里我在做下總結(jié):

運算符sizeof可以計算出給定類型的大小,對于32位系統(tǒng)來說,sizeof(char) = 1; sizeof(int) = 4。基本數(shù)據(jù)類型的大小很好計算,我們來看一下如何計算構(gòu)造數(shù)據(jù)類型的大小。

C語言中的構(gòu)造數(shù)據(jù)類型有三種:數(shù)組、結(jié)構(gòu)體和共用體。

數(shù)組是相同類型的元素的集合,只要會計算單個元素的大小,整個數(shù)組所占空間等于基礎(chǔ)元素大小乘上元素的個數(shù)。

結(jié)構(gòu)體中的成員可以是不同的數(shù)據(jù)類型,成員按照定義時的順序依次存儲在連續(xù)的內(nèi)存空間。和數(shù)組不一樣的是,結(jié)構(gòu)體的大小不是所有成員大小簡單的相加,需要考慮到系統(tǒng)在存儲結(jié)構(gòu)體變量時的地址對齊問題??聪旅孢@樣的一個結(jié)構(gòu)體:

  struct stu1
  {
  int i;
  char c;
  int j;
  };

先介紹一個相關(guān)的概念——偏移量。偏移量指的是結(jié)構(gòu)體變量中成員的地址和結(jié)構(gòu)體變量地址 的差。結(jié)構(gòu)體大小等于最后一個成員的偏移量加上最后一個成員的大小。顯然,結(jié)構(gòu)體變量中第一個成員的地址就是結(jié)構(gòu)體變量的首地址。因此,第一個成員i的偏 移量為0。第二個成員c的偏移量是第一個成員的偏移量加上第一個成員的大小(0+4),其值為4;第三個成員j的偏移量是第二個成員的偏移量加上第二個成 員的大?。?+1),其值為5。

實際上,由于存儲變量時地址對齊的要求,編譯器在編譯程序時會遵循兩條原則:一、結(jié)構(gòu)體變量中成員的偏移量必須是成員大小的整數(shù)倍(0被認為是任何數(shù)的整數(shù)倍) 二、結(jié)構(gòu)體大小必須是所有成員大小的整數(shù)倍。

對照第一條,上面的例子中前兩個成員的偏移量都滿足要求,但第三個成員的偏移量為5,并不是自身(int)大小的整數(shù)倍。編譯器在處理時會在第二個成員后面補上3個空字節(jié),使得第三個成員的偏移量變成8。

對照第二條,結(jié)構(gòu)體大小等于最后一個成員的偏移量加上其大小,上面的例子中計算出來的大小為12,滿足要求。

再看一個滿足第一條,不滿足第二條的情況:

  struct stu2
  {
  int k;
  short t;
  };

成員k的偏移量為0;成員t的偏移量為4,都不需要調(diào)整。但計算出來的大小為6,顯然不 是成員k大小的整數(shù)倍。因此,編譯器會在成員t后面補上2個字節(jié),使得結(jié)構(gòu)體的大小變成8從而滿足第二個要求。由此可見,大家在定義結(jié)構(gòu)體類型時需要考慮 到字節(jié)對齊的情況,不同的順序會影響到結(jié)構(gòu)體的大小。對比下面兩種定義順序

  struct stu3
  {
  char c1;
  int i;
  char c2;
  }

  struct stu4
  {
  char c1;
  char c2;
  int i;
  }

雖然結(jié)構(gòu)體stu3和stu4中成員都一樣,但sizeof(struct stu3)的值為12而sizeof(struct stu4)的值為8。

如果結(jié)構(gòu)體中的成員又是另外一種結(jié)構(gòu)體類型時應該怎么計算呢?只需把其展開即可。但有一點需要注意,展開后的結(jié)構(gòu)體的第一個成員的偏移量應當是被展開的結(jié)構(gòu)體中最大的成員的整數(shù)倍??聪旅娴睦樱?/p>

  struct stu5
  {
  short i;
  
  struct{
    char c;
    int j;
  } ss;
 
  int k;
  }

結(jié)構(gòu)體stu5的成員ss.c的偏移量應該是4,而不是2。整個結(jié)構(gòu)體大小應該是16。

如何給結(jié)構(gòu)體變量分配空間由編譯器決定,以上情況針對的是Linux下的GCC。其他平臺的C編譯器可能會有不同的處理,看到這里估計還是有些同學不太明白,多看幾遍,領(lǐng)悟領(lǐng)悟,就好啦!

成員的獲取和賦值

結(jié)構(gòu)體和數(shù)組類似,也是一組數(shù)據(jù)的集合,整體使用沒有太大的意義。數(shù)組使用下標[ ]獲取單個元素,結(jié)構(gòu)體使用點號.獲取單個成員。獲取結(jié)構(gòu)體成員的一般格式為:

結(jié)構(gòu)體變量名.成員名;

通過上面的格式就可以獲取成員的值,和給成員賦值,看下面的栗子:

#include <stdio.h>
int main(){
    struct{
        char *name;  //姓名
        int num;  //學號
        int age;  //年齡
        char group;  //所在小組
        float score;  //成績
    } stu1;
    //給結(jié)構(gòu)體成員賦值
    stu1.name = "haozi";
    stu1.num = 12;
    stu1.age = 18;
    stu1.group = 'A';
    stu1.score = 136.5;
    //讀取結(jié)構(gòu)體成員的值
    printf("%s的學號是%d,年齡是%d,在%c組,今年的成績是%.1f!\n", stu1.name, stu1.num, stu1.age, stu1.group, stu1.score);
    return 0;
}

運行結(jié)果:
haozi的學號是12,年齡是18,在A組,今年的成績是136.5!
除了這種方式賦值外,還可以在定義的時候賦值:

struct{
    char *name;  //姓名
    int num;  //學號
    int age;  //年齡
    char group;  //所在小組
    float score;  //成績
} stu1, stu2 = { "haozi", 12, 18, 'A', 136.5 };

不過整體賦值僅限于定義結(jié)構(gòu)體變量的時候,在使用過程中只能對成員逐一賦值,這和數(shù)組的賦值非常類似。

結(jié)構(gòu)體與指針

指針也可以指向一個結(jié)構(gòu)體,定義的形式一般為:

struct 結(jié)構(gòu)體名 *變量名;

看例子:

struct Man{
    char name[20];
    int age;
};
void main(){
    struct Man m1 = {"Jack",30};
    //結(jié)構(gòu)體指針
    struct Man *p = &m1;
    printf("%s,%d\n", m1.name, m1.age);
    printf("%s,%d\n",(*p).name,(*p).age);
    //“->”(箭頭)是“(*p).”簡寫形式
    printf("%s,%d\n", p->name, p->age);
    //(*env)->

    system("pause");
}

編譯出的結(jié)果是:

Jack,30
Jack,30
Jack,30
請按任意鍵繼續(xù). . .

上面代碼:printf("%s,%d\n", m1.name, m1.age);還可以換成: printf("%s,%d\n", (*p).name, m1.age);或者 printf("%s,%d\n", p->name, m1.age);
其運行結(jié)果還是一樣的。

獲取結(jié)構(gòu)體成員通過結(jié)構(gòu)體指針可以獲取結(jié)構(gòu)體成員,一般形式為:

(*pointer).memberName

或者:

pointer->memberName

聯(lián)合體(共用體)

結(jié)構(gòu)體(Struct)是一種構(gòu)造類型或復雜類型,它可以包含多個類型不同的成員。在C語言中,還有另外一種和結(jié)構(gòu)體非常類似的語法,叫做共用體(Union),它的定義格式為:

union 共用體名{
    成員列表
};

共用體有時也被成為聯(lián)合體;

結(jié)構(gòu)體和共用體的區(qū)別在于:結(jié)構(gòu)體的各個成員會占用不同的內(nèi)存,互相之間沒有影響;而共用體的所有成員占用同一段內(nèi)存,修改一個成員會影響其余所有成員。

結(jié)構(gòu)體占用的內(nèi)存大于等于所有成員占用的內(nèi)存的總和(成員之間可能會存在縫隙),共用體占用的內(nèi)存等于最長的成員占用的內(nèi)存。共用體使用了內(nèi)存覆蓋技術(shù),同一時刻只能保存一個成員的值,如果對新的成員賦值,就會把原來成員的值覆蓋掉。

共用體也是一種自定義類型,可以通過它來創(chuàng)建變量,例如:

union data{
    int n;
    char ch;
    double f;
};
union data a, b, c;

上面是先定義共用體,再創(chuàng)建變量,也可以在定義共用體的同時創(chuàng)建變量:

union data{
    int n;
    char ch;
    double f;
} a, b, c;

共用體 data 中,成員 f 占用的內(nèi)存最多,為 8 個字節(jié),所以 data 類型的變量(也就是 a、b、c)也占用 8 個字節(jié)的內(nèi)存,請看下面的栗子:

#include<stdio.h>  
union var{  
        long j;  
        int i;  
};  
main(){  
        union var v;  
        v.j = 5;  
        printf("v.j is %d\n",v.i);  
        v.i = 6;  //最后一次賦值有效
        printf("now v.j is %ld! the address is %p\n",v.j,&v.j);  
        
        printf("now v.i is %d! the address is %p\n",v.i,&v.i);  

        system("pause");
}  

編譯并運行結(jié)果:

v.j is 5  
now v.j is 6! the address is 0xbfad1e2c  
now v.i is 6! the address is 0xbfad1e2c  

這段代碼不但驗證了共用體的長度,還說明共用體成員之間會相互影響,修改一個成員的值會影響其他成員。

學習理解并整理下來的筆記。
希望大家能夠指點或提出寶貴意見,謝謝!一起學習。
轉(zhuǎn)載請注明出處:http://blog.csdn.net/u011974987/article/details/52305364
個人站點:xuhao.tech

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容