20160212.CCPP體系詳解(0022天)

尹成發表於2016-03-01

程式片段(01):01.二維陣列.c
內容概要:二維陣列

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

//01.關於棧記憶體開闢陣列:
//  訣竅:將所有維度的陣列看做為一維陣列,
//      然後再採用指向該陣列當中首個元素的指標(變數|常量)
//  祕訣:原始陣列陣列名稱替換法:
//      就可以直接得到指向陣列的指標(將陣列名稱-->替換為-->(*pArr))
//  特點:指標變數可以不用最高維度,
//      但是型別轉換必須加上表示最高維度的中括號!
int main01(void)
{
    //int arrArr[3][4];//位於棧記憶體:
    int * p1 = (int []) {0};//一維陣列-->棧上開闢
    int(*p2)[4] = (int[][4]) { 0 };//二維陣列-->棧上開闢
    int(*p3)[3][4] = (int[][3][4]) { 0 };//三維陣列-->棧上開闢

    system("pause");
}

//02.堆疊開闢指標陣列以及指標陣列的回收順序!
//  1.防止記憶體洩露現象的產生
//  2.動態陣列可以按照靜態陣列的方式進行訪問!
int main02(void)
{
    //堆記憶體開闢陣列!
    int ** pp = calloc(3, sizeof(int *));//分配指標陣列[一級指標陣列!]
    for (int i = 0; i < 3; ++i)
    {
        pp[i] = malloc(4 * sizeof(int));//每個指標必須分配記憶體!
    }

    int num = 0;
    for (int i = 0; i < 3; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            //&pp[i]-->pp+i
            //&pp[i][j]-->*(pp+i)+j
            //pp[i]-->*(pp+i)
            //pp[i][j]-->*(*(pp+i)+j);
            //printf("%4d", pp[i][j] = num++);
            printf("%4d", *(*(pp + i) + j));
        }
        printf("\n");
    }
    //嚴格注意堆記憶體當中陣列的回收順序!
    for (int i = 0; i < 3; ++i)
    {
        free(pp[i]);//先回收一級指標所指向的記憶體塊兒-->防止堆記憶體洩露
    }
    free(pp);//再釋放指標陣列

    system("pause");
}

//03.棧上開闢二維陣列:
//  注:在進行棧記憶體的型別轉換的時候,需要一個準確的陣列!
int main03(void)
{
    //棧上開闢二維陣列
    int(*p)[4] = (int[3][4]) { 0 };//棧上開闢二維陣列,自動回收
    int num = 0;
    for (int i = 0; i < 3; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            printf("%3d", p[i][j] = num++);
        }
        printf("\n");
    }

    system("pause");
}

//04.分塊兒陣列的逐級回收特點!
int main04(void)
{
    //分塊兒記憶體切忌要逐級進行記憶體回收!
    int(*pArr)[4] = malloc(3 * 4 * sizeof(int));//堆記憶體:呈線性儲存的二維陣列
    int num = 0;
    for (int i = 0; i < 3; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            printf("%3d \n", pArr[i][j] = num++);
        }
        printf("\n");
    }
    free(pArr);//根據指向堆記憶體的連續儲存的二維陣列指標,進行堆記憶體二維陣列的回收操作!

    system("pause");
}

程式片段(02):01.函式指標.c
內容概要:函式指標

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

void runmsg()
{
    MessageBoxA(0, "您好!", "天朝城管的全家!", 0);
}

void print()
{
    printf("%s, %s \n", "您好!", "天朝城管的全家!");
}

//01.嚴格區分函式指標變數和函式指標常量:
//  函式名的本質:函式指標常量,儲存的是程式碼區函式實體的入口點!
//  函式指標的本質:儲存函式指標常量的資料|儲存程式碼區函式實體的入口點
//      通過修改函式指標變數所儲存的入口點,讓函式指標變數可以呼叫
//      程式碼區當中不同的函式實體,從而實現改變函式行為的現象!
//02.對函式名的幾種操作:
//  &funName--funName--*funName
//注:這幾種方式所得到的值相同,都是同一個函式實體的入口點地址!
//      區分函式宣告入口點地址和函式呼叫地址!
int main01(void)
{
    //函式指標變數:自己儲存與資料區當中,只是儲存了程式碼區的入口點(函式指標常量)
    //runmsg = runmsg;//函式名的本質:函式指標常量,儲存的是程式碼區函式體的入口點兒
    void(*pFun)() = runmsg;//通過函式指標變數儲存函式指標產量的值|儲存程式碼區函式實體的呼叫地址
    pFun();//通過函式指標變數實現函式的間接呼叫
    pFun = print;//修改函式指標變數所儲存的函式指標常量,從而實現呼叫行為的改變
    pFun();
    printf("%p, %p, %p \n", &runmsg, runmsg, *runmsg);
    printf("%p \n", print);

    system("pause");
}

//03.對於程式碼區而言:
//  1.函式指標有型別之分-->函式指標宣告格式的型別說明!
//  2.對函式名的幾種取值操作所得到的值的性質一樣
int main02(void)
{
    printf("%p, %p, %p \n", &runmsg, runmsg, *runmsg);
    //對於程式碼區而言,函式指標有型別區分
    //&runmsg = runmsg;//編譯器原理:獲取函式指標的地址,和函式地址一樣
    //*runmsg = *(&runmsg)=runmsg

    system("pause");
}

