c語言中的結構體和共用體

結構體與共用體


1、結構體
格式:
struct 結構體名
{ 成員表列};

struct 結構體名  和我們使用的基本的數據類型以及數組等等類型是一樣的。
都是可以進行:類型名 成員名
成員列表 成爲 域表
每一個成員也稱爲結構體中的一個域。

結構體是一種存放不同的類型的組合。可以和數組進行比較 或者 和c++ 中的類進行比較。

eg:
struct student{
    int num;
    char name[20];
    char sex;
    int age;
    float age;
    char addr[30];
};

2、定義結構體類型變量的方法
上面的結構體類型只是相當於一個模型,或者一個種數據類型;其中並沒有具體數據,系統對之也不分配實際的內存單元。

爲了在程序中使用結構體數據,應當定義結構體類型的變量,並且在其中存放具體的數據。

1)先聲明結構體類型再定義變量名

上面的結構體應該是一個聲明的過程,所以沒有被分配內存,和其他的基本的數據類型是一樣的。
所以,需要定義之後纔是有分配內存,真正具有變量和可以運行的特性。

格式:struct 結構體名 結構體的變量名;

eg:struct student student1,student2; //這個過程student1、student2 分別就被分配了59(=2+20+1+2+4+301)個字節。

如果結構體比較大的時候,我們經常也是會將它寫在.h的頭文件中。
因爲這樣就可以使用#include "頭文件名" 來進行包含進去。

2)在聲明類型的同時定義變量
格式:
struct 結構體名{
    成員表列
}變量名錶列;

eg:
struct student{
    int num;
    char name[20];
    char sex;
    int age;
    float age;
    char addr[30];
}studnet1,student2;

3)直接定義結構體類型變量
格式:
struct{
    成員表列
}變量名錶列;
(即爲:這個時候沒有出現結構體名)

說明:
(1)類型和變量是不同的概念,類型是一種模型,變量是一個實際的存儲空間。變量可以進行賦值等等的操作以及系統對其分配內存空間,而類型是不行的。
(2)對結構體的成員 是可以獨立使用的。
(3)成員也可以是一個結構體(我們經常在寫圖的數據算法的時候,定義其結構體是一般是需要定義多個結構體,所以存現結構體中包含有結構體的數據類型的情況)
(4)成員名可以與程序中的變量名相同,兩種代表的是不同的對象。我們在寫程序的時候來進行獲取的時候就可以瞭解和區分開來。

3)結構體的引用
(1)不可以將一個結構體變量作爲一個整體進行輸入和輸出。
格式:結構體變量名.成員名
即爲使用"."這個符號來進行獲取裏面的成員變量,可以將他們看成爲一個整體的變量。
(2)若是出現結構體內部出現結構體來進行定義 的時候,我們需要使用這個符號來進行 不斷地按照從外部到裏面的過程進行獲取裏面的成員變量。
(3)結構體中的成員可以向普通一樣進行各種運算。
(4)可以引用“結構體變量的成員的地址”和“結構體變量地址”
結構體變量地址: 主要是用做函數參數,傳遞結構體變量的地址。

4、和其他的基本類型一樣,可以對結構體進行初始化

5、結構體數組
(與一般的數組不同的地方就是, 結構圖體數組中的每一個元素都是一個結構體變量)
1)定義結構體數組
eg:
struct student{
    int num;
    char name[20];
    char sex;
    int age;
    float age;
    char addr[30];
};

struct student stu[3];

2)結構體數組的初始化
結構體變量的初始化是對結構體變量中的成員進行初始化。(初始化的時候是對應的數值的表列)

結構體數組一般是用於相同的一羣個體之間的,個體中都有很多相應的特性。

6、指向結構體類型數據的指針
1)指向結構變量的指針
和其他的基本變量一樣定義體的變量指針。
注意結構體指針中的變量以及相應的指針的使用情況,尤其是符號“*”、“.”的使用。

以下3種形式是等價的
(1)結構體變量.成員名
(2)(*p).成員名
(3)p->成員名
“—>” :指向運算符,就是使用指針來對相應的對象的屬性(成員進行操作的一種書寫的方式)

