c 基礎三

weixin_34236869發表於2018-07-10
結構體
struct tag{
    member-list
    member-list
    member-list
    ...
}variable-list;
tag 結構體標籤
member-list 標準的變數定義,比如 int i;或者float f;或者啊其他的有效變數
variable-list 結構變數,定義在結構的末尾,最後一個分號之前,您可以指定一個或多個結構變數。 
//此宣告宣告瞭擁有3個成員的結構體,分別為整型的a,字元型的b和雙精度的c
//結構體的標籤被命名為SIMPLE,沒有宣告變數
struct SIMPLE
{
    int a;
    char b;
    double c;
};
//用SIMPLE標籤的結構體,另外宣告瞭變數t1、t2、t3
struct SIMPLE t1, t2[20], *t3;
//此結構體的宣告包含了其他的結構體
struct COMPLEX
{
    char string[100];
    struct SIMPLE a;
};

//定義指向結構的指標,方式與定義指向其他型別變數的指標相似
struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
};
//在上述定義的指標變數中儲存結構變數的地址。為了查詢結構變數的地址,請把 & 運算子放在結構名稱的前面,
struct Books *struct_pointer;struct_pointer = &Book1;
//為了使用指向該結構的指標訪問結構的成員,您必須使用 -> 運算子
struct_pointer->title;
結構體記憶體分配
結構體儲存分配
結構體中成員變數分配的空間是按照成員變數中佔用空間最大的來作為分配單位,同樣成員變數的儲存空間也是不能跨分配單位的,如果當前的空間不足,則會儲存到下一個分配單位中。
#include <stdio.h>

typedef struct
{
    unsigned char a;
    unsigned int  b;
    unsigned char c;
} debug_size1_t;
typedef struct
{
    unsigned char a;
    unsigned char b;
    unsigned int  c;
} debug_size2_t;

int main(void)
{
    printf("debug_size1_t size=%lu,debug_size2_t size=%lu\r\n", sizeof(debug_size1_t), sizeof(debug_size2_t));
    return 0;
}
編譯執行輸出結果:
debug_size1_t size=12,debug_size2_t size=8
結構體佔用儲存空間,以32位機為例
 1.debug_size1_t 儲存空間分佈為a(1byte)+空閒(3byte)+b(4byte)+c(1byte)+空閒(3byte)=12(byte)。
 1.debug_size2_t 儲存空間分佈為a(1byte)+b(1byte)+空閒(2byte)+c(4byte)=8(byte)。
位域
struct
{
  type [member_name] : width ;
};
type    整數型別,決定了如何解釋位域的值。型別可以是整型、有符號整型、無符號整型。
member_name 位域的名稱。
width   位域中位的數量[位元組個數]。寬度必須小於或等於指定型別的位寬度。

//age 變數將只使用 3 位來儲存這個值,如果您試圖使用超過 3 位,則無法完成。
#include <stdio.h>
#include <string.h>

struct
{
  unsigned int age : 3;
} Age;

int main( )
{
   Age.age = 4;
   printf( "Sizeof( Age ) : %d\n", sizeof(Age) );
   printf( "Age.age : %d\n", Age.age );

   Age.age = 7;
   printf( "Age.age : %d\n", Age.age );

   Age.age = 8;
   printf( "Age.age : %d\n", Age.age );

   return 0;
}
//當上面的程式碼被編譯時,它會帶有警告,當上面的程式碼被執行時,它會產生下列結果:
Sizeof( Age ) : 4
Age.age : 4
Age.age : 7
Age.age : 0

有些資訊在儲存時,並不需要佔用一個完整的位元組,而只需佔幾個或一個二進位制位。
例如在存放一個開關量時,只有 0 和 1 兩種狀態,用 1 位二進位即可。為了節省儲存空間,並使處理簡便,C 語言又提供了一種資料結構,稱為"位域"或"位段"。
所謂"位域"是把一個位元組中的二進位劃分為幾個不同的區域,並說明每個區域的位數。每個域有一個域名,允許在程式中按域名進行操作。這樣就可以把幾個不同的物件用一個位元組的二進位制位域來表示
位域定義與結構定義相仿,其形式為:
struct 位域結構名 
{

