課堂代碼(6_19)

#ifndef _TEST_H_
#define _TEST_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
#include <math.h>
#include <time.h>
#include <assert.h>
#include <errno.h>










#endif // !_TEST_H所有變量聲明

//#include "58.h"
//
//
////int main()
////{
////
////	return EXIT_SUCCESS;
////}
//
//
//
//
//int main()
//{
//
//	int a[100];
//	printf("請輸入有多少個數據:-->");
//	int num = 0;
//	scanf("%d", &num);
//
//	printf("請輸入數據列表:");
//	for (int i = 0; i < num; i++)
//	{
//		scanf("%d", &a[i]);
//	}
//
//	int max = a[0];
//	for (int i = 0; i < num; i++)
//	{
//		if (max < a[i])
//		{
//			max = a[i];
//		}
//	}
//
//	printf("最大值:%d\n", max);
//	return EXIT_SUCCESS;
//}
//
//
//
//
//
//////聯合體本身大小不是把所有類型加起來的大小
//////一般是有聯合體內部最大元素的大小決定
//////所有元素共享空間
////union _un
////{
////	int a;
////	char b;
////	char c;
////	char d;
////	int e;
////};
////union _UN
////{
////	int a;
////	char b;
////};
////int main()
////{
////	union _un obj;
////	obj.a = 0x11223344;
////	printf("0x%p\n", &obj.a);
////	printf("0x%p\n", &obj.b);
////	printf("0x%p\n", &obj.c);
////	printf("0x%p\n", &obj.d);
////	printf("0x%p\n\n", &obj.e);
////
////	printf("%x\n", obj.a);
////	printf("%x\n", obj.b);
////	printf("%x\n", obj.c);
////	printf("%x\n", obj.d);
////	printf("%x\n\n\n", obj.e);
////
////	union _UN OBJ;
////	OBJ.a = 1;
////	printf("%d\n", OBJ.b);
////
////	return EXIT_SUCCESS;
////}
//
//////union _u {
//////	int a;
//////	int b;
//////	char c;
//////};
//////int f1(int a)
//////{
//////	return 2 * a;
//////}
//////int f2(int b)
//////{
//////	return 3 * b;
//////}
//////char f3(char c)
//////{
//////	return 4 * c;
//////}
//////int main()
//////{
//////	union _u obj;
//////	obj.a = 10;
//////	fi(obj.a);
//////
//////	obj.b = 10;
//////	fi(obj.b);
//////
//////	obj.c = 10;
//////	fi(obj.c);
//////	return EXIT_SUCCESS;
//////}
//
//
//
//
////枚舉
////枚舉是一種類型,編譯時會進行類型檢查
////enum color
////{
////	RED,
////	BLACK,
////	GREEN,
////	YELLOW,
////	BLUE
////};
////typedef enum color color_c;
////int main()
////{
////	enum color c = RED;
////	printf("%d\n", c);
////	printf("%d\n", RED);
////	printf("%d\n", BLACK);
////	printf("%d\n", GREEN);
////	printf("%d\n", YELLOW);
////	printf("%d\n", BLUE);
//////	RED = 100;  //錯誤,枚舉類型本身是整型常量,不可以被賦值
////
////	color_c a = BLUE;
////	printf("\n%d\n", a);
////	return EXIT_SUCCESS;
////}

引言

結構體(Structure)[在C標準中有時也稱爲聚合體(Aggregate)]是統一在同一個名字之下的一組相關變量的集合,它可以包含不同類型的變量
結構體通常用來定義儲存在文件中的記錄
將指針和結構體聯合使用,可以實現更復雜的數據結構,如鏈表、隊列、堆棧和數

2 結構體的定義

關鍵字 struct 用來引出一個結構體定義
關鍵字 struct 之後的標識符稱爲結構體標記,它是用來給這個結構體定義命名的。結構體標記與關鍵字 struct 一起用來聲明具有這個結構體類型的變量
在結構體定義的花括號內聲明的變量是結構體的成員
同一個結構體類型中的成員不能重名
每一個結構體定義都必須用一個分號來結束
結構體的成員可以是具有原始數據類型的變量,也可以是聚合體聚合體數據類型的變量
一個結構體不能包含它自身的實例,但可以包含一個指向相同類型的另外一個對象的指針
包含一個指向自身結構體類型的指針爲成員的結構體稱爲自引用結構體。自引用結構體被用來構建鏈式數據類型
結構體定義創建一種新的可用來定義變量的數據類型
struct card {
char* face;
char* suit;
}aCard, deck[52], * cardPtr;
將aCard聲明爲一個類型爲struct card 的變量
將deck聲明爲一個包含了52個具有struct card類型的元素數組
將cardPtr聲明爲一個指向struct card類型的指針
上述語句完成後,系統爲一個類型爲struct card的變量aCard、數組feck中的52個struct card類型的元素以及一個未初始化的指向struct card類型的指針申請空間
結構體定義中的結構體標記名是可以省略的。若結構體定義中沒有標記名,則該結構體類型的變量就只能在結構體定義的同時進行聲明
運用於結構體上的合法操作只有:
將結構體變量賦值給其他具有相同類型的結構體變量
用&運算符取得結構體變量的地址
訪問結構體變量中的成員
用sizeof運算符確定結構體變量的大小

