c語言基礎(八)

字節對齊

結構體內成員對齊規則:
1、我們的結構體變量本身就是在4字節對齊的位置,編譯器幫我們做的事。
2、第一個成員,就從結構體開始的地址處,存放。這個元素,具體佔多少字節,由緊挨着下個元素決定。
3、整個成員變量自身都對齊 了,還沒有結束。
4、整個結構體還要是默認字節對齊的最小整數倍。
結構體默認的字節對齊:成員變量最大的那個類型所佔字節

#include<stdio.h>

typedef struct data
{
        int a;               // 4
        char b;             // 1 + 1(填充)
        short c;           // 2    最後整個結構體自身還要對齊
}D;
int main(void)
{
    printf("sizeof(D)=%d.\n",sizeof(D));
    return 0;
}
結果爲8byte

注意:

struct data
{
    int a;
    struct data s;   // 1.此時結構體類型(定義)不完整  2.C語言不允許
}s;
int main(void)
{
    unsigned short a = 0x1234;  
    unsigned char * p1 = (unsigned char *)&a, i = 8;
    //*p1 << i,就產生了臨時變量,內存中你不知道地址,但是有一個默認類型(int)
    printf("sizeof(*p1 << i) = %d.\n", sizeof(*p1 << i));
    printf("0x%x.\n", *p1 <<= i);    // printf("%d.\n", a *= 2);
    printf("0x%x.\n", *(p1+1));
}
結果如下:sizeof(*p1 << i)=4
        0x0
        0x12
#include<stdio.h>

struct data
{
        int a;                              // 4 + 4(padding)

        struct data *p_next;              //64bit機器指針永遠永遠佔8字節
}S;
int main(void)
{
    printf("sizeof(S)=%d.\n",sizeof(S));
    return 0;
}
結果爲16byte

結構體的嵌套

#include<stdio.h>
struct data1
{
        int a;
        short b;
        int c;
        double e;
};//24

typedef struct data
{
        char a;//2
        short b;//2
        int c;//4
        struct data1 s;//24 
        char ch;//8
}S;

int main(void)
{
    printf("sizeof(S)=%d.\n",sizeof(S));
    return 0;
}
結果爲40

字段對齊

位字段:專用於結構體,結構體成員的類型必須是:int || unsigned int
有時侯,結構體成員表示的數據很小,就用幾個位來表示。
0字段,不可訪問,只是佔位整個字中剩下的位,全部寫0
無名字段,不可訪問,只是佔位
字(word)=32bit
下一個字段不夠在剩餘的字存放時,必須另起一個字。字段不能跨字。
超出字段表示的數據範圍,結果不可預期
若最後一個字沒有填滿則也將填滿後返回
字段不可取地址

#include <stdio.h>
struct data1
{
    unsigned a : 1;     // 1就是一個bit,範圍:0~1
    int : 31;           // 無名字段,不可訪問,只是佔位
    unsigned b : 2;     // 2就是2個bit,範圍:0~3
    unsigned c : 2;   // 28位 
}s1;

int main(void)
{
    printf("szieof(s1) = %d.\n", sizeof(s1));
    return 0;
}
結果爲8byte,同時也是2word

特別的是:

struct
{
    int a:1;//此時a只有一位且僅爲符號位,取值範圍爲(-1~0)
};

對齊指令

#pragma pack(n) (1、2、4、8、.....)
#pragma pack()

這兩個配合使用,表示一個區間,只有這個區間內的結構體享受這個待遇。
設置 對齊。
如果將n設置爲1,就是不對齊
1、充分利用內存空間,犧牲了速度,降低了訪問效率。
2、提高效率、性能,犧牲了內存空間。
總結:你指定的對齊方式和結構體自身默認對齊方式,倆者取最小的。

#include <stdio.h>
#include <stdlib.h>

#pragma pack(2)
struct data                 // 16
{
    int a;
    char b;
    double c;
}s;

struct data1
{
    char ch;
}s1;
#pragma pack()

檢查系統錯誤的宏

一旦發生了,系統錯誤就會產生一個錯誤數字(errno),對應相應的錯誤字符串。
C標準定義了兩個值 EXIT_SUCCESS 和 EXIT_FAILURE,可以作爲exit()的參數,來分別指示是否爲成功退出。
exit(參數)傳遞給的是父進程,或者shell終端

#define handle_error(msg)   do{perror(msg); exit(EXIT_FAILURE);}while(0)

Linux內置宏

linux內核裏的兩個宏:在驅動應用中很廣泛。
off_set_of(type, member)計算結構體內元素的偏移量
containe_of(ptr, type, member),ptr是結構體裏成員的指針,通過調用這個宏計算出結構體的首地址.包含兩句代碼(表達式),必須要加{}.
這兩個宏:內核雙鏈表。

分析:
#define off_set_of(type, member)  ((long)&(((type *)0)->member))
1、(type *)0指向結構體零地址                                         
2((type *)0)->member得到了結構體某個成元變量名    
3、給這成員變量名,取地址(相對於零地址),此時&(((type *)0)->member)表示是指針類型 
4、強制類型轉換成(long)

#define container_of(ptr, type, member)   ({typeof(((type *)0)->member) *_mptr = ptr;(type *)((char *)_mptr-off_set_of(type, member));})
1、得到結構體成員變量的類型
2、指針賦值(得到真實成員變量的地址值)
3、減去偏移量得到一個數字,該數字和結構體本身首地址在數值上一樣
4、最後強制類型轉換爲結構體指針類型

舉個栗子:

#include <stdio.h>

#define off_set_of(type, member)  ((long)&(((type *)0)->member))
#define container_of(ptr, type, member)     ({typeof(((type *)0)->member) *_mptr = ptr;(type *)((char *)_mptr-off_set_of(type, member));})

struct da
{
    int a;
    short b;    
    int c;
    double e;
};                      // 16
struct data
{
    char a;
    short b;
    int c;              // 8
    struct da s;    // 16
    char ch;
}s = {1, 3, 10, 3.14, 1.41};
//1、不會因爲有結構體成員,而影響你的基本類型,決定默認對齊
//2、裏面的結構體對齊方式,已經在外面決定了(遍歷整個完整結構體)

int main(void)
{
    printf("sizeof(s) = %d.\n", sizeof(s));
    //結構體自身的首地址
    printf("&s = %p.\n", &s);
    struct da *p = container_of(&s.s.b, struct da, b);
    printf("p = %p.\n", p);
    printf("&s.s = %p.\n", &s.s);
    return 0;
}
p->a == (struct data *)0->a;
/*
//讓你明白強制轉換0地址
struct data *p;     
unsigned long a = 0;        
p = (struct data *)a;  
p->a == (struct data *)0->a;
*/

關於typeof

typeof(),()裏面的參數可以是變量名或者表達式。
typeof(int) p

int *p, a;
typeof(p) p_a = &a;//將p_a的類型轉換爲p的類型,即指針類型
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章