1. 程式人生 > >c語言基礎語法六——結構體(完結)

c語言基礎語法六——結構體(完結)

1;關於c語言結構體的引入;
在前面已經介紹了整形(int,long,….),浮點型(flaot,double),字元型(char),還介紹了陣列(儲存一組具有相同型別的資料),字串。但是在實際問題中只有這些資料型別是不夠的,有時候我們需要其中的幾種一起來修飾某個變數,例如一個學生的資訊就需要學號(字串),姓名(字串),年齡(整形)等等,這些資料型別都不同但是他們又是表示一個整體,要存在聯絡,那麼我們就需要一個新的資料型別。
——結構體,他就將不同型別的資料存放在一起,作為一個整體進行處理。

2;c語言使用結構體變數進一步加強了表示資料的能力。
2.1;結構體宣告;

//申明一個結構體 
struct book { char title[MAXTITL];//一個字串表示的titile 題目 ; char author[MAXAUTL];//一個字串表示的author作者 ; float value;//一個浮點型表示的value價格; };//注意分號不能少,這也相當於一條語句;

這個宣告描述了一個由兩個字元陣列和一個float變數組成的結構體,但是注意,他並沒有建立一個實際的資料物件,而是描述了一個組成這類物件的元素,【因此,我們也有時候將結構體宣告叫做模板,因為它勾勒出資料該如何儲存,並沒有例項化資料物件】。
下面介紹一下上面的結構體宣告;
1,首先使用關鍵字struct,他表示接下來是一個結構體。
2;後面是一個可選的標誌(book),它是用來引用該結構體的快速標記。因此我們以後就會可以這樣建立資料物件

struct book library;//把library設為一個可以使用book結構體的結構體變數,則library這個變數就包含了其book結構體中的所有元素

3;接下來就是一個花括號,括起了結構體成員列表,及就是每個成員變數,使用的都是其自己的宣告方式來描述,用分號來結束描述;
列如;char title[MAXTITL];字元陣列就是這樣宣告的,用分號結束;
注意;其中每個成員可以使用任何一種c資料結構甚至是其他的結構體,也是可以的;

4;在結束花括號後的分號表示結構體設計定義 的結束。

2.2,關於其struct宣告的位置,也就是這段程式碼要放到哪裡。同樣這也是具有作用域的。
這種宣告如果放在任何函式的外面,那麼則可選標記可以在本檔案中,該宣告的後面的所有函式都可以使用。如果這種宣告在某個函式的內部,則它的標記只能咋內部使用,並且在其宣告之後;
這裡寫圖片描述


這裡寫圖片描述

2.3;關於我們不斷說的,標記名是可選的,那麼我們什麼時候可以省略,什麼時候一定不能省略呢?
如果是上面那種宣告定義的方法,並且想在一個地方定義結構體設計,而在其他地方定義實際的結構體變數那麼就必須使用標記;
可以省略,設計的同時就建立該結構體變數,但是這種設計是一次性的,
還有就是引入typedef。
這個到後面的定義結構體變數一起說吧?

關於結構體型別的定義的總結;
一般格式就是;
struct 結構體名(也就是可選標記名)
{
    成員變數;
};//使用分號表示定義結束;

3;定義結構體變數;
3.1;之前我們結構體型別的定義(結構體的宣告)只是告訴編譯器該如何表示資料,但是它沒有讓計算機為其分配空間。我們要使用結構體,那麼就需要建立變數,也就是結構體變數;
建立一個結構體變數;struct book library;
看到這條指令,編譯器才會建立一個結構體變數library,此時編譯器才會按照book模板為該變數分配記憶體空間,並且這裡儲存空間都是以這個變數結合在一起的,這也是後面訪問結構體變數成員的時候,我們就要用到結構體變數名來訪問。
分析;
struct book的作用;在結構體宣告中,struct book所起到的作用就像int,,,,等基礎資料型別名作用一樣。
struct book s1,s2,*ss;
定義兩個struct book結構體型別的結構體變數,還定義了一個指向該結構體的指標,其ss指標可以指向s1,s2,或者任何其他的book結構體變數。
其實;
struct book library;
等效於;
struct book{
char …
….
…..
}librar;
這兩種是等效的,只是第一種可以減少程式碼的編寫量;
3.2;現在還是回到剛才提及的那個問題,可選標誌符什麼時候可以省略;

其一;
struct
{
    char title[MAXTITL]; 
    char author[MAXAUTL];
    float value; 
}library;
//注意這裡不再是定義宣告結構體型別,而是直接建立結構體變量了,這個編譯器會分配記憶體的;
//這樣的確可以省略識別符號也就是結構體名,但是隻能使用一次;因為這是;宣告結構體的過程和定義結構體變數的過程和在了一起;並且個成員變數沒有初始化的;
//如果你想多次使用一個結構體模組,這樣子是行不通的;

其二;

typedef定義新型別名來代替已有型別名,即給已有型別重新命名;
一般格式為;typedef 已有型別 新型別名;
typedef int Elem; 
typedef struct{
    int date;
    .....
    .....
}STUDENT;
STUDENT stu1,stu2;

這裡寫圖片描述

總結一下關於結構體變數的定義;
1;先定義結構體型別後再定義結構體變數;
格式為;struct 結構體名 變數名列表;
struct book s1,s2,*ss;//注意這種之前要先定義結構體型別後再定義變數;
2;在定義結構體型別的同時定義結構體變數;
格式為;
struct 結構體名
{
成員列表;
}變數名列表;//這裡結構體名是可以省的,但儘量別省;
struct book
{
    char title[MAXTITL];//一個字串表示的titile 題目 ; 
    char author[MAXAUTL];//一個字串表示的author作者 ; 
    float value;//一個浮點型表示的value價格;
}s1,s2;
3,直接定義結構體型別變數,就是第二種中省略結構體名的情況;
這種方式不能指明結構體型別名而是直接定義結構體變數,並且在值定義一次結構體變數時適用,無結構體名的結構體型別是無法重複使用的,也就是說,後面程式不能再定義此型別變量了,除非再寫一次重複的struct落、

4;對於結構體變數的初始化;
4.1;先回憶一下關於基本資料型別和陣列型別的初始化;
int a = 0;
int array[4] = {1,2,3,4};//每個元素用逗號隔開
回憶一下陣列初始化問題;
這裡寫圖片描述
再回到結構體變數的初始化吧?
關於結構體變數的初始化與初始化陣列類似;
也是使用花括號括起來,用逗號分隔的初始化好專案列表,注意每個初始化專案必須要和要初始化的結構體成員型別想匹配,

struct book s1={//對結構體初始化 
        "yuwen",//title為字串 
        "guojiajiaoyun",//author為字元陣列 
        22.5    //value為flaot型 
    };
    //要對應起來,用逗號分隔開來,與陣列初始化一樣;

4.2;加入一點小知識;
關於結構體初始化和儲存類時期的問題;
如果要初始化一個具有靜態儲存時期的結構體,初始化專案列表中的值必須是常量表達式,
如果儲存時期是自動的,那麼列表的值就不必是常量了;
關於這點在講儲存類時期的時候在分析;
4.3;注意如果在定義結構體變數的時候沒有初始化,那麼後面就不能全部一起初始化了;
意思就是;

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

4.4;對於結構體的指定初始化;
《這個只存在於c99,》
這裡寫圖片描述

5;訪問結構體成員;
5.1,結構體就像一個超級陣列,在這個超級陣列內,一個元素可以是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部分)但是我們不括起來也是一樣的,因為點的優先順序要高於&。
5.2;如果其成員本身又是一種結構體型別,那麼可以通過若干個成員運算子,一級一級的找到最低一級成員再對其進行操作;
結構體變數名.成員.子成員………最低一級子成員;

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的成員;