 位域列表

};
其中位域列表的形式為:
型別說明符 位域名: 位域長度 
struct packed_struct {
  unsigned int f1:1;
  unsigned int f2:1;
  unsigned int f3:1;
  unsigned int f4:1;
  unsigned int type:4;
  unsigned int my_int:9;
} pack;
一個位域必須儲存在同一個位元組中,不能跨兩個位元組。如一個位元組所剩空間不夠存放另一位域時,應從下一單元起存放該位域。也可以有意使某位域從下一單元開始。
struct bs{
    unsigned a:4;
    unsigned  :4;    /* 空域 */
    unsigned b:4;    /* 從下一單元開始存放 */
    unsigned c:4
}
由於位域不允許跨兩個位元組,因此位域的長度不能大於一個位元組的長度,也就是說不能超過8位二進位。如果最大長度大於計算機的整數字長,一些編譯器可能會允許域的記憶體重疊,另外一些編譯器可能會把大於一個域的部分儲存在下一個字中。
位域可以是無名位域,這時它只用來作填充或調整位置。無名的位域是不能使用的。
struct k{
    int a:1;
    int  :2;    /* 該 2 位不能使用,只能用作填充或者位置調整,就是儲存上個位於存不下的部分 */
    int b:3;
    int c:2;
};
位域的使用
位域的使用和結構成員的使用相同,其一般形式為:
位域變數名.位域名
位域變數名->位域名
main(){
    struct bs{
        unsigned a:1;
        unsigned b:3;
        unsigned c:4;
    } bit,*pbit;
    bit.a=1;    /* 給位域賦值(應注意賦值不能超過該位域的允許範圍) */
    bit.b=7;    /* 給位域賦值(應注意賦值不能超過該位域的允許範圍) */
    bit.c=15;    /* 給位域賦值(應注意賦值不能超過該位域的允許範圍) */
    printf("%d,%d,%d\n",bit.a,bit.b,bit.c);    /* 以整型量格式輸出三個域的內容 */
    pbit=&bit;    /* 把位域變數 bit 的地址送給指標變數 pbit */
    pbit->a=0;    /* 用指標方式給位域 a 重新賦值,賦為 0 */
    pbit->b&=3;    /* 使用了複合的位運算子 "&=",相當於:pbit->b=pbit->b&3,位域 b 中原有值為 7,與 3 作按位與運算的結果為 3(111&011=011,十進位制值為 3) */
    pbit->c|=1;    /* 使用了複合位運算子"|=",相當於:pbit->c=pbit->c|1,其結果為 15 */
    printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c);    /* 用指標方式輸出了這三個域的值 */
}
共用體

共用體是一種特殊的資料型別,允許您在相同的記憶體位置儲存不同的資料型別。您可以定義一個帶有多成員的共用體,但是任何時候只能有一個成員帶有值。共用體提供了一種使用相同的記憶體位置的有效方式。

union [union tag]
{
   member definition;
   member definition;
   ...
   member definition;
} [one or more union variables];

union tag 是可選的,每個 member definition 是標準的變數定義,比如 int i; 或者 float f; 或者其他有效的變數定義。在共用體定義的末尾,最後一個分號之前,您可以指定一個或多個共用體變數,這是可選的。下面定義一個名為 Data 的共用體型別,有三個成員 i、f 和 str:

union Data
{
   int i;
   float f;
   char  str[20];
} data;
//訪問共用體成員
//為了訪問共用體的成員,我們使用成員訪問運算子(.)。
#include <stdio.h>
#include <string.h>
 
union Data
{
   int i;
   float f;
   char  str[20];
};
 
int main( )
{
   union Data data;        
 
   data.i = 10;
   data.f = 220.5;
   strcpy( data.str, "C Programming");
 
   printf( "data.i : %d\n", data.i);
   printf( "data.f : %f\n", data.f);
   printf( "data.str : %s\n", data.str);
 
   return 0;
}
//執行結果
data.i : 1917853763
data.f : 4122360580327794860452759994368.000000
data.str : C Programming
//在這裡,我們可以看到共用體的 i 和 f 成員的值有損壞,因為最後賦給變數的值佔用了記憶體位置,這也是 str 成員能夠完好輸出的原因。

共用體佔用的記憶體應足夠儲存共用體中最大的成員。