1. 程式人生 > >C語言單元小結(6)

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;
}