20160211.CCPP體系詳解(0021天)

尹成發表於2016-03-01

程式片段(01):01.指標陣列.c+02.動態陣列.c
內容概要:指標陣列

///01.指標陣列.c
#include <stdio.h>
#include <stdlib.h>

//01.指標陣列作為函式的形參:
//  會退化為一個二級指標!
//02.如何確定一個陣列作為函式形參將會退化為什麼樣兒的指標?
//  關鍵在於形引數組當中的元素是什麼型別!就是什麼型別的指標
void show01(char * str[5])//char *str[5]
{
    for (int i = 0; i < 5; ++i)
    {
        printf("%s \n", str[i]);//char **str
    }
}

//03.等價關係:
//  二級指標和指標陣列的陣列名所對應的解析方式都一樣!
void show02(char ** str)
{//指標陣列作為函式的形參進行傳參的時候,指標陣列的陣列名將會退化為一個二級指標!
    for (int i = 0; i < 5; ++i)
    {
        printf("%s \n", str[i]);
    }
}

//04.函式形參的攔截機制:
//  如果是陣列,就沒有副本機制,不是副本,就有攔截介質
//注:解除陣列的副本機制依賴於陣列名的退化機制!
//05.如何確定一個變數的資料型別?
//  在定義變數的基礎情況之下,挖掉變數名,就是變數的資料型別
//注:字元指標陣列開發過程當中比較常用!
int main01(void)
{
    int * arr[10];//-->int * [10]-->一級指標陣列型別!
    //一級指標陣列的陣列名arr在作為函式形參的時候,該一級指標的陣列名會自動退化以為一個二級指標
    char * str[5] = { "calc", "notepad", "tasklist", "mspaint", "pause" };
    //char * [N]:字元指標型別的一級指標陣列在開發過程當中經常使用!
    char **pp = str;//變數指標=常量指標(地址意義的陣列賦值動作!)-->指標變數結果-->指標變數遍歷!
    printf("%p \n", str);//二級指標常量的數值
    //show01(str); <= = > show02(str);

    system("pause");
}
///02.動態陣列.c
#include <stdio.h>
#include <stdlib.h>

//01.如何確定動態陣列的陣列名稱?
//  1.開闢記憶體的時候所採用的指標變數名稱就是動態陣列名稱!
//  2.通過指標變數名稱的訪問方式就如同標準陣列的訪問方式!
//  注:嚴格區分變數指標和常量指標之間的差別!
int main02(void)
{
    //動態分配一個一維陣列
    int * const arr = malloc(30 * sizeof(int));
    for (int i = 0; i < 30; ++i)
    {
        printf("%d \n", arr[i] = i);//arr[i]<=>*(arr+i)
    }

    system("pause");
}

//02.動態陣列的分配要點:
//  1.動態記憶體開闢函式!
//  2.解析動態記憶體的指標型別!
//  注:所有陣列都看做為一維陣列!,指向該陣列首元素的指標
//      指標可以使用:變數指標和常量指標(模擬棧記憶體陣列!)
//  注:動態陣列的尺寸在程式執行過程當中決定!
//      每個當前維度的陣列都必須要求明確其維度-1的特點
int main03(void)
{
    //動態分配一個二維陣列
    //int test = 5;
    int(*p)[5] = malloc(30 * sizeof(int));
    for (int i = 0, num = 0; i < 30; ++i, ++num)
    {
        printf("%3d", p[i / 5][i % 5] = num);//0->1->2->3...
        if (0 == (i + 1) % 5)
            putchar('\n');
    }

    system("pause");
}