5.3;整體與分開
5.3.1;可以將一個結構體變數作為一個整體賦值給另一相同型別的結構體變數,可以到達整體賦值的效果;這個成員變數的值都將全部整體賦值給另外一個變數;
5.3.2;不能將一個結構體變數作為一個整體進行輸入和輸出;在輸入輸出結構體資料時,必須分別指明結構體變數的各成員;
這裡寫圖片描述

總結;除開“相同型別的結構體變數可以相互整體賦值”外,其他情況下,不能整體引用,只能對各個成員分別引用;

6;結構體陣列,(這個在國二里面常考的型別,只要考結構體就離不開結構體陣列和成員的訪問,當然也只是簡單的操作)
6.1;為什麼要引用結構體陣列,顯然,在上面的book型別的結構體 ,每本書就需要用一個book型別的結構體變數來描述,若是要描述兩本書,需要使用兩個這樣的變數,依次類推;因此要使用一個該結構體的陣列,來表示這些圖書;並且陣列就是儲存一組具有相同型別的資料,因此就有了結構體陣列的出現,注意本置,
6.2;宣告結構體陣列
與普通的陣列宣告一樣,int a[10];int為元素的資料型別,a為陣列名 【10】表示申請了10的int單元的記憶體;
再看結構體宣告;struct book library[10];是不是類似,struct book為陣列元素的資料型別,library為陣列名,[10]為申請了10個struct book單元的記憶體;
解釋;宣告library為一個具體10個元素的陣列,並且每個元素都book型別的結構,因此可以得到library[0],library[1]…….都是單獨獨立的一個book結構;
注意library本身不是結構體名而是一個數組名;
6.4;結構體陣列的初始化;
這裡寫圖片描述
兩種初始化;就是在定義的時候賦值的兩種情況;
6.3;訪問結構體陣列的成員;
規則;在結構體名後加點運算子,然後再是成員名;
library[5].title;//表示第5個元素的title成員;library[5]是結構體變數名,title就是成員名;
library[5].titlt[4];//注意title是陣列型別,第5個數組元素的title成員的第4個字元;

