詳解C語言結構和聯合

結構體基礎

聚合類型——能夠同時存儲超過一個單獨的數據,C語言提供兩種聚合數據類型數組結構
數組和結構的區別:

  • 數組是相同類型的數據的聚合,結構是不同類型數據的聚合;
  • 因為數組的元素長度相同可以采用下標方式訪問,結構成員的長度不同,只能通過名字訪問,不能通過下標來訪問。
  • 數組名在表達式中使用時被當做一個指針,而結構體變量在表達式中使用時不能被當做一個指針。

我們可以聲明指向結構的指針,取一個結構變量的地址以及聲明結構數組

結構體變量的聲明

struct tag 
{
  number_list;
} variable_list;

其中tag可以省略,但這樣的話只能聲明一個結構體變量,tag用來聲明多個相同的結構體變量。

標簽允許多個聲明使用同一個成員列表,并且創建同一種類型的結構,標簽只標識了一種模式,用于聲明未來標變量。

struct stInfo
{
  int age ;
 char name[];
  int classNo; 
}
  /* 這個聲明只是把stInfo和這個成員列表關聯起來,但是沒有創建任何結構變量*/
struct stInfo info; //聲明結構體變量info
struct stInfo *info1; //聲明指向結構體變量的指針
struct stInfo inf2[]; //聲明指向結構體的數組
  /*info info1 info2都是同一種類型的結構體*/

聲明結構可以使用另外一種良好的技巧是用type創建一種新的類型如下面的例子所示:

type struct
{
  int a;
  char b;
  float c;
} stSimple; //注意最后是有分號(;)的

/*stSimple是一個類型名,而不是一個變量名*/
stSimple x; 
stSimple y[];
stSimple *z;

結構體成員

結構體里面可以聲明任何類型的變量作為結構體的成員,像結構,體聯合體等;

struct COMPLEX
{
  float f;
  int a [20];
  long *lp;
  struct stSimple s;
  struct stSimple sa[20];
  struct stSimple *sp;
}

一個結構體的成員名字和其他結構的成員名字相同,所以這個結構的成員a并不會與struct stSimple s的成員a沖突。

結構體成員變量的直接訪問

結構體變量的成員是通過操作符(.),點操作符接受兩個操作數,左操作數是結構體變量的名字,右操作數是結構體變量的成員,點操作符是自左向右*的結合性。

struct COMPLEX comp;
comp.f; //訪問成員f
comp.s.a //訪問嵌套結構體成員a
comp.a[4] //訪問結構體數組成員
comp sa[4].c //訪問結構體數組成員

結構體成員的間接訪問

當擁有一個指向結構體變量的指針,當使用這個指針訪問結構體成員時就是結構體成員的間接訪問
??結構體成員的間接訪問使用箭頭操作符(——>),該操作符的左操作數是一個指向結構體的指針,右操作數是結構體成員

struct COMPLEX *compp;
compp->f;
compp->a[1];
compp->s.a;
compp->sa[4].c;
compp->sp->b;

結構體變量的自引用

先看一個結構體違法自引用的例子:

struct SELF_RIF1
{
  int a;
  struct  SELF_RIF1 b; //嵌套一個結構體
  int c;
}

這種引用是違法的,因為成員b是另一個完整的結構體,其內部還包括自己的成員b,如此往復循環無窮盡。
??再看合法的結構體自引用

struct SELF_RIF1
{
  int a;
  struct  SELF_RIF1 *b; //嵌套一個結構體
  int c;
}

這個 聲明和前面那個的區別在于b現在是一個指針而不是一個結構。編譯器在結構的長度確定之前就已經知道指針的長度,所以這種類型的自引用是合法的。

事實上一個結構內部包含一個指向該結構本身的指針,它事實上所指向的是同一種類型的不同結構,鏈表和樹都是用這種技巧實現的,每個結構指向鏈表的下一個元素或樹的下一個分支。

結構的初始化

結構的初始化方式和數組的初始化很類似——一個位于一對花括號內部,由逗號分隔的初始列表可用于結構各個成員的初始化,這些值根據結構成員的順序寫出。如果初始列表的值不夠,剩余的結構成員將使用缺省值進行初始化。
??結構中如果包含數組和結構成員,其初始化方式類似于多維數組的初始化。

struct INIT_EX
{
  int a;
  short b[10];
  Simple c;
} x = {
  10,
  {1,2,3,4,5},
 { 25,'x',1.9}
};

作為函數參數的結構

結構是一個標量,它可以用于其它標量可以使用的任何場合,因此把結構作為一個參數傳遞給一個函數是合法的。

給函數傳遞指向結構的指針的效率要遠大于向函數傳遞整個結構。

typedef struct
{
  char product[PRODUCT_SIZE];
  int quantity;
  float unit_price;
  float total_amount;
} Transaction;
/函數參數為整個結構的版本/
void print_receipt(Transaction trans)
{
  ...
}
/函數參數為指向結構的指針版本/
void print_receipt(Transaction *trans)
{
  ...
}
/分別定義結構和指向結構的指針并調用函數/
 Transaction current_trans;
  print_receipt(current_trans);  //調用函數參數為整個結構的版本
  print_receipt(&current_trans); //調用函數參數為指向結構指針的版本

向函數傳遞指向結構的指針和向函數傳遞整個結構相比效率會很高,且結構越大這種優勢越明顯。

聯合

聯合和結構相似,但它的行為和結構不一樣。聯合的所有的成員引用的是內存中的相同位置

 union 
{
  float f;
  int i;
} fi;

在一個浮點型和整型都是32位的機器上,變量fi只占用內存中一個32位的字,如果成員f被使用,這個字就被作為浮點值訪問,如果成員i被使用,這個字就被當作整型值訪問。

聯合在某一時刻只能有一個成員被訪問,如果聯合成員的長度不一樣,聯合的長度就是它最長成員的長度,若長度懸殊特別大,可以在聯合中存儲指向不同成員的指針而不是直接存儲成員本身,所有指針的長度都相同。

聯合的初始化

聯合變量可以被初始化,但這個值必須是聯合的第一個成員的類型,而且它必須位于一堆花括號里面,例如:

union
{
  int a;
  float f;
  char c[4];
} x = {5};

我們不能把這個類變量初始化為一個浮點數或字符值,如果給出的初始值是任何其它類型,它就會轉換為一個整型值并賦值給x.a;

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,540評論 1 51
  • 指針是C語言中廣泛使用的一種數據類型。 運用指針編程是C語言最主要的風格之一。利用指針變量可以表示各種數據結構; ...
    朱森閱讀 3,474評論 3 44
  • 輕輕合上整本書,抬起酸痛的脖子望向壁鐘,已12點20分,我足足花了四個小時一口氣把這本《故鄉是潮汕》余下的180頁...
    海言的時光閱讀 261評論 0 0
  • 講師訓班長職責 將軍的風度和果斷: 帶頭完成各項任務和指標, 以最嚴格的標準...
    月光寶盒里的白晶晶閱讀 110評論 0 0
  • 1 熙來人往的街讓人彷徨,路邊的音響兀自歌唱 是什么歌? 漫漫停下腳步,靜靜的聽著,街邊的喧鬧被放到一邊 她聽得入...
    我要一所大房子閱讀 202評論 0 0