//03.兩種區別的陣列:
//  二維陣列:陣列當中的每個相鄰陣列元素的記憶體地址必須連續
//      (連續!+對齊!)
//  分塊陣列:針對於同一個陣列當中的每個陣列元素的記憶體地址必須連續
//      但是內部就整體而言的陣列元素之間是可以不連續的
//      (不連續+不對齊!)
//  注:動態記憶體開闢函式的返回值型別為("void *")型別,只是負責返回一個有效
//      地址,但是沒有具備解析意義+堆記憶體動態開闢函式前面的賦值號只是用於
//      返回一個明確的地址而已!-->指標型別決定對動態記憶體的實際解析意義!
int main04(void)
{
    //使用malloc函式在堆記憶體當中開闢一個指標陣列!
    int **pp = malloc(5 * sizeof(int *));//20個堆記憶體位元組
    int num = 0;
    for (int i = 0; i < 5; ++i)
    {
        pp[i] = (int *)malloc((5 + i)*sizeof(int));
        for (int j = 0; j < 5 + i; ++j)
        {
            pp[i][j] = num++;//賦值
        }
    }
    for (int i = 0; i < 5; ++i)
    {
        for (int j = 0; j < 5 + i; ++j)
        {
            printf("%3d", pp[i][j]);
        }
        printf("\n");
    }

    system("pause");
}


//04.常見資料結構的特點:
//  陣列:查詢效率高+增加|刪除效率低
//  連結串列:增加|刪除效率高+查詢效率低
//  分塊:查詢效率和陣列一樣+增加|刪除效率比陣列高(略低於連結串列,不過可以忽略!)
//      可以不用連續,可以不用對齊!
//05.如何分配一個N-1級指標陣列?
//  需要一個N級的指標!
// N級指標,儲存N-1級指標陣列的地址
// N級指標分配一個陣列,用於存放N-1級指標陣列的首個元素的地址(區分變數指標|常量指標)
// 資料等同於0級指標

程式片段(02):01.Array.c
內容概要:陣列的三種形態

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

//01.C語言版本標準:
//  第一版:C89
//  第二版:C99
//02.(VC2013以上+GCC4.7以上)支援C99語法
//  VC2013+GCC4.7
//03.C99新語法分配陣列模式!
//  1.位於棧記憶體的靜態陣列
//  2.可以進行指明初始化!
//      指明初始化的最大索引可以推導陣列總元素個數
//  3.指明初始化方式的特點:可以明確長度!
//      純大括號初始化:只能同時指明陣列長度,並且給陣列最後一個元素初始化賦值
//          其餘陣列元素統統採用預設初始化方式!
//      非純大括號初始化:可以同時指明陣列長度,並且給多個陣列元素初始化賦值
//          其餘陣列元素統統採用預設初始化方式!
int main01(void)
{
    int arr[] = { [10] = 10 };//預設不操作的陣列元素被賦值為0,[10]-->arr[10]=0->11個元素
    for (int i = 0; i < 11; ++i)
    {
        printf("%d \n", arr[i]);
    }

    system("pause");
}

int main02(void)
{
    int n = 4;
    int * pC99 = (int[]) { [2] = 1, [4] = 10 };//靜態陣列
    //預設不指明初始化的陣列元素所對應的值為0
    for (int i = 0; i < 5; ++i)
    {
        printf("%d \n", pC99[i]);
    }

    system("pause");
}

//04.靜態陣列與動態陣列:
//  靜態陣列:
//      1.儲存於棧記憶體
//      2.編譯時期決定陣列元素個數
//      3.(自動開闢+自動回收)
//  動態陣列:
//      1.儲存於堆記憶體
//      2.執行時期決定陣列元素個數
//      3.(手動開闢+手動釋放)
//  注:嚴格區分(分配+釋放)以及(開闢+回收)的區別
//      分配+釋放:許可權
//      開闢+回收:記憶體
void run()
{
    //int arr[5] = { 1, 2, 3, 4, 5 };
    int * p = malloc(10 * sizeof(int));
    for (int i = 0; i < 10; ++i)
    {
        p[i] = i;//p[i]-->*(p+i)
    }
    free(p);
    //基於C99語法的靜態陣列:處於棧記憶體當中,自動開闢回收,用於解決心臟起搏器問題!
    //int * pC99 = (int []){ [4] = 10 };
    //printf("%p \n", pc99);
    printf("\n");
}

int main03(void)
{
    run();
    printf("\n\n\n");//加速記憶體的回收動作!+防止Release模式!優化操作
    run();
    printf("\n\n\n");

    system("pause");
}