程式片段(03):01.DllInject.c
內容概要:非法呼叫

//moveto(int z, int x, int y);//跨點函式:所跨越的維度為三維

_declspec(dllexport)void main()
{
    void(*pFun)(void) = 0x0134110E;//宣告一個函式指標變數,用於間接呼叫函式實體
    pFun();//呼叫
}

程式片段(04):01.陣列名.c+02.二維陣列.c
內容概要:陣列名作為函式引數

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

//陣列名作為函式的引數,會退化為指標
void run01(int a[5])//一維陣列沒有副本機制,作為引數退化為一級指標
{
    printf("\n run=%d", sizeof(a));//大小是4
    a = a + 1;//陣列名可以進行改變,因為已經變為了指標變數的型別
    //int b[5];
    //b = b + 1;
}

void run02(int *p)//一位陣列名作為該函式的引數同樣會退化為一級指標,所以這裡採用一級指標進行接收
{
    for (int i = 0; i < 5; i++)
    {
        printf("\n %d", p[i]);
    }
}

void rev(int *p, int length)
{
    //指標法,由於下標,表現對指標的熟練度
    for (int *phead = p, *pback = p + length - 1; phead < pback; phead++, pback--)
    {
        int temp = *phead;
        *phead = *pback;
        *pback = temp;
    }
}

void show(int *p, int length)//一位陣列名作為函式的引數進行傳遞會自動退化為一級指標
{
    for (int i = 0; i < length; i++)
    {
        printf("\n %d", p[i]);
    }
}

void main01()
{
    int a[5] = { 1,2,3,4,5 };
    //a=1;
    printf("%d", sizeof(a));
    run02(a);

    system("pause");
}

void main02()
{
    int a[5] = { 1,2,3,4,5 };
    int *p = (int[]){ 1, 2, 3, 4, 5, 6 };
    rev(a, 5);
    show(a, 5);
    printf("\n\n");
    rev(p, 6);
    show(p, 6);

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

//01.在C語言當中的static關鍵字用途:
//  限定(變數|函式)只能作用於本檔案;
//  相當於縮小全域性變數的作用範圍!
//02.陣列作為函式形參的退化指標規律:
//  將所有陣列看做為一維陣列;
//  那麼指向該陣列當中首個元素的指標(變數|常量)
//  的指標就是該陣列所退化成為的指標
//03.如何快速定位堆上陣列的解析方式?
//  開闢幾位陣列就採用指向幾位陣列的指標進行解析!
//  至於指標的宣告格式採用上一個說明規律!
static void show(int(*pArr)[4])
{
    printf("run: sizeof(pArr) = %d \n", sizeof(pArr));
    //pArr = pArr + 1;//變數指標:陣列名作為函式指標將退化成為變數指標!
    int res = 0;
    for (int i = 0; i < 3; ++i)
    {
        for (int j = 4; j < 4; ++j)
        {
            printf("%3d", res += pArr[i][j]);
        }
        printf("\n");
    }   
    printf("平均分 = %d \n", res /= 3);//統計所用總分/人數
}

void search(int(* pArr)[4], int ifind)
{
    for (int i = 0; i < 3; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            printf("%3d", pArr[i][j]);
        }
        printf("\n");
    }
}

void get(int(*pArr)[4])
{
    int flag = -1;
    for (int i = 0; i < 3; ++i)
    {
        flag = 1;
        for (int j = 0; j < 4; ++j)
        {
            if (*(*(pArr + i) + j) < 60)
            {
                flag = 0;
                break;
            }
        }
        if (flag)
        {
            printf("%d號學生的成績及格! \n", i);
        }
        else
        {
            printf("%d號學生的成績不及格! \n", i);
        }
    }
}

//04.關於陣列記憶體的開闢方式:
//  1.按照所屬記憶體區塊兒:
//      棧記憶體+堆記憶體
//  2.按照解析方式:
//      標準二維陣列+分塊陣列模型
//  注:位於棧記憶體的陣列都是連續儲存的,位於堆記憶體的陣列視情況而定(指標決定對該片兒記憶體的解析方式)
int main03(void)
{
    int arrArr[3][4] = { 89, 78, 55, 71, 82, 54, 53, 70, 100, 98, 99, 91 };
    //這樣分配的二維陣列是位於堆記憶體的連續儲存空間(整體連續),而採用二級指標所指向的陣列是非整體連續的
    int(*pArr)[4] = (int[][4]) { 89, 78, 65, 71, 82, 94, 93, 70, 100, 98, 99, 91 };//棧上的二維陣列
    show(pArr);
    search(pArr, 2);
    get(pArr);

    system("pause");
}

程式片段(05):01.函式指標.c
內容概要:函式指標強化

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

//01.在C語言當中,當形參位於函式宣告位置的時候:
//  可以不用指明形參名稱;但是函式實現的時候需要指明形參名稱!
int add(int, int);

int add(int a, int b)
{
    return a + b;
}

int sub(int a, int b)
{
    return a - b;
}

