變量的作用域
C語言根據變量作用域的不同,將變量分為局部變量和全局變量
1.局部變量
1> 定義:在函數內部定義的變量,稱為局部變量。形式參數也屬於局部變量。
2> 作用域:局部變量只在定義它的函數內部有效,即局部變量只有在定義它的函數內部使用,其它函數不能使用它。
2.全局變量
1> 定義:在所有函數外部定義的變量,稱為全局變量。
2> 作用域:全局變量的作用范圍是從定義變量的位置開始到源程序結束,即全局變量可以被在其定義位置之後的其它函數所共享。
變量的存儲類型
* 變量的存儲類型就是指變量存儲在什麼地方。有3個地方可以用於存儲變量:普通內存、運行時堆棧、硬件寄存器。變量的存儲類型決定了變量何時創建、何時銷毀以及它的值能保持多久,也就是決定了變量的生命周期。
* C語言根據變量的存儲類型的不同,可以把變量分為:自動變量、靜態變量、寄存器變量。
1.自動變量
1> 定義:自動變量是存儲在堆棧中的。
2> 哪些是自動變量:被關鍵字auto修飾的局部變量都是自動變量,但是極少使用這個關鍵字,基本上是廢的,因為所有的局部變量在默認情況下都是自動變量。
3> 生命周期:在程序執行到聲明自動變量的代碼塊(函數)時,自動變量才被創建;當自動變量所在的代碼塊(函數)執行完畢後,這些自動變量就會自行銷毀。如果一個函數被重復調用,這些自動變量每次都會重新創建。
2.靜態變量
1> 定義:靜態變量是存儲在靜態內存中的,也就是不屬於堆棧。
2> 哪些是靜態變量:
所有的全局變量都是靜態變量
被關鍵字static修飾的局部變量也是靜態變量
3> 生命周期:靜態變量在程序運行之前創建,在程序的整個運行期間始終存在,直到程序結束。
1 #include <stdio.h> 2 3 int a; 4 5 void test() { 6 static int b = 0; 7 b++; 8 9 int c = 0; 10 c++; 11 12 printf("b=%d, c=%d \n", b, c); 13 } 14 15 int main() { 16 int i; 17 // 連續調用3次test函數 18 for (i = 0; i<3; i++) { 19 test(); 20 } 21 22 return 0; 23 }
* 第3行的變量a、第6行的變量b都是靜態變量,第9行的變量c、第16行的變量i是自動變量。
* 因為第6行的變量b是靜態變量,所以它只會被創建一次,而且生命周期會延續到程序結束。因為它只會創建一次,所以第6行代碼只會執行一次,下次再調用test函數時,變量b的值不會被重新初始化為0。
* 注意:雖然第6行的變量b是靜態變量,但是只改變了它的存儲類型(即生命周期),並沒有改變它的作用域,變量b還是只能在test函數內部使用。
* 我們在main函數中重復調用test函數3次,輸出結果為: b = 1, c = 1
b = 2, c = 1
b = 3, c = 1
3.寄存器變量
1> 定義:存儲在硬件寄存器中的變量,稱為寄存器變量。寄存器變量比存儲在內存中的變量訪問效率更高(默認情況下,自動變量和靜態變量都是放在內存中的)
2> 哪些變量是寄存器變量:
被關鍵字register修飾的自動變量都是寄存器變量
只有自動變量才可以是寄存器變量,全局變量和靜態局部變量不行
寄存器變量只限於int、char和指針類型變量使用
3> 生命周期:因為寄存器變量本身就是自動變量,所以函數中的寄存器變量在調用該函數時占用寄存器中存放的值,當函數結束時釋放寄存器,變量消失。
4> 使用注意:
由於計算機中寄存器數目有限,不能使用太多的寄存器變量。如果寄存器使用飽和時,程序將寄存器變量自動轉換為自動變量處理
為了提高運算速度,一般會將一些頻繁使用的自動變量定義為寄存器變量,這樣程序盡可能地為它分配寄存器存放,而不用內存
結構體
形式:
struct 結構體名{
類型名1 成員名1;
類型名2 成員名2;
……
類型名n 成員名n;
};
結構體變量的定義
1.先定義結構體類型,再定義變量
struct Student { char *name; int age; }; struct Student stu;
2.定義結構體類型的同時定義變量
struct Student { char *name; int age; } stu;
3.直接定義結構體類型變量,省略類型名
struct { char *name; int age; } stu;
結構體的注意點
不允許對結構體本身遞歸定義
結構體內可以包含別的結構體
定義結構體類型,只是說明了該類型的組成情況,並沒有給它分配存儲空間,就像系統不為int類型本身分配空間一樣。只有當定義屬於結構體類型的變量時,系統才會分配存儲空間給該變量
結構體變量占用的內存空間是其成員所占內存之和,而且各成員在內存中按定義的順序依次排列
結構體內存結構
1、結構體所占用的內存與其成員的聲明順序有關,例如:
1 struct stu1 { 2 char a; //1個字節 3 char b; //1個字節 4 int c; //4個字節 5 }; 6 struct stu2 { 7 char a; 8 int c; 9 char b; 10 }; 11 12 sizeof(stu1) --> 8 ?; //和系統有關 13 sizeof(stu2) --> 12 ?; //和系統有關
2、寬度和對齊的概念:
1)每個成員的寬度用sizeof即可知道,例如bool和char是1,short是2,int是4,string是8。 2)預編譯命令#pragma pack(n), n=1,2,4,8,16用來指定"對齊系數",#pragma pack()用來恢復默認。 3)數據成員對齊規則:結構體struct(或聯合體union)的成員,第一個成員的偏移量為0,以後每個數據成員的偏移量取其寬度和對齊系數的最小值。 4)結構體自身對齊規則:結構體自身也要對齊,對齊按照其成員最大寬度和對齊系數的最小值。
解析(#pragma pack(4)):
1 struct stu1 { 2 char a; //1個字節 偏移0 3 char b; //1個字節 偏移1 4 //由於對齊系數是4,這裡要補充2個字節。 5 int c; //4個字節 偏移4 6 }; 7 sizeof(stu1) --> 8 8 struct stu2 { 9 char a; //1個字節,偏移0,占1個字節 10 //由於對齊系數是4,這裡要補充3個字節 11 int c; //4個字節,偏移4 12 char b; //1個字節,偏移8 13 //由於對齊系數是4,這裡要補充3個字節 14 }; 15 sizeof(stu2) --> 12
結構體的初始化
將各成員的初值,按順序地放在一對大括號{}中,並用逗號分隔,一一對應賦值。
比如初始化Student結構體變量stu
struct Student { char *name; int age; }; struct Student stu = {"Zhy", 23};
只能在定義變量的同時進行初始化賦值,初始化賦值和變量的定義不能分開,下面的做法是錯誤的:
struct Student stu; stu = {"Zhy", 23};
結構體的使用
一般對結構體變量的操作是以成員為單位進行的,引用的一般形式為:結構體變量名.成員名
如果某個成員也是結構體變量,可以連續使用成員運算符"."訪問最低一級成員
相同類型的結構體變量之間可以進行整體賦值
結構體數組
定義方式
struct Student { char *name; int age; }; struct Student stu[5]; //第一種 struct Student { char *name; int age; } stu[5]; //第二種 struct { char *name; int age; } stu[5]; //第三種
初始化
struct { char *name; int age; } stu[2] = { {"Zhy", 23}, {"zz", 18} };
結構體作為函數參數
將結構體變量作為函數參數進行傳遞時,其實傳遞的是全部成員的值,也就是將實參中成員的值一一賦值給對應的形參成員。因此,形參的改變不會影響到實參
指向結構體的指針
* 每個結構體變量都有自己的存儲空間和地址,因此指針也可以指向結構體變量
* 結構體指針變量的定義形式:struct 結構體名稱 *指針變量名
* 有了指向結構體的指針,那麼就有3種訪問結構體成員的方式
結構體變量名.成員名
(*指針變量名).成員名
指針變量名->成員名
#include <stdio.h> int main(int argc, const char * argv[]) { // 定義一個結構體類型 struct Student { char *name; int age; }; // 定義一個結構體變量 struct Student stu = {"Zhy", 23}; // 定義一個指向結構體的指針變量 struct Student *p; // 指向結構體變量stu p = &stu; /* 這時候可以用3種方式訪問結構體的成員 */ // 方式1:結構體變量名.成員名 printf("name=%s, age = %d \n", stu.name, stu.age); // 方式2:(*指針變量名).成員名 printf("name=%s, age = %d \n", (*p).name, (*p).age); // 方式3:指針變量名->成員名 printf("name=%s, age = %d \n", p->name, p->age); return 0; }
枚舉
概念:枚舉是C語言中的一種基本數據類型,並不是構造類型,它可以用於聲明一組常數。當一個變量有幾個固定的可能取值時,可以將這個變量定義為枚舉類型。
例如:可以用一個枚舉類型的變量描述星期,因為星期只有7中可能星期一至星期日
枚舉類型的定義
一般形式為:enum 枚舉名 {枚舉元素1,枚舉元素2,……};
1.先定義枚舉類型,再定義枚舉變量
enum Season {spring, summer, autumn, winter}; enum Season s;
2.定義枚舉類型的同時定義枚舉變量
enum Season {spring, summer, autumn, winter} s;
3.省略枚舉名稱,直接定義枚舉變量
enum {spring, summer, autumn, winter} s;
上面三種方式定義的都是枚舉變量s
枚舉使用注意
1> C語言編譯器會將枚舉元素(spring、summer等)作為整型常量處理,稱為枚舉常量。
2> 枚舉元素的值取決於定義時各枚舉元素排列的先後順序。默認情況下,第一個枚舉元素的值為0,第二個為1,依次順序加1。
enum Season {spring, summer, autumn, winter};
也就是說spring的值為0,summer的值為1,autumn的值為2,winter的值為3
3> 也可以在定義枚舉類型時改變枚舉元素的值
enum season {spring, summer=3, autumn, winter};
沒有指定值的枚舉元素,其值為前一元素加1。也就說spring的值為0,summer的值為3,autumn的值為4,winter的值為5
枚舉基本操作
enum Season {spring, summer, autumn, winter} s; s = spring; // 等價於 s = 0; s = 3; // 等價於 s = winter;