C語言單元小結(6)
結構體
1.位元組對齊
知識點:
對齊規則:1、我們的結構體變數在4位元組對齊的位置。
2、第一個成員,從結構體開始的地址處存放。具體佔多少位元組,由緊挨著下個元素決定。
3、整個結構體是預設位元組對齊的最小整數倍。
//結構體預設的位元組對齊:成員變數最大的那個型別所佔位元組
舉例:
#include<stdio.h>
struct data
{
char a;//填充1
short b;//填充4
double c;
}A;
sizeof(A)=16 byte
注意:
結構體不允許此類定義
struct data
{
int a;
struct data s; // 此時結構體型別(定義)不完整
}s;
2.結構體巢狀
知識點:
1、不會因為有結構體成員,而影響你的基本型別,只決定預設對齊方式
2、裡面的結構體對齊方式,已經在外面決定了(遍歷整個完整結構體)
舉例:
struct data1
{
int a;
short b;
int c;
double e;
};//24
struct data
{
char a;//2
short b;//2
int c;//4
struct data1 s;//24
char ch;//8
}//40
3.對齊指令
知識點:
#pragma pack(n) (1、2、4、8、…..)
#pragma pack()
表示一個區間,區間內的結構體有此設定
指定的對齊方式和結構體自身預設對齊方式取最小的。
作用:
1、充分利用記憶體空間,犧牲了速度,降低了訪問效率。
2、提高效率、效能,犧牲了記憶體空間。
舉例:
#include <stdio.h>
#pragma pack(2)
struct
{
int a;
char b;
}A;
#pragma pack()
sizeof(A)=6 byte
4.位欄位
知識點:
1.專用於結構體,結構體成員的型別必須是:int || unsigned int
2.欄位不可取地址
3.0欄位,不可訪問,只是佔位整個字中剩下的位,全部寫0
4.無名欄位,不可訪問,只是佔位
5.字(word)=32bit
6.下一個欄位不夠在剩餘的字存放時,必須另起一個字。欄位不能跨字。
7.超出欄位表示的資料範圍,結果不可預期
8.若最後一個字沒有填滿則也將填滿後返回
舉例:
//寫法
struct data
{
unsigned a : 1; // 1就是一個bit,範圍:0~1
int :0; // 0欄位,不可訪問,只是佔位 整個字剩下的位,全部寫0
unsigned b : 2; // 2就是兩個bit,範圍:0~3
int:31; // 無名欄位,不可訪問,只是佔位
}A;
sizeof(A)=3 word=12 byte
注意:
struct
{
int a:1;//此時a只有一位且僅為符號位,取值範圍為(-1~0)
};
5.網路結構體
知識點:
標頭檔案路徑:/usr/include/netinet/in.h
例項:
標頭檔案內容:
/* Internet address. */
typedef uint32_t in_addr_t;
typedef uint16_t in_port_t; // 16的無符號short型別
struct in_addr
{
in_addr_t s_addr;
};
/* Structure describing an Internet socket address. */
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_); /* AF_INET IPv4 Internet protocols 指定網路地址型別*/
in_port_t sin_port; /* Port number. 埠號 16位無符號short */
struct in_addr sin_addr; /* Internet address. 具體的網址32位無符號int*/
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
利用以上內容定義sockaddr_in結構體並賦值
#include <stdio.h>
#include <features.h>
#include <stdint.h>
#include <sys/socket.h>
#include <bits/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 1234
#define ADDR "172.25.254.3"
void set_struct (struct sockaddr_in *p)
{
p->sin_family = AF_INET;
p->sin_port = htons(PORT);
p->sin_addr.s_addr = inet_addr(ADDR);
}
int main(void)
{
struct sockaddr_in s;
set_struct(&s);
return 0;
}
6.結構體與函式
知識點:
結構體中不能呼叫函式,需要宣告函式指標
實現:
#include <stdio.h>
typedef int (*p_func)(int a,int b);//重新命名函式型別為函式指標
int add(int a,int b)//函式本體,實現具體功能
{
return a+b;
}
struct data
{
int a;
int b;
p_func p;//函式指標
}s;
void set_func (struct data *s)//結構體賦值
{
s->a=1;
s->b=2;
s->p=add;
}
int ret_func (struct data *s)//函式使用
{
return s->p(s->a,s->b);
}
int main(void)
{
struct data s;
set_func(&s);
int ret=ret_func(&s);
printf("%d\n",ret);
return 0;
}
7.柔性陣列
知識點
標誌是結構體的最後一個成員是一個沒有定義大小的陣列
需要malloc動態申請陣列大小後使用
實現
1.int型陣列
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int len;
int arr[];
}S;
S* create_soft_arr(int len)
{
S* p=(S*)malloc(sizeof(S)+sizeof(int)*len);
if(NULL==p)
printf("malloc error");
p->len=len;
return p;
}
void set_arr(S* p)
{
int i=0;
for(i=0;i<(p->len);i++)
{
if(i<=1)
p->arr[i]=1;
else if(i>=2)
p->arr[i]=p->arr[i-1]+p->arr[i-2];
}
}
void show_arr(S* p)
{
int i=0;
for(i=0;i<(p->len);i++)
{
printf("%d ",p->arr[i]);
}
}
int main(void)
{
S* p = create_soft_arr(5);
set_arr(p);
show_arr(p);
return 0;
}
2.char型陣列
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct
{
int len;
char arr[];
}S;
int main(void)
{
char a[]="abcd";
S* p=(S*)malloc(sizeof(S)+sizeof(a));
strcpy(p->arr,a);
printf("%s",p->arr);
return 0;
}
巨集
舉例
1.
__FILE__:所在檔案
__LINE__:所在行數
2.
#define handle_error(msg) (do{perror(msg); exit(EXIT_FAILURE);}while(0))
解釋:
perror:檢查系統錯誤的巨集.一旦發生了,系統錯誤就會產生一個錯誤數字(errno),對應相應的錯誤字串。
EXIT_SUCCESS 和 EXIT_FAILURE:C 標準定義了兩個值,可以作為exit()的引數,來分別指示是否為成功退出。
exit():傳遞給的是父程序,或者shell終端
3.linux核心裡的兩個巨集:在驅動應用中很廣泛
off_set_of(type, member)計算結構體內元素的偏移量
containe_of(ptr, type, member)結構體的首地址,需傳入該結構體的成員地址
原理:
#define off_set_of(type, member) ((long)&((type*)0->member))
將0轉換為結構體型別的指標,再指向其成員,將地址轉換為數值接收即為偏移量
#define container_of(ptr, type, member) ({typeof ((type*)0->member) *mystr = ptr ; (type *)((long)mystr-off_set_of(type,member));})
將0轉換為結構體型別指標,再指向其成員,呼叫typeof函式得到成員型別並申請一個該型別的指標,賦值為傳入的成員地址str;該地址減去偏移量即結構體的首地址
補充:
typeof(),得到引數的型別。引數可以是變數名或者表示式。
int *p, a;
//變數名作引數
typeof(p) p_a = &a;
//表示式作引數
typeof(*p_a) b = 23;
列舉
知識點:
列舉:定義常量符號,就是巨集定義常數的集合體。如四季,星期,意義相關的常數
舉例:
實現密碼鎖,迴圈輸入至出現正確密碼
#include <stdio.h>
typedef enum//書寫格式
{
STATE1,//以逗號結尾
STATE2,
STATE3,
STATE4,
}S;
int main(void)
{
S c_state = STATE1;//宣告一個變數的格式,以列舉值賦值
printf("Input key:");
int num=0;
while(1)
{
scanf("%d",&num);
switch(c_state)
{
case STATE1:
if(num == 2)
c_state=STATE2;
else
c_state=STATE1;
break;
case STATE2:
if(num == 2)
c_state=STATE3;
else
c_state=STATE1;
break;
case STATE3:
if(num == 1)
c_state=STATE4;
else
c_state=STATE1;
break;
default:
c_state=STATE1;
break;
}
if(c_state==STATE4)
{
printf("successful\n");
return 0;
}
}
return 0;
}