20160221.CCPP體系詳解(0031天)

尹成發表於2016-03-15

程式片段(01):01.結構體靜態陣列.c+02.結構體動態陣列.c
內容概要:結構體陣列

///01.結構體靜態陣列.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//01.C語言的結構體特點:
//  1.C語言要求結構體或者共用體至少得有一個成員
//      也就是說C語言並不允許空結構體或者空共用體的出現!
//  2.嚴格區分採用結構體型別到底定義的是哪種具體變數:
//      普通變數+指標變數+陣列變數
struct
{//匿名結構體
    int num;
} *pStruct, structArr[10], structVar;

//02.定義結構體的不同分類方式:
//  1.按照宣告型別和定義變數的時機不同:
//      宣告結構體型別的同時定義結構體變數
//      宣告結構體型別之後再定義結構體變數
//  2.按照所屬記憶體空間的不同:
//      棧記憶體:
//      堆記憶體:
//      靜態區:
//注:結構體變數(普通+指標+陣列)
struct MyStruct01
{//標準結構體
    int id;
    char str[10];
} ms01[10];

//03.結構體的兩種常見分類:
//  標準結構體:標準情況
//  匿名結構體:鎖定變數變數個數
//注:要想鎖定變數個數就不能使用指標變數:
//  因為針對於結構體指標變數可以進行動態記憶體分配,於是就會
//  導致結構體變數數目的不確定性
//04.結構體初始化:
//  1.位於棧記憶體的結構體可以採用如同陣列一樣的靜態初始化方式
//  2.可以採用靜態預設初始化方式{0}來將整個結構體空間清空為0
//  3.如果涉及到結構體陣列的形式,那麼最外層{}代表結構體陣列整體
//      內層的每一個{}代表一個結構體變數本身
//  4.如果涉及到一維結構體陣列的初始化操作,想要省略內部的{}
//      就需要按照結構體變數一個一個按照型別一致,順序對應的關係
//      完成初始化操作
//05.關於靜態初始化:
//  1.針對於所有複合型別:
//  2.最外層{}代表整體:
//      內層{}一般代表單個,也可代表子整體
//注:任何位於棧記憶體的複合型別採用指標進行指向,都需要進行型別轉換
//     任何位於棧記憶體的複合型別都可以採用{0}進行整體資料清零操作,無論
//          符合型別的巢狀級數-->複合型別快捷靜態初始化操作!
int main01(void)
{
    struct MyStruct01 ms02[10];
    struct MyStruct01 * pMS01 = (struct MyStruct01[]) { 0 };//結構體靜態初始化(預設形式)

    struct MyStruct01 ms03[] = { {10, "20" },{10, "20" },{10, "20"} };
    struct MyStruct01 * pMS02 = (struct MyStruct01[]) { {10, "20"}, { 10,"20" }, { 10, "20" } };
    struct MyStruct01 ms04[] = { {0}, {0} };
    struct MyStruct01 ms05[] = { 0 };
    struct MyStruct01 ms06[][10] = { 0 };//複合型別整體初始化

    system("pause");
}

struct CSDN01
{
    char name[100];
    char pass[100];
    char email[100];
} csdnData[10]
= {
    {"zdg", "12344321", "zdg@csdn.net"},
    {"LaoZheng", "670203313747", "chengming_zheng@163.com"},
    {"fstao", "730413", "fstao@tom.cn"}
}, *pCsdnStruct;

//06.關於陣列的型別以及指向陣列的指標總結:
//  1.所有陣列(無論陣列維度是多少),該陣列的型別都是去掉最高維度數
//  2.所有指向陣列(無論陣列維度是多少)型別的指標,都是將最高維度變為
//      陣列指標型別(*pArr)
//07.成員選擇:
//  指標:箭頭號
//  物件:點兒號
//注:嚴格注意運算子優先順序特性
int main02(void)
{
    pCsdnStruct = (struct CSDN01[]) {
        {"zdg", "12344321", "zdg@csdn.net"},
        {"LaoZheng", "670203313747", "chengming_zheng@163.com"},
        {"fstao", "730413", "fstao@tom.com"}
    };
    for (int i = 0; i < sizeof(csdnData) / sizeof(*(csdnData + 0)); ++i)
    {
        //a.b       (&a)->b     (*p).b;
        //char * p = strstr(*(*(csdnData + i)).email, "chengming_zheng@163.com");
        //char * p = strstr((&csdnData[i])->email, "chengming_zheng@163.com");
        //char * p = strstr((*(pCsdnStruct + i)).email, "chengming_zheng@163.com");
        //char * p = strstr((pCsdnStruct + i)->email, "chengming_zheng@163.com");
        char * p = strstr((*(pCsdnStruct + i)).email, "chengming_zheng@163.com");
        if (p)
        {
            puts((*(csdnData + i)).pass);
        }
    }

    system("pause");
}
///02.結構體動態陣列.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>

