自定義類型的認識

結構體

typedef的使用

typedef struct S
{
    char c[20];
    struct S* next;
}S;
#include <stdio.h>
int main()
{
    S s1;
    return 0;
}

typedef是一個很實用的標識符,能夠將類型重命名,好比編譯器就將,unsigned int 重命名爲size_t,在這裏也是一樣的將 struct S重命名爲S,使用的時候更加方便。我本人是比較喜歡在結構體中使用 typedef 的。ide

結構體的自引用

struct S
{
    char c[20];
    struct S* next;
};
#include <stdio.h>
int main()
{
    return 0;
}

結構體裏面是不能直接出現或者引用自身的,若是出現就會進入無限的自引用,可是能夠出現指向自身的指針,直到指針溢出指針就無效了,也不會再引用。優化

結構體內存對齊

#pragma pack(4)
//設置默認對齊數爲4
//MSVC的默認對齊數爲8
typedef struct S1
{
    char a;
    int c;
    char b;
}S1;
//typedef struct S2
//{
//  char a;
//  char b;
//  int c;
//}S2;
//typedef struct S3
//{
//  int c;
//  char a;
//  char b;
//}S3;
#pragma pack()
//取消設置的默認對齊數
#include <stdio.h>
#include <stddef.h>
int main()
{
    S1 n1 = { 0 };
    printf("%d\n", sizeof(n1));
    //S2 n2 = { 0 };
    //printf("%d\n", sizeof(n2));
    //S3 n3 = { 0 };
    //printf("%d\n", sizeof(n3));

    printf("%d\n", offsetof(S1, a));
    printf("%d\n", offsetof(S1, b));
    printf("%d\n", offsetof(S1, c));
    return 0;

}

內存對齊的現象在各個編譯器中都會存在,可是沒有統一的標準,內存對齊,指的是在結構體內部,對內部的元素進行內存分配時存在的一種規律,之內存對齊數爲標準,MSVC中默認的內存對齊數爲8,成員類型的對齊數爲自己字節數,好比int的對齊數爲4,double的對齊數爲8,對齊數是默認對齊數與成員對齊數的較小值。
以第一個元素存儲開始的內存位置爲0,下一個成員開始的位置的須要是相對於第一個成員內存開始的位置的對齊數的整數倍,好比,顯存進去一個char 佔一個字節,那麼以這個字節爲開始,後面要繼續存進去一個int (四個字節)的話,就須要在下一個是4的整數倍的位置開始,也就是說不能從該char以後的第1,2,3個位置存,得從第4個開始存,因此可以明顯的知道,若是先定義char 再定義int 那麼這兩個成員在結構體中佔據了8個字節,若是先定義int,在定義char,那麼這兩個成員只會佔5個字節
#pragma pack()是能夠改默認對齊數使,將要改的數字放在括號裏,用完以後要在後面再加一句#pragma pack(),使得默認對齊數恢復默認。指針

位段

//位段 - 位指的是二進制位
#include <stdio.h>
struct A
{
    int _a : 2;//只要佔2個比特位
    int _b : 5;//只要佔5個比特位
    int _c : 10;//只要佔10個比特位
    int _d : 30;//只要佔30個比特位
};
//注重移植的程序不該該使用位段
//int 位段被當成有符號數仍是無符號數是不肯定的
//位段中的最大位數是不能肯定的
//位段中的從左向右分配仍是從右向左分配還沒有定義
//當一個結構體包含兩個位段,第二個位段成員較大時,沒法容納第一個位段剩餘位時,是捨棄剩餘位仍是利用,這是不肯定的
//位段的數字不能大於32
int main()
{
    struct A s;
    printf("%d\n", sizeof(s));//8個字節
    return 0;
}
typedef struct S
{
    char a : 3;
    char b : 4;
    char c : 5;
    char d : 4;
}S;

int main()
{
    S s = { 0 };

    s.a = 10;
    s.b = 20;
    s.c = 3;
    s.d = 4;

    return 0;
}

位段也是一種自定義類型,不肯定性較多,最重要的是不能移植,可是優勢是可以優化內存管理。調試

枚舉

enum Color
{
    RED,
    GREEN,
    BULE
};

#include <stdio.h>
int main()
{
    enum Color c = RED;

    return 0;
}

若不賦初值,則自動進行了賦初值,對應的是RED = 0, GREEN = 1, BULE = 2也能夠進行手動賦初值
使用方法結構體相似,可是定義出來的變量的值只能取枚舉內部的值
優勢
1.增長了代碼的可讀性
2.和define定義的標識符比枚舉有類型檢查,更加嚴謹code

  1. 防止了命名污染(封裝)
  2. 便於調試
  3. 使用方便,一次能夠定義多個常量
    合 - 聯合體 - 共用體

    聯合(聯合體 /共用體)

聯合體的認識

#include <stdio.h>

typedef union Un
{
    char c;//1個字節
    int i;//4個字節
}Un;
int main()
{
    Un u;
    printf("%d\n", sizeof(u));
    printf("%p\n", &u);
    printf("%p\n", &(u.c));
    printf("%p\n", &(u.i));
    //i和c不能同時使用
    return 0;
}

聯合體也是一種自定義類型,意思是,內部的成員使用了同一塊空間,聯合體所佔的空間大小是由內部較大的成員決定的,當最大成員不是最大對齊數的整數倍時,就要對齊到最大對齊數的整數倍,可是兩個成員不能同時使用,由於,同一塊空間不能存得下兩個元素。內存

簡單的使用

#include <stdio.h>

int check_sys()
{
    union U
    {
        char c;
        int i;
    }u;
    u.i = 1;
    return u.c;

    //int a = 1;
    ////返回1 表示小端
    //return *(char*)&a;
}
int main()
{
    int ret = check_sys();

    if (1 == ret)
    {
        printf("小端字節序\n\a");
    }
    else
    {
        printf("大端字節序\n");
    }

    //高高低低 小端字節序
    //高低低高 大端字節序
    return 0;
}

對於大端字節序和小端字節序,我有點記不住,就發明了個簡短的總結,高高低低,意思是數字的高位存儲在內存的高位,數字的低位存儲在內存的低位,叫作小端字節序,高低低高,就是數字的高位存儲在內存的低位,數字的低位存儲在內存的高位,叫作大端字節序。編譯器

當初是寫了一個a=1來進行讀取第一個字節的數字判斷大小端,如今用聯合體,由於公用同一塊空間,因此在空間內部存進去數字以後,不一樣的類型讀取的位數不一樣,可是讀取的順序都是由低地址到高地址,因此經過char訪問由int存進去的數字,就可以判斷大小端了。it

相關文章
相關標籤/搜索