int mul(int a, int b)
{
    return a * b;
}

int divv(int a, int b)
{
    return a / b;
}

int getmin(int a, int b)
{
    return a < b ? a : b;
}

int getmax(int a, int b)
{
    return a > b ? a : b;
}

//02.採用函式指標作為形參可以實現固化介面的作用
//  固化介面+動化邏輯!
void op(int(*pFun)(int, int), int a, int b)
{
    printf("%d \n", pFun(a, b));
}

//02.只要作為宣告的格式,就可以進行函式形參的省略!
//  1.區分函式呼叫和讀取函式指標常量所儲存的函式入口點地址
//  2.對於函式指標變數沒有自變的說法,因為毫無意義!
//03.區分函式指標變數和函式指標變數的型別!
//  訣竅:挖取函式指標變數宣告格式當中的函式變數名就是
//      該函式指標的具體型別!
int main01(void)
{
    //錯誤的定義方式,因為型別不匹配
    //int(*pFun)(int, int) = add(1, 2);//不行:函式呼叫-->返回結果-->被當做函式呼叫地址!-->錯誤現象
    //引數名可以省略,宣告的結構
    int(*pFun)(int, int) = add;
    //p1++;//函式指標變數,沒有變的說法!
    //p2++;
    //p3 + n;

    //int(*pFun)(int, int);//函式指標
    //int (*)(int, int)//函式指標的型別
    int(* get() )(int, int);//一個返回值為函式指標,引數為void型別的函式常量指著

    //難度解析:規律剖析
    //  原始:int(*get(int(*y)(int, int), double))(int, int);
    //  剖析:int(*        get(        int (*y)(int, int), double      )       )(int, int)
    //  解析:一個返回值為函式指標(int (*)(int, int)),引數為函式指標(int(*y)(int, int))和雙精度浮點型(double)的函式指標常量
    //  特點:函式實現的時候,所有形參必須具備形參名稱!
    //      作為函式的返回值型別宣告不需要進行名稱說明!
    //  擴充:指向該函式指標的函式指標變數宣告格式
    //  int (*(*pFun)(int(*y)(int, int), double))(int, int)-->該函式指標常量所對應的函式指標變數型別
    //  int (* (* const pFun)(int(*y)(int, int), double))(int, int)-->該函式指標常量所對應的函式指標型別
    system("pause");
}

程式片段(06):函式指標陣列.c
內容概要:函式指標陣列與指向函式指標的指標

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

int getmin(int a, int b)
{
    return a < b ? a : b;
}

int getmax(int a, int b)
{
    return a > b ? a : b;
}

int add(int a, int b)
{
    return a + b;
}

int sub(int a, int b)
{
    return a - b;
}

int mul(int a, int b)
{
    return a * b;
}

int divv(int a, int b)
{
    return a / b;
}

//01.陣列格式的規律:
//  在陣列所儲存元素的定義格式基礎上,在陣列元素名稱的末尾新增中括號[陣列元素個數];
//      該陣列元素名稱成為陣列名稱
//02.函式指標的規律:
//  在函式宣告格式的基礎之上,將函式名稱直接替換為(*pFun),那麼pFun
//      就是指向該函式的函式指標(*pFun)-->函式變數指標;(* const pFun)函式常量指標,如同陣列名
int main01(void)
{
    //int a;
    //int a[10];
    //int *p;
    //int *arrP[10];

    int(*funP)(int, int) = getmax;
    //函式指標陣列
    int(*funPArr[10])(int, int) = { getmin, getmax, add, sub, mul, divv };
    //funPArr是函式指標陣列的陣列名,二級函式指標可以直接儲存一個一級函式指標陣列的首地址
    //int (**funP1)(int, int)<=>int (*funP2[6])(int, int)
    //  funP1是二級函式變數指標+funP2是二級函式常量指著
    //注:凡是設計陣列名都是常量指標
    //printf("%d \n", sizeof(funP1));
    //funP2 = funP1;//funP2是一個常量指標
    for (int i = 0; i < 6; ++i)
    {//索引遍歷
        //printf("%d \n", funPArr[i](1, 2));//funPArr[i]代表函式指標變數本身
        printf("%d \n", (*(funPArr + i))(1, 2));//funPArr[i]=>*(funPArr + i)
    }
    for (int(**funPP)(int, int) = funPArr; funPP < funPArr + 6; ++funPP)
    {
        printf("%d", (*funPP)(100, 10));
    }

    system("pause");
}

//03.函式指標相關概念!
//int (*funP)(int, int)
//  funP是一級函式變數指標
//int (*funPArr[10])(int, int)
//  funPArr是二級函式常量指標
//int (**funPP)(int, int)
//  funPP是二級函式變數指標