struct CSDN01
{
    int id;
    int num;
};

//01.靜態開闢和動態回收:
//  1.靜態開闢:
//      (1).記憶體尺寸編譯指定
//      (2).未使用記憶體分配函式
//      (3).使用的是常量決定尺寸
//      (4).只能使用陣列型別作為型別轉換(靜態型別轉換)
//      (5).不可進行手動回收
//  2.動態開闢:
//      (1).記憶體尺寸執行決定
//      (2).使用了記憶體分配函式
//      (3).可以使用變數決定尺寸
//      (4).只能使用指標型別作為型別轉換(動態型別轉換)
//      (5).可以進行手動回收
//02.記憶體清零函式:
//  格式:memset(首地址, 資料, 位元組數);
//  注意:可以指定任意記憶體首地址,尤其注意所需引數的特點
//      是地址層面的意義,不具備指標層面的意義
//03.陣列資料的兩種訪問方式:
//  靜態訪問:中括號
//  動態訪問:點兒號
int main03(void)
{
    int num;
    scanf("%d", &num);
    struct CSDN01 * pStruct01 = (struct CSDN01 *)alloca(num * sizeof(struct CSDN01));//棧記憶體
    struct CSDN01 * pStruct02 = (struct CSDN01 *)malloc(num * sizeof(struct CSDN01));//堆記憶體
    //記憶體清零函式:memset();
    memset(pStruct01, 0, sizeof(num * sizeof(struct CSDN01)));
    memset(pStruct02, 0, sizeof(num * sizeof(struct CSDN01)));
    //遍歷動態陣列[動態陣列可以像靜態陣列那樣的引用方式]
    for (int i = 0; i < num; ++i)
    {//快速初始化:memset();手動初始化:賦值操作方式
        printf("%d, %d\t%d, %d\n", (*(pStruct01 + i)).id = i, (*(pStruct01 + i)).num = i, (*(pStruct02 + i)).id = i, (*(pStruct02 + i)).num = i);
    }
    for (int i = 0; i < 10; ++i)
    {//投票次數
        int id = 0;
        scanf("%d", &id);
        for (int j = 0; j < num; ++j)
        {//具體投票
            if ((*(pStruct01 + i)).id == id)
            {
                ++(*(pStruct01 + i)).num;
                ++(*(pStruct02 + i)).num;
                break;
            }
        }
    }
    for (int i = 0; i < num; ++i)
    {
        printf("%d, %d\t%d, %d \n", (*(pStruct01 + i)).id, (*(pStruct01 + i)).num, (*(pStruct02 + i)).id, (*(pStruct02 + i)).num);
    }
    free(pStruct02);

    system("pause");
}

struct MyStruct01
{
    int id;
    char str[10];
};

int main04(void)
{
    int num;
    scanf("%d", &num);
    struct CSDN01 * pCSDNStruct01 = (struct CSDN01 *)malloc(num * sizeof(struct CSDN01));
    struct CSDN01 * pCSDNStruct02 = (struct CSDN01[]) { 1, 2, 3, 4 };//靜態分配
    printf("%d \n", (*(pCSDNStruct02 + 1)).id);
    printf("%d \n", (pCSDNStruct02 + 1)->id);

    struct CSDN01 csdnx = { 10, 20 };
    struct CSDN01 * pCSDNX = &csdnx;
    printf("%d \n", (*(pCSDNX)).id);
    printf("%d \n", pCSDNX->id);

    system("pause");
}

int main05(void)
{
    struct CSDN01 csdnData[100];
    struct CSDN01 * pCSDNStruct = csdnData;
    struct CSDN01 csdnDataX[10][10];
    struct CSDN01(*pArr01)[10] = csdnDataX;
    struct CSDN01(*pArr02)[10] = (struct CSDN01(*)[10])malloc(50 * sizeof(struct CSDN01));
    pArr02[3][4];

    system("pause");
}

程式片段(02):結構體大小.c
內容概要:結構體大小

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