2)我們知道了直線數據的指針和指向數組元素的指針的使用情況。同樣的方式,我們可以對結構體數組以及其元素使用指針或者指針變量。
eg:
void main()
{
    struct student *p;
    for(int i=0; i<3 ;i++){
    printf("%5d%-20s%2c%4d \n",p->num,p->name,p->sex,p->age);
    }
}

說明:
(1)p的初始值是直線數組中的第一個元素,(++p)也就是指向了下一個元素。
(2)程序定義的p是直線給一個struct student類型數據的指針變量,它是用來指向一個struct student類型的數據(eg:stu數組的而一個元素(eg: stu[0],stu[1])的起始地址),不應用來指向stu數組元素中的某一個成員。
eg:p = stu[1].name; 出現類型的地址是不匹配的。
可以實現強制類型的轉化:
p = (struct student*)stu[0].name;
這個是將後面的強制轉化爲前面p的同樣的一種類型。
所以p的類型還是不變的。

3、使用結構體的變量和結構體變量的指針作函數的參數
(1)用結構體變量的成員作爲參數。和普通的類型的變量傳值是一樣的,屬於“值”傳遞。
(2)用結構體變量作爲實參。這種方法由於結構體太大的時候,消耗內存比較大,同時造成很大的不變。這種方法是比較“少用”的。
(3)用指向結構體變量(或數組)的指針作實參,將結構體變量(或者數組)的地址傳遞給形參。這種方式應該使用的會是比較多,因爲這樣和我們使用數組指針是一個道理。

7、使用指針處理鏈表
1)鏈表我們會經常使用,它也是一種特殊的數據結構。經常鏈表中有指向下一個節點的指針,所以鏈表中有一個指針類型。
2)簡單的鏈表。
聲明的節點;
stuct student{
    long num;
    float score;
    struct student *next;
};
這樣就聲明瞭一個鏈表的一個節點的數據結構體。

3)處理動態鏈表所需要的函數  (拓展:void * 類型是任意類型)
(1)malloc 函數
函數原型:void *malloc(unsigned int size)

內存的動態分配一個長度爲size的連續空間,此函數的值(返回值)是一個指向分配域起始地址的指針(類型爲:void*),如果此函數沒有能夠正確的執行(eg:內存的控件不足),則返回空指針(NULL)。

malloc 只是用來開闢一個內存空間。

(2)calloc函數
函數原型:void *calloc(unsigned n ,unsigned size)
內存的動態分配n個長度爲size的連續空間,函數返回一個指向分配域起始地址的指針,如果分配不成功,返回null。

即爲:calloc可以用來爲一維數組開闢動態存儲空間,n爲數組元素個數,每一個元素長度爲size。

(3)free函數
函數原型:void free(void* p)
釋放由p指針指向的內存區域,這一部分的內存可以被其他的變量使用。

使用這幾個函數對鏈表進行 創建、插入數據、刪除數據、輸出數據等等的操作。

8、共用體的概念(共用體似乎很少用)
1)公用體的概念
格式:
union 公用體名{
    成員列表
}變量表列。
其實它的聲明以及定義和結構體的聲明和定義的方式是一樣的。
但是含義不一樣。

有的時候需要集中不同類型的變量存放到同一段內存單元中。
eg:可以把一個整型變量、一個字符變量、一個實型變量放在同一地址開始的內存單元中。
即爲:這幾個類型的變量是從同一地址開始進行存儲。

區別:
結構體變量:結構體變量所佔內存長度是各個成員佔的內存的長度之和,每一成員分別佔有器自己的內存單元。
共用體變量:所佔用的內存的長度等於最長的成員的長度。

2)共用體的變量的飲用方式
注意:只有先定義共用體纔可以應用它,不能夠引用共用體變量,並且引用的是共用體變量中的成員。
eg:
union data{
    int i;
    char ch;
    float f;
}a,b,c;

使用方式:
a.i a.f a.ch 這樣都是正確的

printf("%d",a); //這樣是錯誤的