//05.指標變數可以指向任何記憶體地址:
//  無論是棧記憶體,堆記憶體,全域性區,程式碼區
int main04(void)
{
    int num = 30;
    //int * pC99 = (int[30]) { 0 };
    //pC99 = (int[10]) {0};//pC99是int*型別
    //pc99 = 1;
    int * pC99 = (int [30]) { [2] = 1, [4] = 10 };//靜態陣列
    for (int i = 0; i < 30; ++i)
    {
        printf("%d \n", pC99[i]);
    }

    system("pause");
}

//06.區分靜態分配和動態分配區別:
//  int num=30;//靜態分配:必須在編譯時期,決定靜態陣列的元素個數
//  int a[num];//棧上的靜態分配-->可變長度的陣列只有在GCC編譯器當中實現,VC編譯器當中不能進行實現
//  注:VC不支援位於棧記憶體的靜態陣列+GCC支援位於棧記憶體的靜態陣列!

程式片段(03):01.記憶體.c
內容概要:四大分配堆記憶體的函式

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

//01.記憶體四大分配函式:
//  malloc<->calloc:
//      malloc:記憶體不清零(引數:記憶體位元組數)
//      calloc:記憶體清零(引數1:陣列元素個數+引數2:單個元素尺寸)
//  realloc<->_recalloc:
//      realloc:記憶體不清零(引數1:記憶體首地址,總記憶體位元組數)
//          1.如果原始記憶體地址足夠擴充,就在原始記憶體地址進行擴充
//          2.如果原始記憶體地址不夠擴充,就在新地址記憶體進行擴充:
//              拷貝原始記憶體地址所對應的有效資料+回收原始記憶體地址資料
//          3.擴充之後的記憶體如果沒有資料進行覆蓋,就不回執行記憶體清零操作!
//      _recalloc:記憶體清零(引數1:記憶體首地址+引數2:陣列元素個數+引數3:單個元素尺寸)
//          1.如果原始記憶體地址足夠擴充,就在原始記憶體地址進行擴充
//          2.如果原始記憶體地址不夠擴充,就在新地址記憶體進行擴充
//              拷貝原始記憶體地址所對應的有效資料+回收原始記憶體地址資料
//          3.擴充之後的記憶體如果沒有進行手動初始化,系統將會執行自動初始化操作!
int main01(void)
{
    //int * p = (int *)malloc(100);//malloc不會初始化引數,引數是整體所佔用的記憶體尺寸(位元組數)!
    int * p = calloc(25, sizeof(int));//calloc存在初始化引數,引數解釋:第一個引數是元素個數,第二個引數是元素記憶體尺寸
    printf("%p \n", p);
    //for (int i = 0; i < 25; ++i)
    //{
    //  p[i] = i;
    //}

    system("pause");
}

int main02(void)
{
    int * p = malloc(10 * sizeof(int));//指標能夠操作這片兒堆記憶體!
    //int * p_p = malloc(100);
    for (int i = 0; i < 10; ++i)
    {
        printf("%d \n", p[i] = i);
    }
    printf("p = %p \n", p);

    int * px = realloc(p, 200);//擴充記憶體,記憶體不清零
    //返回值是記憶體首地址,說明擴充成功,後續地址擴充,擴充不成功,重新開闢記憶體
    //原來的記憶體就被回收了
    printf("px = %p \n", px);
    for (int i = 0; i < 50; ++i)
    {
        px[i] = i;
        printf("%d \n", px[i]);
    }
    //p[120387] = 10;

    system("pause");
}

int main03(void)
{
    int * p = calloc(25, sizeof(int));//會初始化為0,引數:陣列元素個數+記憶體位元組大小
    //scanf("123");
    printf("%p \n", p);
    for (int i = 0; i < 25; ++i)
    {
        p[i] = i;
    }
    p = _recalloc(p , 50, sizeof(int));//記憶體清零操作
    for (int i = 25; i < 50; ++i)
    {
        p[i] = i;
    }
    system("pause");
}

程式片段(04):main.c
內容概要:GccArray

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

//01.VC不支援棧記憶體動態陣列,GCC支援棧記憶體動態陣列!
int main()
{
    int num=30;
    int a[num];//棧上的動態分配
     //int *pc99 = (int[30]){0};

    //printf("Hello world!\n");
    return 0;
}

相關文章