總結;
library //book結構體的陣列
library[2]//陣列的第二個元素,一個book結構體型別的變數名;
library[2].title;//char陣列,結構體陣列的第二個元素的title成員;
library[2].title[4];//表示一個字元,結構體陣列的第二個元素的title成員的第四個字元;

這裡寫圖片描述

7;指向結構體的指標,
7.1;使用指向結構體的好處;就像指向陣列的指標一樣,它比陣列本身更容易操作,指向結構體的指標通常也比結構體本身更容易操作;
宣告和初始化結構體指標,
宣告struct book * him;
規則就是,struct 結構體名+ * + 指標名;
這個宣告不是建立一個新的結構體,而是建立了一個指標型別的him指標變數,他可以指向任何現有的book型別的結構體;
him = &library[0];
指標him正指向結構體library[0],如何使用him來取得library[0]的一個成員呢?
方法一;
引入一個運算子,->
後跟->運算子的結構體指標和後跟.點運算子的結構體名是一樣操作的;
注意一點的是;不能使用him.成語;因為him不是結構體名;

總結
->只用於結構體指標訪問成員;
.點只用於結構體名訪問成員;

方法二;
如果him=&library[0],那麼him=library[0];因為&和是一個互逆的運算子;
&取地址,*取值;
=》library[0].value 等價於 (*him).value;注意必須使用圓括號,優先順序問題;
然後都與him.value是一個作用;

對於考國二懂得上面的也就差不多了;足夠了;

8;向函式傳遞結構體資訊;
8.1;傳遞結構體成員;
只要結構體成員是具有單值的資料型別,(及int等基礎資料型別)就可以把它作為引數傳遞給一個接受這個特定引數型別的函式;
注意;這個只能實現訪問,不能修改;
8.2;使用結構體地址;
這裡寫圖片描述
注意,如果不修改值,則設定為const;
8.3;使用結構體作為引數傳遞;
這裡寫圖片描述
其中s也是結構體變數,並且為s1結構體變數的副本。
總結;
結構體指標,使用->運算子訪問成員;
結構體名;使用.點運算子訪問成員;
要想通過呼叫函式修改實參結構體變數的值,只能傳遞地址,通過指標來修改;直在地址上修改;
8.4;結構體之間的雙向通訊;
先注意這個;c語言中對於結構體變數是可以整體賦值的,無論其成員是怎樣的;

傳遞地址,使用結構體指標接收,用於訪問不做修改;const限制修改,

這裡寫圖片描述

結構體作為引數傳遞;不能成功修改實參,只能訪問;

這裡寫圖片描述

結構體作為引數,修改結構體並且返回型別也為結構體;達到修改的目的;

這裡寫圖片描述

傳遞地址,使用結構體指標接收,用於修改;不使用const限制修改,

這裡寫圖片描述

通常我們是使用結構體指標的,如果不修改那麼我們會使用const修改;

9;在結構體中使用字元陣列還是字元指標來儲存字串;
答案先給出,儘量使用字元陣列;
9.1;使用字元陣列;

#define MAXTITL 100
#define MAXAUTL 100
struct book 
{
    char title[MAXTITL];//一個字串表示的titile 題目 ; 
    char author[MAXAUTL];//一個字串表示的author作者 ; 
    float value;//一個浮點型表示的value價格; 
}; 
字串的儲存在結構體內部的;
結構體總分配200個位元組的記憶體給這兩個字串

9.2;使用字元指標;

struct book 
{
    char *title; 
    char * author;  
}; 
這裡的字串是儲存在編譯器認為儲存字串常量的任何地方,這個結構體中存放的只有兩個地址而已,值分配8分位元組;結構體不為字串分配任何記憶體儲存空間,因此這時候在輸入的時候存在了一個潛在的危險;
scanf("%s",s.last);//把字串放到由s.last指向的記憶體中因為這是一個未初始化的變數,因此該地址是可以指向任意大小的,因此此時就是一個潛在的危險;

總結,因我們最好是使用字元陣列來儲存字串;

關於結構體的介紹就暫時到這裡
時間有點急,學得也不深,所以暫時先補充到這裡,下次補上;
還有未補充的內容;
1;結構體陣列的函式;
2;結構體內容儲存在檔案當中;
3;一些結構體的例子;