3)共用體數據類型的特點
(1)同一個內存端可以用來存放幾種不同的類型的成員,同一時間只能夠使用一種成員變量。
(2)共用存放最後的一次的 成員起到作用,即爲原有的成員就會失去作用。
(3)公用體變量的地址和它的各個成員的地址都是同一個地址,eg:&a,&a.i ,&a.c ,&a.f 都是相同的地址。
(4)不能夠對公用體的變量名進行賦值,也不能夠應用變量名來得到一個值,不能夠在定義共用體變量的時候對其進行初始化。
下面的表述是錯誤的:
(1》》union{
    int i;
    char ch ;
    float f;
}a={1,'a',1.5};
a = 1;
m = a;
上面的這個三種方式都是錯誤的。
(5)不能把共用體變量作爲函數的參數,也不能夠使函數帶回共用體變量,使用指向共用體變量的指針(與結構體變量這種用法相似)
(6)共用體也是一種(自定義的)數據類型,也是可以存放在其他類型中的。

9、枚舉類型
枚舉含義:將變量意義列舉出來。
關鍵字:enum
eg:
enum weekday{sun,mon,tue,wed,thu,fri,sat};
聲明瞭一個枚舉類型weekday,可以是用來定義變量
enum weekday workday,week-end;
定義的變量可以知道是:workday、week-end
它們的值是:從sun到sat這幾個值。
eg:
workday = mon;
week-end = sat;

同樣也可以直接定義枚舉變量;
enum {sun,mon,tue,wed,thu,fri,sat} workday ,week-end;
說明:
(1)枚舉中的元素,是常量,所以是不可以用來賦值的。
(2)枚舉元素是常量,它是有值的,按照定義的順序分別爲:0,1,2,3,……
也可以自定義數值,
eg:
enum  weekday{sun=7,mon=1,tue,wed……}
這樣sun的值是7,mon的值是1,tue的值爲2,即爲後面自動加上1。
(3)枚舉值可以用來作比較判斷
(4)一個整數不能夠直接賦值給一個枚舉變量的
eg:workday = 2;(錯誤)
應該進行強制類型轉換個給一個枚舉變量。
eg:workday = (enum weekday)2

可以知道枚舉類型也是一種自定義的類型,和結構體、共用體一樣,都是可以自定義類型來進行使用。
枚舉類型一般是用於比較少並且明確的分類中。

10、typedef
用來聲明新的類型名來代替已有的類型名
eg:
typedef int INTERGER
即爲:以後就可以使用INTERGER來替代int這個類型來定義變量。

聲明一個新的類型名的方法:
(1)先按照定義變量的方法寫出定一體(eg:int i)
(2)將變量名換成新類型名
(3)在最前面加typedef(eg:將i換成COUNT)
(4)然後可以用新類型名去定義變量

說明:
(1)typedef可以用聲明各種類型名,但不可能用來定義變量。用typedef可以聲明數組類型、字符串類型,這樣使用比較方便。
(2)用typedef只是對已經存在的類型增加一個類型名,而沒有創造新的類型。
eg:typedef int NUM[10]; 這個無非就是講int n[10]定義的數組變量類型用一個新的名字NUM表示出來。無論使用哪一種方式定義變量,效果都是一樣的。
(3)typedef與#define有相似之處
typedef int COUNT

#define COUNT int
作用都是COUTN 代表 int ,
區別:
#define 是在預編譯的時候處理,它只能夠簡單的字符串替換。
typedef 是編譯時處理,並不是做字符串的簡單替換
eg:
typedef int NUM[100];(聲明NUM爲整型數組類型)
NUM n;(定義n爲整型數組變量)

這裏並不是用NUM[100]替換int,而是採用如同義變量的方法那樣來聲明一個類型。

(4)不同的文件中用到同一個類型數據(尤其是數組,指針,結構體、共用體等等),常用typedef聲明一些數據類型,把他們單獨放在一個文件中。使用#include 關鍵字包括進來。
(5)使用typedef有力程序的通用和移植。


總結:
該模塊只要是複習了一下:
(1)結構體、共用體的使用,共用體很少用,最爲主要的就是掌握結構體,尤其是在鏈表中經常使用到。(因爲:涉及到結構體和指針的關係,結構體數組等等的關係)
(2)typdef和#define關鍵字的使用的異同。


發佈了144 篇原創文章 · 獲贊 11 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章