一篇文章帶你用C語言玩轉結構體
前言
C語言提供瞭不同的數據類型,比如說int、float、double、char等,不同的類型決定瞭一個變量在內存中應該占據的空間以及表現形式。
但是,當我們定義一個人的時候,人的不同屬性就比較難用同一個數據類型來定義瞭,因為人的身高、年齡、體重等屬性往往需要不同數據類型,在這個時候,我們便引入結構體這個概念。
一、結構體的聲明與定義
1.結構體的聲明
結構是一些值的集合,這些值稱為成員變量。結構的每個成員可以是不同類型的變量
當我們面對的事物有多個不同的數據類型的時候,我們就可以使用結構體來組織瞭。
比如說,一本書有書名、作者、售價、出版日期等等不同的數據類型,這時候我們可以創建結構體來包含書的不同數據類型。
而結構體聲明是描述結構體組合的主要方法,語法格式為:
struct 結構體名稱
{
結構體成員1;
結構體成員2;
結構體成員3;
…
};//分號不能丟
【註意】
結構體成員既可以是任何一種基本的數據類型,也可以是另一種結構體,如果是後者就相當於結構體的嵌套。(俗稱套娃)
例如:
struct Book//描述一本書的相關屬性,其中Book是這個框架的名稱 { char name[20];//書名 char author[20];//作者 float price;//價格 };//分號一定不能丟
這樣就相當於描述瞭一本書的框架。
2.結構成員的類型
結構成員的類型可以是標量、數組、指針、甚至是其他結構體。
3.結構體的定義
結構體的聲明隻是進行一個簡單的描述,實際上在沒有定義結構體類型變量之前,它是不會在內存中分配空間的。
也就是說,它還沒有被真正使用,虛擬存在,隻有定義瞭結構體類型變量,才真實存在。
舉個例子,上面定義瞭書的框架
struct Book//描述一本書的相關屬性,其中Book是這個框架的名稱 { char name[20];//書名 char author[20];//作者 float price;//價格 };//分號一定不能丟
這裡在編譯器中,並不會分配內存空間,它僅僅是虛擬存在。而一旦我們定義瞭結構體類型變量,它就可以被分配空間瞭。
比如:
struct Book//描述一本書的相關屬性,其中Book是這個框架的名稱 { char name[20];//書名 char author[20];//作者 float price;//價格 };//分號一定不能丟 int main() { struct Book book;//局部變量--放在棧區 return 0; }
我們在上面例子中也可以註意到,定義結構體變量的語法是:
struct 結構體名稱 結構體變量名
此外,還可以在結構體聲明的時候定義結構體變量
struct Book//描述一本書的相關屬性 { char name[20]; char author[20]; float price; }b1,b2;//b1,b2是全局變量。放在靜態區 int main() { struct Book book;//局部變量--放在棧區 return 0; }
b1、b2結構體變量是一個全局變量,在其他函數中也可以對它進行訪問。
二、初始化結構體
我們在定義一個變量或數組的時候可以對其進行初始化,
例如:
int a=10; int arr[10]={1,2,3,4,5,6,7,8,9,0};
同理,定義結構體變量的時候,我們也可以同時為其初始化
struct Book//描述一本書的相關屬性 { char name[20]; char author[20]; float price; }b1,b2;//b1,b2是全局變量。放在靜態區 int main() { struct Book book= { "《笑傲江湖》","金庸",30 };//這樣的話,就將結構體變量初始化瞭,也就是定義變量的同時賦初值 return 0; }
三、訪問結構體成員
結構體變量訪問成員 結構變量的成員是通過點操作符(.)訪問的。點操作符接受兩個操作數。
比如,book.name就是引用book結構體變量的name成員,它是一個字符數組。
#include <stdio.h> struct Book//描述一本書的相關屬性 { char name[20]; char author[20]; float price; }b1, b2;//b1,b2是全局變量。放在靜態區 int main() { struct Book book= { "《笑傲江湖》", "金庸", 30 };//這樣的話,就將結構體變量初始化瞭,也就是定義變量的同時賦初值 printf("%s %s %f\n", book.name, book.author, book.price); //用. 來訪問 return 0; }
四、結構體嵌套
如果訪問嵌套的結構體成員的話,就需要使用多層點號運算符來進行操作。因為C語言的結構體隻能對最底層的成員進行訪問,如果存在多級結構體嵌套的話,就需要一級一級地深入,直到找到最底層的成員才行
struct S { int a; char c; double d; }; struct T { struct S s;//結構體嵌套 char name[20]; int num; }; int main() { struct T t = { {100,'c',3.14},"裡斯",30 }; printf("%d %c %f %s %d\n", t.s.a, t.s.c, t.s.d, t.name, t.num);//使用瞭兩層點號運算符尋找成員 return 0; }
五、結構體指針
在開頭的時候說過,結構的成員可以是標量、數組、指針。
在這裡,我們來認識一下結構體指針。
struct Book *pt;
這裡聲明瞭一個指向Book結構體類型的指針變量pt
struct S { int a; char c; double d; }; struct T { struct S s; char name[20]; int num; }; int main() { struct T t = { {100,'c',3.14},"裡斯",30 }; printf("%d %c %f %s %d\n", t.s.a, t.s.c, t.s.d, t.name, t.num); struct T*pt = &t;//拿到地址的方式 printf("%d %c %f %s %d\n", (*pt).s.a, (*pt).s.c, (*pt).s.d, (*pt).name, (*pt).num); printf("%d %c %f %s %d\n",pt->s.a,pt->s.c,pt->s.d,pt->name,pt->num); return 0; }
【註意】數組名指向的是第一個元素的地址,所以可以直接將數組名賦值給指針變量。但是結構體變量的變量名並不是指向該結構體的地址,所以要使用取地址運算符(&)才能獲取其地址。
如上面的:
struct T*pt = &t;//拿到地址的方式
通過上面的例子我們也可以發現,通過結構體指針訪問結構體成員有以下兩種方法:
(1)(*結構體指針).成員名
(2)結構體指針->成員名
第一種由於點號運算符(.)比指針的取值運算符(*)優先級高,所以要使用小口號先對指針進行解引用,讓它變成該結構體變量,再用點運算符取訪問其成員。
以上兩種方法在實現的時候完全等價。但是,切記,點號(.)隻能用於結構體,而箭頭(->)隻能用於結構體指針。
【打印結果一樣】
當二者皆可用的時候,優先采用第二種方法,因為箭頭具有指向性,很直觀的就可以把它與指針聯系起來瞭。
六、結構體傳參
函數調用的時候,參數的傳遞就是值傳遞的過程,也就是將實參傳給形參的過程。所以,結構體變量可以作為函數的參數傳遞,兩個相同結構體類型的結構體變量也支持直接賦值。
struct S { int arr[100]; int num; char ch; double d; }; //結構體傳參 void print1(struct S ss) { printf("%d %d %d %c %1f", ss.arr[0],ss.arr[2],ss.num,ss.ch,ss.d); } //結構體地址傳參 void print2(struct S*ps) { printf("%d %d %d %c %1f", ps->arr[0], ps->arr[2], ps->num, ps->ch, ps->d); } int main() { struct S s = { {1,2,3,4,5}, 100, 'w',3.14 }; print1(s);//傳結構體 print2(&s);//傳地址 return 0; }
可以看到,確實把參數傳遞過去瞭。
那麼,上面的 print1 和 print2 函數哪個好些?
答案是:首選print2函數。 原因:
函數傳參的時候,參數是需要壓棧的。 如果傳遞一個結構體對象的時候,結構體過大,參數壓棧的的系統開銷比較大,所以會導致性能的下降。
因此,結構體傳參的時候,要傳結構體的地址。
總結
本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!
推薦閱讀:
- C語言 structural body結構體詳解用法
- 一篇文章帶你瞭解C語言:入門基礎(2)
- C語言 自定義類型全面系統理解
- C語言自定義類型詳解(結構體、枚舉、聯合體和位段)
- C語言自定義數據類型的結構體、枚舉和聯合詳解