3 結構體的初始化

可以採用初始值列表來對結構體進行初始化
若列表中初始值個數少於結構體成員的個數,則剩餘成員自動地被初始化爲0(成員是指針時,被初始化爲NULL)
在函數之外定義的結構體變量,若沒有顯式地在外部進行初始化,將被自動地初始化爲0或NULL
通過將一個結構體變量賦值給另外一個與其類型相同的結構體變量,或逐個對結構體變量成員進行賦值來實現結構體變量的初始化

4 結構體訪問

可以用結構體成員運算符(.)和結構體指針運算符(–>)來訪問結構體的成員
結構體成員運算符是通過結構體變量名來訪問結構體成員的
結構體指針運算符通過指向結構體的指針來訪問結構體成員

5 在函數中使用結構體

將結構體傳遞給函數有三種方式:
傳遞結構體的個別成員
傳遞整個結構體
傳遞一個指向結構體的指針
結構體變量在默認的情況下以傳值的方式被傳遞給一個函數的
若採用(模擬)按引用方式來傳遞一個結構體,傳遞給被調函數的是結構體變量的地址。結構體數組(與其他數組一樣)都是自動以(模擬)按引用方式傳遞的
要想以傳值的方式來傳遞一個數組,可創建一個以該數組爲成員的結構體,然後以傳值的方式來傳遞這個結構體。這樣數組就以傳值的方式被傳遞過去了

6 typedef的使用

關鍵字typedef提供了一種爲已定義好的數據類型創建同義詞的機制
爲了創建更簡短的類型名稱,通常使用typedef 來定義結構體類型的名字
typedef 還常常被用來爲基本數據類型創建一個別名。例如,一個處理4字節長整數的程序運行於某個系統時,會被要求用類型 int 定義變量;而運行於另外一個系統時,則會被要求用類型long定義變量。爲了可移植,程序就用typedef 爲4字節長的整數創建一個別名,如Integer。這樣,只需對別名Integer的定義做一次修改,就可使程序能夠運行於兩個不同的系統之上

7 共用體

共用體是以與聲明一個結構體相同的格式,通過關鍵詞union來聲明的。它的成員共享同一個存儲空間
共用體的成員可以是任意數據類型,但是存儲一個共用體所用的字節總數,必須保證至少足以能夠容納其最大的成員
每次只允許訪問共用體的一個成員,確保以正確的數據類型來訪問共用體中的數據
可對共用體執行的操作有:兩個具有相同類型的共用體之間的賦值,用&運算符取得一個共用體變量的地址,用結構體成員運算符或結構體指針運算符來訪問共用體的成員
可以在聲明語句中,用與其第一個成員數據類型相同的數值來對共用體進行初始化

8 位運算符

在計算機內部,,所有數據都是以二進制數序列的形式來表示的,每個數位取值0或1
在大多數計算機系統中,8個二進制數位的序列組成一個字節——存儲一個字符型變量的標準存儲單元,其他數據類型的變量則語言更多的字節來存儲
位運算符用來處理整型操作數(如字符型 char 、短整型 short 、整型 int 以及長整型 long );有符號整型和無符號整型的各個數位。通常將操作數當成無符號整型數據來處理
位運算符有:按位與(&),按位或(||),按位異或(^),左移(>>),右移(<<)和按位取反(~)
按位與、按位或和按位異或都是逐位地對兩個操作數進行比較。當兩個操作數相應的二進制數位都是1時,按位與運算符纔會將運算結果的二進制數位設置爲1。只要兩個操作數相應數位有一個是1(或者兩個都是1),按位或運算符就會將運算結果的相應的二進制數位設置爲1。僅當兩個操作數相應的二進制數位只有一個是1時,按位異或運算符纔將運算結果相應的二進制數位設置爲1
左移運算符是將其左邊的操作數按位向左移動,移動的位數由其右邊的操作數指定。右邊騰空的數位補0,左邊移出的數位丟棄
右移運算符是將其左邊的操作數按數位向右移動,移動的數位由其右邊的操作數指定。對無符號整數進行右移運算時,左邊騰空的數位補上0,右邊移出的數位丟棄
按位取反運算符是將其操作數中所有的0設置成1,所有的1設置成0,然後得到運算結果
通常,按位與運算符要與一個被稱爲掩碼的操作數一起使用,掩碼是某些特定位被置成1的一個整數。掩碼用來在選取某些數位的同時屏蔽掉其他數位
符號常量CHAR_BIT(在頭文件 < limits.h >中定義)表示一個字節中的二進制位數(標準情況下是8)。可以用它來增強一個位操作程序的可移植性和可擴展性
每個二進制位運算符都有一個相應的位運算後賦值運算符