//04.所有陣列的推導特點
//int a;
//int a[10];
//int * a;
//int * p1;
//int * p1[10];
//int ** p1;
int main02(void)
{
    //int intArr[6] = { 1, 2, 3, 4, 5, 6 };
    int(*funPArr[6])(int, int) = { getmin, getmax, add, sub, mul ,divv };
    //intArr和funPArr都屬於常量指標:作為數值而言,都是儲存與程式碼區符號表當中
    //int * p = (int []){ 1, 2, 3, 4, 5, 6 }//棧上開闢一個一維陣列
    //int(**funPP)(int, int);//二級函式變數指標,儲存函式指標陣列的陣列名
    //int (*[])(int, int);//函式指標陣列型別
    int(**funPP)(int, int) = (int(*[])(int, int)) { add, sub, mul, div };
    for (int i = 0; i < 4; ++i)
    {
        //printf("%d \n", funPP[i](100, 10));
        printf("%d \n",(*(funPP + i))(100, 10));
    }

    system("pause");
}

int main03(void)
{
    int(**funPP)(int, int) = malloc(4 * sizeof(int(*)(int, int)));//在堆記憶體開闢一個一級函式指標陣列
    *funPP = add;
    *(funPP + 1) = sub;
    *(funPP + 2) = mul;
    *(funPP + 3) = divv;
    for (int i = 0; i < 4; ++i)
    {
        //printf("%d \n", funPP[i](100, 10));
        printf("%d \n", (*(funPP + i))(100, 10));
    }

    system("pause");
}

//05.變數+變數陣列+指向變數的變數:
//  三種形式的推導規律
//int * p;------->int (*funP)(int, int);
//int * p[10];--->int (*funP[10])(int, int);
//int ** pp;----->int (**funPP)(int, int)

//06.typedef的強大作用:
//  某些情況之下的複雜函式指標必須通過typedef進行別名定義!
//int a;
//typedef int a;
//int b[10];
//typedef int b[10];
//double * d;
//typedef double * d;
//int (* funP )(int, int);
//typedef int (*funP)(int, int);
//int (*funP[10])(int, int);
//typedef int (*funP[10])(int, int);
//int (**funPP)(int, int);
//typedef int (**funPP)(int, int);
//typedef終極規律:
//  先定義變數名:型別+變數名
//  再使用前置typedef:於是變數名就成為了型別別名
//注:typedef並不是生產出一個新型別,而是為已有型別領取一個簡潔的別名
//  編譯後期操作型別-->某些情況之下的複雜函式指標不得不使用typedef關鍵字
//  進行別名定義!
int main04(void)
{
    //a a1;
    //b b1;
    //d d1;
    //p d1 = add;
    //px px1 = { add, sub, mul, divv };
    //pp pp1 = (px){ add, sub };
    //printf("%d \n", sizeof(px1));

    system("pause");
}

//07.函式指標終極形式!
//  原始:int(**x(int(*z)(int,int),int,double))(int);
//  返回值:int(**   x(int(*z)(int,int),int,double)   )(int)
//  形參:int(**   x(  int(*z)(int, int), int, int )   )(int)
//  解讀:
//      函式名:x是一個函式的名稱,屬於函式常量指標
//      返回值:int(**)(int),是一個函式二級函式指標
//      形參:int (*z)(int, int), int, double,有三個形式引數
//           分別是函式指標型別+int型別+double型別
//注:區分陣列指標和函式指標
//  陣列指標:
//  int arrArr[a][b]-->int (*p)[a][b];
//注:指向N維陣列的指標宣告規律:
//  只需要將陣列名稱替換為(*p)

//08.陣列指標和函式指標的區別:
//  1.都含有(*p)說明特點
//  2.各自的後續內容不一樣:
//      陣列指標跟的是中括號!
//      函式指標跟的是小括號!
//注:所有指向陣列型別的指標定義絕招!
//  先寫出陣列的宣告格式,再將陣列名稱替換為(*pArr);
//注:所有指向函式型別的指標定義絕招!
//  先寫出函式的宣告格式,再將函式名稱提花為(*Fun);
int main05(void)
{
    int(*funPArr[10])(int, int);
    int(*(*pFunPArr)[10])(int, int);//指向函式陣列的指標

    system("pause");
}

程式片段(07):dialog.cpp
內容概要:PingWindows

#include "dialog.h"
#include <QApplication>

void run()
{
    //01.在棧記憶體當中建立對話方塊容易自動回收掉!
    //Dialog w;
    //w.show();

    //02.在堆記憶體當中建立的對話方塊需要手動回收!
    //Dialog * p = new Dialog;
    //(*p).show();
    //p->show();

    //03.同樣是在棧記憶體當中建立的對話方塊
    //Dialog ws[10];
    //for (int i = 0; i < 10; ++i)
    //{
    //    ws[i].resize(100, 100);
    //    ws[i].move(100*i, 100*i);
    //    ws[i].show();
    }

     //04.逐個在堆記憶體當中建立對話方塊,不會被自動回收掉!
     //Dialog * p[10];
     //for (int i = 0; i < 10; ++i)
     //{
     //   p[i] = new Dialog;
     //   p[i]->resize(100, 100);
     //   p[i]->move(100*i, 100*i);
     //   p[i]->show();
     //}

    //5.建立50個位於堆記憶體當中的對話方塊
    //Dialog *px[5][10];
    //for(int i=0;i<5;i++)
    //{
    //    for(int j=0;j<10;j++)
    //    {
    //        px[i][j]=new Dailog;
    //        px[i][j]->resize(100,100);
    //        px[i][j]->move(100*i,100*j);
    //        px[i][j]->show();
    //    }
    //}
}