//01.關於結構體記憶體位元組對齊的相關知識:
//  1.明確基本資料型別:
//      char short int long float double long long long double
//      (通常情況之下:double和long long尺寸一樣)
//  2.結構體的記憶體尺寸一定是大於或者等於所有成員標準尺寸之和
//  3.結構體記憶體位元組準確判定方式:
//      (1).確定基本對齊尺寸:
//          編譯器指定尺寸+基本資料型別最寬尺寸(兩者取最短)
//      (2).準確判定記憶體位元組尺寸:
//          方式一:
//              結構體最終尺寸必須可以整除對齊尺寸
//              結構體成員的首地址-結構體的首地址=偏移量(該偏移量必須是當前成員尺寸的整數倍)
//              結構體對齊單元的末尾不足部分,會被預設填充
//          方式二:
//              所有結構體在記憶體當中的儲存形式是矩形方式
//              矩形的寬度為對齊記憶體尺寸
//              放進矩形的的成員相對位置必須能夠整除自身尺寸
//  4.結構體如果只是存在單個成員的情況,根本就不存在結構體記憶體位元組對齊問題:
//      所以此時的結構體尺寸就是結構體當中的第一個成員的尺寸
//  5.結構體記憶體位元組對齊的優點:
//      (1).便於快速定址
//      (2).便於節約記憶體
//  6.結構體記憶體位元組優化方式:
//      1.首先確定基本資料型別最寬的成員:
//          (包含結構體多層巢狀都生效的最寬基本成員)
//      2.結構體矩形的形成順序,務必按照從內到外,從前到
//          後的順序進行確定
//      3.並且每個結構體矩形成員必須是獨立矩形!
//      4.再其次定義基本資料型別最寬的成員
//          (按照不同結構體單元進行分類順序考慮)
//      5.再其次考慮各個結構體當中當中的陣列資料型別和資料型別最寬的成員關係
//          陣列尺寸和對齊位元組數之間的關係
//      6.再逐個按照基本資料型別寬度尺寸遞減的方式定義成員
//    注意:每個獨立的結構體都是按照矩形形式儲存的!
//      這個矩形的寬度就是所有結構體當中資料型別最寬的那個成員
//      (巢狀結構體必須保證每個結構體自身先形成矩形,從內到外進行矩形確定)
struct MyStruct01
{//預設順序
    char chr1;//8
    double db2;//8
    int num3;//8
    char chr4;
    double db5;//8
};

struct MyStruct02
{//節省順序
    double db2;//8
    double db5;//8
    int num3;//8
    char chr1;
    char chr4;
};

struct MyStruct03
{//單成員結構體不存在結構體記憶體位元組對齊問題
    char num1;
    //double num4;
};


int main01(void)
{
    struct MyStruct01 ms01;
    struct MyStruct02 ms02;
    printf("sizeof(ms01) = %d \n", sizeof(ms01));//32-->預設儲存
    printf("sizeof(ms02) = %d \n", sizeof(ms02));//24-->節省儲存
    printf("&ms01 = %p \n", &ms01);
    printf("&ms01.chr1 = %p \n", &ms01.db2);
    printf("&ms01.db2 = %p \n", &ms01.db2);
    printf("&ms01.num3 = %p \n", &ms01.num3);
    printf("&ms01.chr4 = %p \n",    &ms01.chr4);
    printf("&ms01.db5 = %p \n", &ms01.db5);

    system("pause");
}

程式片段(03):01.Test.c+02.結構體結構體陣列副本機制.c+03.指標與結構體.c
內容概要:結構體成員與指標

///01.Test.c
#include <stdio.h>
#include <stdlib.h>

//01.結構體佔用位元組統計:
//  1.確定記憶體位元組對齊數:
//      編譯器指定+基本資料型別最寬成員(兩者取較短那個)
//      注:包括巢狀形式情況下的結構體資料型別最寬成員
//  2.從外往內,從前往後分析結構體巢狀形式:
//      注:任何一個獨立的結構體本身一定是按照矩形結構儲存的
//          結構體巢狀結構體-->類似於寬度相同的矩形巢狀
//  3.任何結構體成員的的起始位置減去相對矩形左外邊的尺寸
//      一定是當前結構體成員位元組的整數倍-->如果當前矩形行能夠
//      儲存下當前結構體成員,則存下,如果當前矩形行儲存不下,則
//      換下一個矩形行,當前矩形行的其餘位置空置
//02.結構體佔用位元組優化:
//  1.確定記憶體位元組對齊數:
//      1.編譯器指定+基本資料型別最寬成員(兩者取較短那個)
//      2.這個記憶體位元組對齊數用於確定每個獨立結構體的矩形寬度
//      注:包括巢狀形式情況下的結構體基本資料型別最寬成員!
//  2.從外往內,從前往後進行巢狀結構體的記憶體位元組對齊優化:
//  3.先安置與記憶體位元組對齊數相同的基本資料型別成員
//  4.再考慮結構體型別的成員安置情況
//  5.再考慮陣列型別的成員安置情況,這個需要與記憶體位元組對齊數
//      進行綜合考慮
//  6.在依次考慮基本資料型別當中從大到小的基本你資料型別安置
struct
{
    char chr1;//8--1--7
    double db2;//8--8--0
    int num3;//8--4--0
    char chr4;//0--1--0
    short num5;//0--2--1
    char chr6;//8--1--7
    long long num7;//8--8--0
} anonymity01;//40=25+15

struct
{
    long long num7;//8-8-0
    double db2;//8-8-0
    int num3;//8-4-0
    short num5;//0--2--0
    char chr1;//0--1--0
    char chr4;//0--1--0
    char chr6;//8--1-7
} anonymity02;//32=25+7