9 位域

當結構體或共用體中有無符號整型或有符號整型成員時,C語言允許用戶指定這些成員所佔用的存儲位數,這被稱爲位域。通過將數據存儲在它們所需的最小數目的存儲位內,位域能夠提高存儲空間的利用率。
聲明一個位域的方法是:在無符號整型或有符號整型的成員的名字後面加上一個冒號 ( : )和一個表示位域寬度的整型常數。這個常數值必須是一個整數,其範圍取值在0到系統中存儲一個整形所需二進制位數之間的閉區間內
對結構體中位域成員,是按照與其他成員相同的方式來訪問的
可以指定一個無名位域作爲結構體中的補位
寬度爲0的無名位域將使下一個位域對齊在一個新的存儲單元的邊界上

10 枚舉常量

通過關鍵字enum引入的枚舉常量,是一個用標識符表示的整形常量的稽覈。除非專門定義,枚舉常量中枚舉的值都是從0開始並且遞增1
在定義枚舉類型時,可以通過給標識符賦值來顯式地給枚舉常量定值

11 自定義類型

結構體
枚舉
聯合

12 結構體內存對齊

結構體的第一個成員一定放在結構體起始位置的 0 偏移處,截止字節由其本身類型大小決定
從第二個成員開始,每個成員都要放在某個對齊數的整數倍的偏移處(這個對齊數:成員自身的大小和默認對齊數的較小值 ——8(VS)——4(linux))
結構體的總大小必須是所有成員的對齊數中最大對齊數的整數倍
如果有嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,結構體的總大小是所有對齊數(包含嵌套結構體的對齊數)中最大對齊數的整數倍

13 爲什麼結構體內存對齊

平臺原因(移植原因):不是所有的硬件平臺都能訪問任意地址上的任意數據的;某些硬件平臺只能在某些地址處取某些特定類型的數據,否則拋出硬件異常
性能原因:數據結構(尤其是棧)應該儘可能地在自然邊界上對齊。原因在於,爲了訪問未對齊的內存,處理器需要做兩次內存訪問;而對齊的內存訪問僅需要一次訪問

總體來說:
1.結構體的內存對齊是拿空間來換取時間的做法
2.所以在設計結構體的時候,爲了滿足對齊,又要節省空間可以讓佔用空間小的成員儘量集中在一起

14 修改默認對齊數

#pragma pack (1)//默認對齊數修改爲4,改爲 1 即爲沒對齊,一般修改爲2,4,6……
struct S3 {
double d;
char c;
};
#pragma pack()//取消默認對齊數的修改

15 結構體傳參

函數傳參時,參數是需要壓棧的。如果傳遞一個結構體對象的時候,結構體過大,參數壓棧的系統開銷較大,就會導致性能的下降
結構體 傳參的時候,要傳結構體的地址

16 位段

位段的聲明和結構體是類似的,但是有兩個不同:
1.位段的成員必須是 int、unsigned int或signed int 、char。
2.位段的成員名後邊有一個冒號和一個數字。
比如:struct A {
int _a : 2;
int _b : 3;
int _c : 10;
int _d : 30;
};
A就是一個位段類型
冒號後邊的數字表示要存儲 _a 需要兩個字節, _b 需要3個字節

位段的成員可以是 int、unsigned int或signed int 、char(屬於整形家族)類
位段的空間上是按照需要以4個字節(int)或者1個字節(char)的方式來開闢的
位段涉及很多不確定因素,位段是不跨平臺的,可移植的程序應避免使用位段
VS中多餘位段不會被捨棄

17 枚舉的優點

增加代碼的可讀性和可維護性
和 #define 定義的標識符比較枚舉有類型檢查,更加嚴謹
防止了命名污染(封裝)
便於調試
使用方便,一次可以定義多個常量

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章