int main(int argc,char *argv[])
{
    QApplication a(argc,argv);
    //run();
    Dialog w;
    w.show();

    return a.exec();
}

程式片段(08):01.多執行緒.c
內容概要:函式指標陣列與多執行緒

///01.多執行緒.c
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <process.h>

void run01(void * p)
{
    MessageBoxA(0, "1", "1", 0);
}

void run02(void * p)
{
    MessageBoxA(0, "2", "2", 0);
}

void run03(void * p)
{
    MessageBoxA(0, "3", "3", 0);
}

//01.多執行緒內容:
//  1.關於模式的不同:
//      單執行緒模式:只能實現單執行緒,不能實現多執行緒
//      多執行緒模式:技能實現單執行緒,又能實現多執行緒
//  2.關於多執行緒的等待特點:
//      無限等待:-->如同單執行緒
//      有限等待:-->定時等待!
//  3.多執行緒情況下的主輔執行緒特點:
//      主執行緒進行全域性控制
//      輔助執行緒進行獨立控制
//  4.執行緒控制程式碼陣列的使用:管理執行緒
//      可以實現同時等待多條執行緒
//02.關於是否需要對棧記憶體陣列進行強制轉換的問題:
//  指向陣列的指標,無需進行型別轉換
//  指向指標的指標,需要進行型別轉換
//03.關於多執行緒的排程問題:
//  需要使用到執行緒控制程式碼陣列進行管理
//  WaitForMutipleObjects(arg1,arg2,arg3,arg4);
//      arg1:執行緒控制程式碼個數
//      arg2:執行緒控制程式碼陣列名
//      arg3:等待單個還是多個?-->競賽最快的那個!-->可以等待單個!
//      arg4:等待時間
int main01(void)
{
    //run01(NULL);
    //run02(NULL);
    //run03(NULL);//-->單執行緒模式

    for (int i = 0; i < 3; ++i)
    {
        HANDLE hd = _beginthread(run01, 0, NULL);
        //WaitForSingleObject(hd, INFINITE);//無限同步等待
        WaitForSingleObject(hd, 3000);//有限同步等待!
    }

    //HANDLE hd[3] = {0};//宣告執行緒控制程式碼陣列
    //HANDLE * hd = malloc(12);
    HANDLE * hd = malloc(3 * sizeof(HANDLE));
    //void(*funPArr[3])(void *) = { run01, run02, run03 };//函式指標陣列
    //採用二級函式變數指標進行指向一個棧記憶體的函式指標陣列
    void(**funPP)(void *) = (void(*[])(void *)) { run01, run02, run03 };
    for (int i = 0; i < 3; ++i)
    {
        hd[i] = _beginthread(funPP[i], 0, NULL);
    }
    //TRUE等待所有執行緒,false表示等待單個
    WaitForMultipleObjects(3, hd, FALSE, INFINITE);//無限等待多個執行緒結束
    //主執行緒,控制全域性

    system("pause");//卡頓主執行緒-->讓主執行緒不要提前執行完畢
}

////執行緒既可以同步,也可以非同步
////WaitForSingleObject(hd,300);//3000等待3秒
////主執行緒,控制全域性
////WaitForMultipleObjects(3,hd,TRUE,INFINITE);

程式片段(09):01.Alloca.c
內容概要:棧記憶體分配

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

//01.關於堆記憶體開闢四大函式和棧記憶體開闢單函式:
//  堆記憶體:malloc-->calloc-->realloc-->_recalloc
//  棧記憶體:alloca-->位於malloc.h標頭檔案
//注:棧記憶體沒有對應的記憶體回收函式,堆記憶體才有對應
//  的記憶體回收函式!!
int main01(void)
{
    int * p = alloca(10 * sizeof(int));//超過棧記憶體尺寸上限
    for (int i = 0; i < 10; ++i)
    {
        p[i] = i;
    }
    //free(p);//棧記憶體沒有回收的free();函式

    system("pause");
}

void show()
{
    int * p = alloca(10 * sizeof(int));//超過棧記憶體大小
    for (int i = 0; i < 10; ++i)
    {
        p[i] = i;
    }
    //free(p);//棧記憶體沒有匹配的free函式-->自動回收所屬記憶體
    printf("\n");
}

int main02(void)
{
    show();
    printf("\n\n");//促進記憶體回收動作的發生
    show();//檢驗棧記憶體陣列的自動回收特點
    printf("\n");//注意編譯器的優化情況

    system("pause");
}

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

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

//01.GCC支援棧記憶體的動態陣列的實現原理就是
//  位於malloc.h標頭檔案當中的alloca函式!
//02.VC編譯器不支援位於棧記憶體的動態陣列!
int main01()
{
    printf("Hello world! \n");

    //GCC當中的實現原理就是C語言當中的Alloc,但是這種規則不是C語言的標準機試
    int num=20;
    int a[num];

   // a=a;
    return 0;
}

程式片段(11):01.Main.c
內容概要:獲取引數

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