int main01(void)
{
    printf("sizeof(anonymity01) = %d \n", sizeof(anonymity01));
    printf("sizeof(anonymity02) = %d \n", sizeof(anonymity02));

    system("pause");
}
///02.結構體結構體陣列副本機制.c
#include <stdio.h>
#include <stdlib.h>

struct MyStruct01
{
    int arr[10];
    int len;
};

void change(struct MyStruct01 ms01)
{
    printf("change:ms01.arr = %p \n", ms01.arr);
    for (int i = 0; i < ms01.len; ++i)
    {
        *((ms01.arr) + i) = i * 3;
        printf("%3d", *((ms01.arr) + i));
    }
    printf("\n");
}

//01.符合型別都可以使用靜態初始化方式:
//  靜態初始化方式-->大括號{}
int main02(void)
{
    struct MyStruct01 ms01 = { { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 10 };
    change(ms01);
    printf("main:ms01.arr = %p \n", ms01.arr);
    for (int i = 0; i < ms01.len; ++i)
    {
        printf("%3d", *(ms01.arr + i));
    }

    system("pause");
}

//02.函式副本機制:
//  1.函式的形參和返回值都具備副本機制
//  2.只是該副本機制針對於陣列型別進行了優化:
//      函式副本機制唯一對陣列特殊,採用的是退化指標的解決方案
//03.關於各種取地址符運算的結果:
//  1.對一維陣列當中所儲存的元素:
//      獲取的是列指標
//  2.對一維陣列整體(陣列名稱)
//      獲取的行指標
//  3.對任意陣列名稱執行取地址運算子:
//      獲取的都是指向陣列的指標(陣列指標)
//04.如何確定陣列的型別以及指向陣列的指標?
//  1.所有陣列的型別:
//      去掉陣列名+去掉最高維度係數
//  2.指向陣列的指標:
//      替換陣列名為(*pArr),pArr就是指向陣列的指標
//  3.如何確定陣列名型別:
//      去掉陣列名+替換最高維度(包含中括號以及維度數)為(*),此時就是陣列名的變數型別
//注:嚴格區分陣列名的變數型別和常量型別
struct MyStruct01 testReturn()
{
    struct MyStruct01 ms01 = { { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 10 };
    printf("testReturn:ms01.arr = %p;ms01.arr + 1 = %p \n", ms01.arr, ms01.arr + 1);
    printf("testReturn:&ms01.arr = %p;&ms01.arr + 1 = %p \n", &ms01.arr, &ms01.arr + 1);
    return ms01;
}

//05.函式的返回值不能是陣列型別:
//  但可以是指向陣列的指標型別
typedef int * intP;//int型別的指標
typedef int intArr[10];//int型別的陣列
//int[] returnUnit()//陣列型別-->錯誤
//intArr returnUnit()//陣列型別-->錯誤
intP returnUnit(){}

int main03(void)
{
    struct MyStruct01 ms01 = testReturn();
    for (int i = 0; i < ms01.len; ++i)
    {
        printf("%3d", *(ms01.arr + i));
    }
    printf("\n");

    system("pause");
}

struct info
{
    char name[10];
    int age;
};
struct info infos[3];
struct info infos[];
struct info * pInfo;
void changeInfo(struct info * pInfo)
{//陣列名作為函式形參將會退化和陣列名相同的變數指標型別
    //pInfo = pInfo + 3;
    (*(pInfo + 1)).age = 88;
}

//06.通過結構體來描述結構體動態陣列:
//  結構體可以用於描述任何資料型別
struct data
{
    struct info * pArr;//陣列首元素變數指標
    int len;
};

int main04(void)
{
    struct info infos[3] = { {"fang", 18}, {"hua", 19}, {"lin", 19} };
    //infos = infos;
    changeInfo(infos);
    for (int i = 0; i < 3; ++i)
    {
        printf("%s, %d \n", (*(infos + i)).name, (*(infos + i)).age);
    }

    system("pause");
}
///03.指標與結構體.c
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

struct Data01
{
    int num;
};

int main05(void)
{
    //int intArr[3][4];//intArr:陣列名的常量指標形式
    int(*intArr)[4] = {0};//intArr:陣列名的變數指標形式
    //int * [4];//指標陣列形式
    printf("intArr:%p, intArr+1:%p \n", intArr, intArr + 1);

    struct Data01(*pArr)[10] = malloc(90 * sizeof(struct Data01));//動態分配一個標準的二維結構體陣列
    int k = 0;
    for (int i = 0; i < 9; ++i)
    {
        for (int j = 0; j < 10; ++j)
        {
            //printf("%3d", pArr[i][j].num = ++k);
            printf("%3d", (*(*(pArr + i) + j)).num = ++k);
        }
        printf("\n");
    }

    system("pause");
}

int main06(void)
{
    struct Data01 **pp;//結構體鋸齒陣列
    pp = (struct Data01 **)malloc(10 * sizeof(struct Data01 *));
    int k = 1;
    for (int i = 0; i < 10; ++i)
    {
        *(pp + i) = (struct Data01 *)malloc((i) * sizeof(struct Data01));
        for (struct Data01 * pStruct = *(pp + i); pStruct <= *(pp + i) + i; ++pStruct)
        {
            printf("%3d", (*pStruct).num = k++);
        }
        printf("\n");
    }

    system("pause");
}

int main07(void)
{
    //struct Data01 * pStruct = (struct Data01 *)alloca(10 * sizeof(struct Data01));//棧記憶體-->動態分配
    struct Data01 * pStruct = (struct Data01 *)malloc(sizeof(struct Data01) * 10);//堆記憶體-->動態分配
    int i = 1;
    for (struct Data01 * pTemp = pStruct; pTemp < pStruct + 10; ++pTemp)
    {
        printf("%p, %d \n", pTemp, (*pTemp).num = i++);
    }

    system("pause");
}

程式片段(04):位域.c
內容概要:位域

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

//01.位域:通過限定資料的儲存位數,從而實現節省記憶體佔用
//      位域-->指定儲存位數-->節省記憶體|顯二進位制位
//02.在操作二進位制位運算的過程當中,一定要注意到符號位的特點
//  1.位域結構體有其自身的資料儲存方式和解析方式
//  2.可以進行資料的分批次儲存
struct MyStruct01
{
    unsigned int a : 5;//00~31[2^5=32]
    unsigned int b : 5;//00~12[2^4=16]
    unsigned int c : 16;//0000[2^16]
};//4

int main01(void)
{
    printf("sizeof(struct MyStruct01) = %d \n", sizeof(struct MyStruct01));

    struct MyStruct01 ms1, *pStruct;
    pStruct = &ms1;
    ms1.a = 8;//位域-->壓縮記憶體-->操作二進位制位-->效率高
    ms1.b = 11;
    ms1.c = 1999;//儲存資料都一樣,解析方式不同
    printf("%d ,%d, %d \n", ms1.a, ms1.b, ms1.c);
    printf("%d, %d ,%d \n", pStruct->a, pStruct->b, pStruct->c);

    system("pause");
}

struct Data01
{
    unsigned short num1 : 1;
    unsigned short num2 : 2;
};//2

//03.位域結構體在進行資料的分批次儲存的時候:
//  1.注意有無符號的影響
//  2.注意資料越界的情況:
//      如果出現越界,就只會解析界限以內的資料
int main02(void)
{
    printf("sizeof(struct Data01) = %d \n", sizeof(struct Data01));
    struct Data01 data01;
    data01.num1 = 2;//資料越界2-->二進位制-->10-->顯示0
    printf("%d \n", data01.num1);

    system("pause");
}

//04.只有位域才有可能出現按照資料型別進行合併的情況
//  同型別+不越界=>記憶體壓縮
struct Data02
{
    unsigned int num1 : 32;
    unsigned short num2 : 1;
};//8

int main03(void)
{
    printf("%d \n", sizeof(struct Data02));

    system("pause");
}

int isit()
{
    unsigned short num = 1;//0000 000 0000 0001
    return (1 == *(char *)&num);//接觸原則
}

int main04(void)
{
    short num = 1;
    printf("&num = %p \n", &num);//低位在低位元組,高位在高位元組
    printf("%d \n", isit());//0000 0000 0000 0001
    //逆序儲存:1000 0000 0000 0000
    //順序顯示:0000 0000 0000 0001

    system("pause");
}

程式片段(05):顯示整數.c
內容概要:位域程式設計實戰

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

//01.關於二進位制位與高低直接之間的關係:
//  1.現代手機,電腦:
//      低位在低位元組:
//  2.Unix伺服器系列:
//      低位在高位元組:
//注:關於兩種不同儲存方式的優缺點:
//      低位在低位元組:
//          節省記憶體
//      低位在高位元組:
//          快速檢索
//02.除錯顯示狀態:
//  低位在低位元組的情況記憶體結構分析:
//      1000 0000 0000 0000 0000 0000 0000 0000//逆序儲存
//      0000 0000 0000 0000 0000 0000 0000 0001//順序顯示
//  解析特點:
//      1000 0000:8個二進位制位儲存於低位元組位置-->節省記憶體
//注:逆序儲存-->實現低位在低位元組-->順序顯示(解析方式)
//03.結構體與位域:
//  1.位域所涉及的知識:
//      結構體+位運算
//  2.按照單位元組解析二進位制位的結構體:
//      特殊結構體:位域
//04.位域特點分析:
//  1.位域的用處:
//      (1).記憶體壓縮
//      (2).解析資料二進位制位
//  2.記憶體位元組對齊特點:
//      (1).基於標準結構體法則
//      (2).基於記憶體合併法則
//          相同型別,如果位數相加小於該型別位元組數,則壓縮儲存
//          相同型別,如果位數相加大於該型別位元組數,則填位移字
//      (3).特殊位域處理:
//          如果位域當中的位域為空,則空置下一個此型別位元組數
//          如果位域當中的域名沒有,則空置下一個此型別位元組數
//  3.快速識別位域:
//      結構體+冒號
//  4.位域組成特點分析:
//      BitField:位域結構體名(整體)
//      unsigned char:位域結構體成員型別
//      chr:域名
//      : :位域結構體標識
//      1:位寬
//05.採用位於結構體顯示一個資料的二進位制儲存形式注意事項:
//  1.採用指向位域結構體的指標來儲存變數的地址!
//  2.該位於結構體的指標是按照單位元組進行記憶體解析的
//      從低位元組開始逐個進行解析
//  3.逆序儲存-->順序顯示:
//      (1).逆序指標:先列印高位元組
//      (2).逆序域名:反轉儲存實質
struct BitField
{
    unsigned char chr1 : 1;
    unsigned char chr2 : 1;
    unsigned char chr3 : 1;
    unsigned char chr4 : 1;
    unsigned char chr5 : 1;
    unsigned char chr6 : 1;
    unsigned char chr7 : 1;
    unsigned char chr8 : 1;
};//一個位元組

int main05(void)
{
    printf("sizeof(struct BitField) = %d \n", sizeof(struct BitField));
    int data = 1;//原始整數
    int bitLen = 4;
    struct BitField * pBitField = &data;//將一個整數指標直接賦值給位域結構體變數
    //pBitField儲存的是位域結構體陣列方式儲存的
    printf("&data = %p \n", &data);
    while (bitLen--)//存在-->獲位
    {//每次解析一個位元組-->從高位元組開始進行解析-->一直解析到低位元組
        printf("%d%d%d%d %d%d%d%d \n",
            (pBitField + bitLen)->chr8,
            (pBitField + bitLen)->chr7,
            (pBitField + bitLen)->chr6,
            (pBitField + bitLen)->chr5,
            (pBitField + bitLen)->chr4,
            (pBitField + bitLen)->chr3,
            (pBitField + bitLen)->chr2,
            (pBitField + bitLen)->chr1);
    }
    //1000 0000
    //0000 0000 
    //0000 0000 
    //0000 0000 

    system("pause");
}

程式片段(06):Union.c
內容概要:Union

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

//01.結構體和共用體的相同點:
//  1.既不允許空結構體的存在,也不允許空共用體的出現:
//      也就是說結構體或共用體至少必須包含一個成員
//  2.使用結構體型別或者共用體型別的時候都必須包含
//      struct關鍵字
union MyUnion01
{
    int num;
    double db;
};

//02.共用體內容總結:
//  1.共用體的記憶體尺寸等於最長的那個成員所佔據的記憶體尺寸
//      最長成員(包含複合型別)
//  2.共用體的所有成員共享同一資料實質(記憶體形式一致)
//      但是由於共用體的不同成員型別不一致,因此解析結果不一致
//注:共用體的各個成員儲存資料儲存實質一樣,但是解析方式卻不一樣
//  區分儲存實質和解析方式的不同所造成的差異
int main01(void)
{
    //printf("sizeof(union MyUnion01) = %d \n", sizeof(union MyUnion01));//結構體記憶體尺寸等於最寬的那個成員

    union MyUnion01 mu01;
    mu01.num = 3;
    //mu01.db = 111111111123.98776;
    printf("%8d, %lf \n", mu01.num, mu01.db);//儲存實質一致,但是解析型別不一致
    mu01.num = 0;
    printf("%8d, %lf \n", mu01.num, mu01.db);

    system("pause");
}

程式片段(07):Union.c
內容概要:Union定義方式

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

union MyUnion01
{//標準形式
    int num;
    float fl;
} mu, arr[3], *p;

//01.共用體定義方式:
//  1.宣告共用體型別的同時定義共用體變數
//  2.先宣告共用體型別,再定義共用體變數
//  3.標準共用體和匿名共用體
union
{//匿名形式
    int num;
    float fl;
} mu1, arr1[3], *p1;

//02.共用體的特點:
//  1.所有成員共用同一個資料
//  2.所有成員公用同一個地址
//  3.只是由於成員的不同型別,因此導致對
//      同一記憶體地址的同一個資料解析結果不同
//注:所有變數使用同一地址,所有變數使用同一資料
//  不同的成員只是決定不同的資料解析方式
int main01(void)
{
    //printf("sizeof(mu1) = %d \n", sizeof(mu1));

    union MyUnion01 mu2, arr2[3], *p2;
    mu2.num = 100;
    printf("%d, %f \n", mu2.num, mu2.fl);
    printf("%p, %p \n", &(mu2.num), &(mu2.fl));

    system("pause");
}

程式片段(08):對齊.c
內容概要:記憶體對齊

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

struct MyStruct01
{
    char str[23];
    short num;
};

//01.結構體巢狀的情況之下:
//  記憶體位元組對齊數就是所有基本型別成員當中
//  佔用記憶體位元組數最寬的那個成員
struct MyStruct02
{
    struct MyStruct01 ms01;
    char chr;
};

int main01(void)
{
    //printf("sizeof(struct MyStruct01) = %d \n", sizeof(struct MyStruct01));
    printf("sizeof(struct MyStruct02) = %d \n", sizeof(struct MyStruct02));

    system("pause");
}


union MyUnion01
{
    char str[13];
    int db;
};

//02.共用體的記憶體尺寸特點:
//  1.必須遵守記憶體位元組對齊方式
//  2.為佔用記憶體尺寸最大的那個成員
//      陣列特點+對齊尺寸
int main02(void)
{
    printf("sizeof(sruct MyUnion01) = %d \n", sizeof(union MyUnion01));

    system("pause");
}

程式片段(09):01.同與不同.c+02.TypeDef.c
內容概要:結構體與共用體的不同

///01.同與不同.c
#include <stdio.h>
#include <stdlib.h>

struct MyStruct01
{
    int num;
    double db;
};

union MyUnion01
{
    int num;
    double db;
};

//01.結構體和共用體各個成員的特點:
//  結構體的各個成員記憶體地址獨立,資料之間沒有關係
//  共用體的各個成員記憶體地址相同,資料也是完全相同
//注:共用體的各個成員由於其資料型別的不用,因此對
//  同樣一塊兒記憶體資料的解析結果就不一樣
//  (有無符號+補碼解析+階碼解析)
int main01(void)
{
    struct MyStruct01 ms01, * ps01;
    union MyUnion01 mu01, *pu01;
    printf("%p, %p \n", &ms01.num, &ms01.db);//結構體各個成員記憶體地址不同
    printf("%p, %p \n", &mu01.num, &mu01.db);//共用體各個成員記憶體地址相同

    ps01 = &ms01;
    pu01 = &mu01;
    ms01.num;
    (&ms01)->num;
    (*&ms01).num;
    ps01->num;
    ps01->num;
    (*ps01).num;
    mu01.num;
    (&mu01)->num;
    (*(&mu01)).num;

    system("pause");
}
///02.TypeDef.c
#include <stdio.h>
#include <stdlib.h>

//01.typedef的特點:
//  所有連續定義的變數名稱都會成為資料型別的別名
typedef struct MyStruct01
{
    int num;
    double db;
} ms01, ms02;

typedef struct MyStruct01 MS;

typedef union MyUnion01
{
    int num;
    double db;
} mu01, mu02;

typedef union MyUnion01 MU;

typedef unsigned int num1, num2, num3, num4;//num1,num2,num3,num4都是資料型別的別名

//02.共用體賦值特點:
//  1.共用體只能對其中一個成員進行初始化賦值操作
//  2.共用體在經過多次賦值操作之後,其最終結果是最後一次賦值結果
//03.結構體和共用體都可以採用指定成員初始化的方式:
//  必須是靜態初始化特點才可以採取指定成員初始化的方式
int main02(void)
{
    MS my01 = { .num = 10, 10.9 };
    ms01 my02 = { 11, 11.9 };
    MU myu01 = { 10 };
    mu01 myu02 = { .db = 11.9 };
    ms02 my3 = { 12, 129 };

    system("pause");
}

程式片段(10):PC.c
內容概要:共用體

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct MyStructX
{
    double db;
};

union PC
{
    struct MyStructX;
    int num;
    char price[10];
    char * p;

} pc01 = { .num = 10000 };

struct MyStruct
{
    union PC pc1;
};

int main01(void)
{
    union PC pc02;
    pc02.p = (char *)malloc(100);
    strcpy(pc02.p, "海華裝的電腦,價格未知!");

    union PC pc03 = pc02;
    pc03.p = malloc(100);
    strcpy(pc03.p, pc02.p);
    printf("%s, %s \n", pc02.p, pc03.p);
    free(pc02.p);
    printf("%s, %s \n", pc02.p, pc03.p);

    system("pause");
}

//01.針對於結構體和共用體而言:
//  1.都存在著淺拷貝和深拷貝問題
//  2.變數名稱可以和結構體的名稱相同
int main02(void)
{
    union PC pc = { 10000 };
    union PC arr[3] = { {10000},{10000}, {10000} };
    union PC * p = (union PC []){ {10000},{10000},{10000} };
    //++pc;
    //arr = arr;
    pc.num++;
    ++pc.num;
    printf("%d \n", pc.num);


    system("pause");
}

//02.採用共用體可以採用多種方式描述同一個事物:
//  注:共用體當中所儲存的成員一旦改變,就只能訪問最後一個資料
//      同一個地址,同一個資料(指標變數和普通變數儲存的內容一致)
int main03(void)
{
    union PC pc;
    pc.p = (char *)malloc(100);
    strcpy(pc.p, "海華裝的電腦,價格位置!");
    pc.num = 4000;
    printf("%s \n", pc.p);
    free(pc.p);//嚴重錯誤

    system("pause");
}

程式片段(11):Data.c
內容概要:資料管理程式設計

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//01.如何"多行表示"一個"整體儲存"的字串?
//  採用反斜槓"\"操作符作為多行表示字串的結尾連結狀態
char str[1024] = "521276402----hanlei@19940403 \
    286738260----WEIPEI559720 \
    501223616----feng66532008 \
    851400824----shuidongwo520 \
    1340382355----huang.512yang \
    1061817115----fz62wangyong1983 \
    347232860----20080811 \
    1028181591----7404850554 \
    120539543----0.0123456789 \
    754229005----460228214 \
    819781633----zmt1993826 \
    1319148052----YNU1500621032 \
    904972448----zhouxiaowen.520 \
    750134133----1292857988 \
    438905700----320675 \
    379644978----7758521tao \
    346083956----devl1017 \
    562193724----5361a749 \
    286124126----xuqiang1988 \
    4008167599----234567 \
    937350113----MAWENcxn1020 \
    873589635----qqco1341HUA \
    853249795----5385939d ";

//02.建立型別為struct info的結構體型別
//  1.用於描述單個使用者的資訊實體
//  2.單個結構體對應於一個使用者的資訊
struct info
{
    long long QQ;
    char pass[16];
};

//03.採用結構體描述動態結構體陣列:
//  使用者結構體動態陣列用於描述多個使用者資訊實體
//注:基於C語言的所有資料結構都依賴於結構體
struct infos
{
    struct info * pInfo;
    int len;
} myInfos;

int getRowNum(char * pStr)
{//根據每行字串所共有的特徵進行資料行的統計
    int rows = 0;
    for (char * pTemp = strstr(pStr, "----"); NULL != pTemp; pTemp = strstr(pTemp + 4, "----"))
    {//兩面夾擊統計法
        ++rows;
    }
    return rows;
}

void initMem()
{//採用結構體模擬動態結構體陣列
    int len = getRowNum(str);
    myInfos.pInfo = (struct info *)malloc(len * sizeof(struct info));
    myInfos.len = len;
}

void initData(char * pStr)
{
    int len = strlen(pStr);
    for (char * p = pStr; p < pStr + len; ++p)
    {//針對於原始資料的預處理操作
        if (' ' == *p)
        {
            *p = '\0';
        }
    }
    for (char * p = pStr, chr = 0; p < pStr + len; ++p)
    {//根據字串資料進行結構體資料拼裝
        //printf("%p, %d \n", p, chr);
        char * pTmp = (char *)calloc(strlen(p) + 1, sizeof(char));
        strcpy(pTmp, p);
        //"521276402\0---hanlei@19940403"//pTmp-->pWei+4
        char * pWei = strstr(pTmp, "----");
        *pWei = '\0';
        sscanf(pTmp, "%lld", &(*(myInfos.pInfo + chr)).QQ);
        sprintf((*(myInfos.pInfo + chr)).pass, "%s", pWei + strlen("----"));
        p += strlen(p) + 1;
    }
}

void showAllData()
{
    for (int i = 0; i < myInfos.len; ++i)
    {
        printf("QQ = %lld, pass = %s \n", (*(myInfos.pInfo + i)).QQ, (*(myInfos.pInfo + i)).pass);
    }
}

void showDataStatus()
{
    printf("還有%d條資料! \n", myInfos.len);
}

void help()
{
    printf("1.顯示資料 \n");
    printf("2.刪除資料 \n");
    printf("3.插入資料 \n");
    printf("4.修改資料 \n");
    printf("5.查詢資料 \n");
    printf("6.顯示全部資料 \n");
    printf("7.對於QQ排序 \n");
    printf("8.清屏 \n");
    printf("9.Helop \n");
    printf("\n");
}

void deleteData(){}

int main01(void)
{
    initMem();
    initData(str);
    help();
    while (1)
    {
        char chr = getch();//實時獲取單個字元
        //char chr = '\0';
        //getchar(chr);
        //putchar(chr);
        switch (chr)
        {
        case '1':
            showDataStatus();
            break;
        case '2':
            break;
        case '3':
            break;
        case '4':
            break;
        case '5':
            break;
        case '6':
            break;
        case '7':
            break;
        case '8':
            system("cls");
            break;
        case '9':
            help();
            break;
        default:
            break;
        }
    }

    system("pause");
}

相關文章