//01.定義主函式的格式:
//  無形參形式:
//      void main(void);
//      int main(void);
//  有形參形式:
//      void main(int argc, char * args[]);
//      int main(int argc, char * args[]);  
//02.標準主函式宣告格式詳解:
//  int main(int argc, char * args[], char * envp[]);
//      返回值:int-->體現跨平臺特性
//      引數1:argc-->說明了有效引數個數
//      引數2:args-->說明了有效引數列表
//          該字元指標陣列當中的第一個字元指標所指向的字串是當前所執行程式的程式名稱(程式碼區常量池"字串")
//          該字元指標陣列當中的第二個字元指標開始之後的指標表示的是當前程式執行時所指定的附加引數!
//      引數3:表示環境變數列表!
//          一個字元指標描述一個環境變數!
int main01(int argc, char * args[])
{
    for (int i = 0; i < argc; ++i)
    {
        puts(args[i]);
    }

    system("pause");
}

int main02(int argc, char * args[], char * envp[])
{
    printf("%d \n", sizeof(envp));//凡是陣列作為函式形參,該陣列都將會被退化為指標(統統只會佔用4個記憶體位元組!)
    //for (int i = 0; i < 100; ++i)
    //{
    //  puts(envp[i]);
    //}

    char **pp = envp;
    while (NULL != *pp)
    {
        puts(*pp);
        ++pp;
    }

    system("pause");
}

程式片段(12):01.返回.c
內容概要:返回指標

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

//01.對字元陣列除了定義並初始化格式之外的字串賦值方式!
//  必須藉助strcpy();函式進行對字元陣列的賦值操作!
//注:所以陣列的陣列名都絕對是一個常量指標!
//02.被掉函式在進行返回指標的時候絕對不能夠返回指向棧記憶體
//  的指標!切忌這一點兒
char * get()
{
    char str[100] = { 0 };//棧記憶體-->字元陣列
    strcpy(str, "Hello ZhouRuiFu");//為字元陣列賦予字串
    printf("%s \n", str);
    return str;
}

//03.如何快速定位某個陣列的兩種形式:
//  1.陣列的兩種形式:分別用指標形式進行表述
//      普通陣列形式-->指向陣列的指標
//      指標陣列形式-->指向指標的指標
//  2.任何陣列都可以用兩種形式的指標進行指向!
//      規律總結:針對任何一個維度的陣列!
//      普通陣列形式-->指向陣列的指標:將陣列名直接替換為(*arrP)
//      指標陣列形式-->指向指標的指標:將陣列名以及最高維度直接替換為(*pArr)
int * rungetmin()
{
    int * p = calloc(10, sizeof(int));//不會進行自動回收!
    time_t te;
    unsigned int seed = (unsigned int)time(&te);
    srand(seed);
    for (int i = 0; i < 10; ++i)
    {
        printf("%d \n", p[i] = rand() % 100 + 1);
    }
    int minIndex = 0;
    for (int i = 1; i < 10; ++i)
    {
        if (p[i] < p[minIndex])
            minIndex = i;
    }
    //p + minIndex = &p[minIndex];
    //p[minIndex] = *(p + minIndex)
    return p + minIndex;
}

//04.printf();函式針對於字串的解析特點:
//  字元陣列:按照字元陣列的長度+直接解析到'\0'
//      如果沒有解析到'\0'就會出現燙燙...
//  字元指標:解析字元指標所指向的字串本身+直接解析到'\0'
//      分字元指標所指向的記憶體空間
//          如果是棧記憶體和堆記憶體-->有可能字字串沒有字串結尾識別符號'\0'
//          如果是程式碼區常量池-->就一定會有字串結尾識別符號'\0'
int main01(int argc, char * args[], char * envp[])
{
    char str[100] = { 0 };
    strcpy(str, "Hello ZhouRuiFu");
    printf("%s \n", str);

    system("pause");
}

//05.迷途指標
int main02(int argc, char * argv[], char * envp[])
{
    char * p = get();
    printf("%s \n", p);//函式在返回指標的時候,不可以返回指向棧記憶體空間的指標
    //因為有可能出現迷途指標

    system("pause");
}

int main03(int argc, char * argv[], char * envp[])
{
    printf("最小的是%d \n", *rungetmin());

    system("pause");
}

程式片段(13):01.函式返回.c
內容概要:函式返回值

//#include <stdio.h>
//#include <stdlib.h>
//
////01.函式返回值,存在副本機制!
////    高階CPU:CPU-Cache
////    底端CPU:MEM-Cache
////注:副本資料和副本本身!
////    副本資料不可以獲取地址!
////    副本本身沒有操作許可權!
////    原本只是在已經被回收的棧記憶體!
//02.原本本身-->原本資料-->副本本身-->副本資料!
//int get()
//{
//  int a = 5;
//  return a;
//}
//
//int main(int argc, char * args[], char * envp[])
//{
//  //&get();//get();該操作所對應的返回值位於暫存器快取記憶體(高階CPU),(低階CPU)會將記憶體某段兒空間當中儲存
//  //  但是同樣不允許進行訪問操作!-->針對對於數值本身而言就是副本資料!
//  printf("%d \n", get());//返回的是副本資料
//
//  system("pause");
//}

程式片段(14):Mem.c
內容概要:左值右值與記憶體實體

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

//01.左右值:
//  左值:
//      1.能夠出現在賦值號左邊的值
//      2.存在有效的記憶體實體
//      3.變數
//  右值:
//      1.只能夠出現在賦值號右邊的值
//      2.不存在有效的記憶體實體
//      3.常量+表示式(暫存器操作)
int main01(int argc, char * args[], char * envp)
{
    int a = 1;
    a = a + 10;
    a = a + 8;
    //a = 3;
    //&5;
    //&(a + 1);
    a = a;//左值都存在記憶體實體
    //右值一般是出於暫存器當中,左值也可以當做右值進行使用
    //a + 1 = 3;

    system("pause");
}

int main02(int argc, char * args[], char * envp)
{
    int a = 10;
    int b = 20;
    printf("%p, %p \n", &a, &b);
    int * p;//指標變數
    p = &a;
    p = &b;

    system("pause");
}

//02.取地址符(&)取值之後的地址數值具備型別特點!
//  指標常量(數值不可變)+常量指標(指向不可變)
int main(int argc, char * args[], char * envp)
{
    //int num;//num是左值
    //const int num;//num是左值-->存在記憶體實體-->常變數
    const int num = 10;//&num-->int const * num:
    *(int *)(&num) = 4;//取地址之後的所得到的地址數值存在型別特點
    printf("%d \n", num);

    system("pause");
}

int main04(int argc, char * args[], char * envp[])
{
    //注意指標型別的統一!
    int * p = (int *)3;//地址意義!
    *p = 3;
}

//03.區分兩個概念:(空型別的指標+空指標)
//  空型別的指標:
//       實質:空型別的指標變數
//  空指標:
//      用於標識指標變數沒有任何指向!
int main05(int argc, char * argv[], char * envp[])
{
    int * p = NULL;//標識P這個指標變數沒有任何指向
    int num = 20;
    void * pv = &num;//任何型別的地址(只是用於儲存地址,但是不知道如何進行解析!)
    *(int *)pv;

    system("pause");
}

程式片段(15):01記憶體.c
內容概要:Malloc.Calloc.Realloc

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

//01.記憶體五大開闢函式總結:
//  堆記憶體:
//      malloc:不會初始化記憶體+整體記憶體尺寸
//      calloc:會初始化記憶體+元素個數+單個記憶體尺寸
//      realloc:不會初始化記憶體+原始指標+擴充後的總體大小
//      _recalloc:會初始化記憶體+原始指標+擴充後的元素個數+擴充後的單個記憶體尺寸
//  棧記憶體:
//      alloca:
//          位於malloc.h標頭檔案
//          實際佔用的記憶體總尺寸!
int main01(int argc, char * args[], char * envp)
{
    int * p = calloc(25, sizeof(int));//在堆記憶體開闢一個普通型別的一維陣列,會自動初始化為0
    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");
}

int main02(int argc, char * args[], char * envp)
{
    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[2398787] = 10;

    system("pause");
}

int main03(int argc, char * args[], char * envp)
{
    //int * p = malloc(100);//malloc不會初始化記憶體,是整體記憶體尺寸!
    int * p = calloc(25, sizeof(int));//calloc會初始化參記憶體,個數+尺寸
    printf("%p \n", p);
    for (int i = 0; i < 25; ++i)
    {
        p[i] = i;
    }
    int * p = alloca(123);

    system("pause");
}

程式片段(16):01.c
內容概要:Test

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

//01.在棧記憶體開闢4個位元組的記憶體空間:
//  注:指標可以指向四區的任何位置,只是指標的型別不一樣而已!
int * run()
{
    int * p = alloca(4);
    *p = 4;
    printf("%d \n", *p);

    return p;
}

int main01(int argc, char * args[], char * envp[])
{
    int * px = run();//返回指向棧記憶體的指標是錯誤的做法!
    printf("\n");
    printf("%d \n", *px);//棧記憶體在函式出棧之後,立即進行回收!

    system("pause");
}

//02.關於多維陣列獲取其所儲存資料的規律:
//  一維陣列:一顆星
//  二維陣列:兩顆星
//  三維陣列:三顆星
//  N維陣列:N顆星...
//注:獲取的物件既可以是常量指標,也可以是變數指標!
//  無論常量指標還是變數指標都會被當做陣列名對待!
int main02(int argc, char * args[], char * envp[])
{
    int arrArr[3][4] = {
        1, 2, 3, 4,
        5, 6, 7, 8,
        9, 10, 11, 12
    };//棧記憶體-->二維陣列-->靜態初始化!-->程式碼塊兒直接初始化!
    //printf("%d \n", arrArr[1][1]);
    printf("%d \n", *(arrArr + 1)[1]);//[]的優先順序既大於星號("*")又大於點號(".")
    printf("%d \n", *(*(arrArr + 1) + 1));
}

程式片段(17):01.Search.c
內容概要:記憶體模型大資料

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

#define srcPath "E:\\Resource\\TestData\\BigDB\\1EQQBig.txt"

//01.記憶體架構:檔案緩衝於記憶體當中,將記憶體用作資料臨時緩衝!
char ** g_pp;//全域性二級指標變數
int imax = 84357147;//檔案總行數
int jmax = 20027;//檔案最寬列數

int getimax()
{
    int hang = -1;
    FILE * pf = fopen(srcPath, "r");
    if (NULL == pf)
    {
        printf("檔案讀取失敗! \n");
        return -1;
    }
    else
    {
        printf("開始讀取! \n");
        hang = 0;
        while (!feof(pf))//到了檔案末尾返回1,沒有到檔案末尾返回0
        {
            char readStr[1024] = { 0 };
            fgets(readStr, 1024 - 1, pf);//一次讀取一樣進緩衝區
            ++hang;//自增
        }
        fclose(pf);
    }
    return hang;
}

int getjmax()
{
    int width = -1;
    FILE * pf = fopen(srcPath, "r");
    if (NULL == pf)
    {
        printf("檔案讀取失敗! \n");
        return -1;
    }
    else
    {
        printf("開始讀取! \n");
        while (!feof(pf))
        {
            char readStr[30000] = { 0 };
            fgets(readStr, 30000 - 1, pf);//每次讀取一行的資料到手動字元陣列緩衝區當中
            int strLength = strlen(readStr);
            if (strLength > 50)//字串篩選
            {
                //pus(readstr);
                if (strLength > width)
                {
                    width = strLength;
                }
            }
        }
        fclose(pf);
        printf("結束讀取! \n");
    }
    return width;
}

//02.字串函式的處理特點:
//  不僅可以處理字元陣列還可以處理字元指標!
//  相容C語言當中的兩種字串表示方式!
//載入待讀取的檔案資料進記憶體,構建記憶體架構模型
void loadFromFile()
{
    //宣告並初始化指標陣列
    g_pp = (char **)malloc(imax * sizeof(int *));//分配指標陣列
    memset(g_pp, '\0', imax * sizeof(char *));//清空指標陣列
    FILE * pf = fopen(srcPath, "r");
    if (NULL == pf)
    {
        printf("檔案讀取失敗! \n");
        return -1;
    }
    else
    {
        for (int i = 0; i < imax; ++i)
        {
            char str[1024] = { 0 };
            fgets(str, 1024 - 1, pf);//(按行+個數)進行字串讀取
            int strLength = strlen(str);
            if (strLength < 50)
            {
                g_pp[i] = malloc((strLength + 1)* sizeof(char));
                strcpy(g_pp[i], str);
            }
        }
        fclose(pf);
    }
}

//03.在記憶體架構模型當中執行檢索操作!
void search(char * str)
{
    if (NULL == g_pp)
        return;
    for (int i = 0; i < imax; ++i)
    {
        if (NULL != g_pp[i])
        {
            char * p = strstr(g_pp[i], str);
            if (NULL != p)
            {
                puts(g_pp[i]);
            }
        }
    }
}

int main01(int argc, char * args[], char * envp[])
{
    //printf("imax = %d \n", getimax());
    //printf("jmax = %d \n", getjmax());

    loadFromFile();
    while (1)
    {
        char str[100] = { 0 };
        scanf("%s", str);
        search(str);
    }

    system("pause");
}

程式片段(18):01.KaiFang.c
內容概要:開放資料檢索

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

#define srcPath "E:\\Resource\\TestData\\BigDB\\KaiFang.txt"
char ** g_pp;
int imax = 20151574;

int getimax()
{
    FILE * pf = fopen(srcPath, "r");
    if (NULL == pf)
    {
        printf("讀取檔案失敗! \n");
        return -1;
    }
    printf("讀取檔案成功! \n");
    int hang = 0;
    while (!feof(pf))
    {
        char readStr[1024] = { 0 };
        fgets(readStr, 1023, pf);
        ++hang;
    }
    fclose(pf);
    return hang;
}

void loadfromfile()
{
    g_pp = (char **)malloc(imax * sizeof(int *));
    memset(g_pp, 0, imax * sizeof(char *));
    FILE * pf = fopen(srcPath, "r");
    if (NULL == pf)
    {
        printf("開啟檔案失敗! \n");
        return -1;
    }      
    printf("開啟檔案成功! \n");
    for (int i = 0; i < imax; ++i)
    {
        char str[1024] = { 0 };
        fgets(str, 1023, pf);
        int strLength = strlen(str);
        g_pp[i] = (char *)malloc((strLength + 1) * sizeof(char));
        if (NULL != g_pp[i])
            strcpy(g_pp[i], str);
    }
    fclose(pf);
}

void searchStr(char * str)
{
    char strPath[100] = { 0 };
    sprintf(strPath, "E:\\Resource\\TestData\\Test\\%s.txt", str);
    FILE * pf = fopen(strPath, "w");
    if (NULL == g_pp)
        return;
    for (int i = 0; i < imax; ++i)
    {
        if (NULL == g_pp[i])
            continue;
        char * p = strstr(g_pp[i], str);
        if (p == NULL)
            continue;
        puts(g_pp[i]);
        fputs(g_pp[i], pf);
    }
    fclose(pf);
    system(strPath);
}

int main01(void)
{
    //printf("imax = %d \n", getimax());
    loadfromfile();
    printf("記憶體資料庫架構完成! \n");
    while (1)
    {
        char str[100] = { 0 };
        scanf("%s", str);
        searchStr(str);
    }
    system("pause");
}

相關文章