20160206.CCPP體系詳解(0016天)

尹成發表於2016-02-18

程式碼片段(01):.指標.c+02.間接賦值.c
內容概要:記憶體

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

//01.取地址操作符(&)詳解:
//  1.操作物件:變數名(實質:記憶體實體|資料實體)
//  2.操作特點:屬於暫存器操作(操作結果不是記憶體實體)
//      (1).取地址操作(&變數名)是在CPU的暫存器區域所完成的操作;
//      (2).地址資料不佔用記憶體,記憶體地址是在CPU核心構成元件暫存器產生的,
//          記憶體地址的數值和記憶體儲存沒有實質關係;
//  3.計算機地址匯流排:連線CPU與硬體記憶體的匯流排
//      (1).匯流排數目與CPU位數+作業系統位數有關
//      (2).地址數值作為一個常量恆久不變
//      (3).地址作為產量恆值並不會佔用記憶體
//          記憶體地址是在CPU的暫存器當中產生的,因此不具備記憶體實體,也就不會佔用記憶體
//      (4).匯流排的個數決定者位數的不同:
//          32條匯流排對應於32位作業系統;64條匯流排對應於64位作業系統
//02.對指標變數的解釋:
//  P一個指標變數,用於儲存普通變數的地址數值,然後通過操作指標變數的方式間接操作普通變數
//03.對於指標所佔用的位元組數分析:
//  Win32平臺所編譯的程式的指標型別佔用4個位元組
//  Win64平臺所編譯的程式的指標型別佔用8個位元組
int main01(void)
{
    int num = 10;
    int data = 20;
    //P是一個指標變數,可以儲存相同型別的不同變數的記憶體地址,常用於做間接訪問變數本身(記憶體實體)操作
    int * pNum = &num;//0x12312312
    int * pData = &data;
    printf("%d \n", sizeof(pNum));//指標變數所佔用的記憶體尺寸為4個位元組(Win3平臺所編譯的程式)
    printf("%p \n", pNum);//直接列印指標變數(pNum)的數值,相當於間接列印了普通變數(num)的地址
    printf("%p \n", &pNum);//表示獲取"指標"變數的地址,型別為int **

    system("pause");
}

//04.指標變數內容詳解:
//  1.型別:
//      既決定了記憶體實體步長,也決定了記憶體實體解析方式
//  2.數值:
//      就是一個地址數值
//  3.場景:(攜帶*號的情況)
//      賦值號左邊:
//          給記憶體實體寫入資料
//      賦值號右邊:
//          從記憶體實體讀取資料
int main02(void)
{
    int num1 = 1100;
    int num2 = 1200;//變數num都分配了記憶體,記憶體實體(資料實體)的數值代表變數的資料
    int * pNum = &num1;//pNum1是int型別的一級指標變數,&num1是一級指標真常量數值(含有型別含義)
    pNum = &num2;//pNum的值是num2這個變數(記憶體實體|資料實體)的記憶體地址,該記憶體地址統一佔用4個位元組,int * 地址,指標變數的長度統一為4
    //int * 對稱於pNum,指標型別既決定了記憶體實體的訪問位元組步長,也決定了記憶體實體的解析方式,特殊點:浮點型資料的特殊儲存形式,既是指數形式儲存還涉及到無符號型別(儲存實質階碼)
    *pNum = 12;//通過指標變數間接的訪問了普通變數的記憶體實體//根據指標數值找到記憶體地址,根據指標型別既決定了儲存步長也決定了儲存方式
    printf("%p \n", &pNum);//&pNum表示獲取一級指標變數pNum的記憶體地址,該地址數值儲存於CPU核心元件暫存器區域,完全不會佔用記憶體儲存
    printf("%d \n", num2);
    printf("num2 = %d \n", num2);

    system("pause");
}

//05.野指標:就是沒有進行初始化操作的指標變數
//  VC:編譯檢測,GCC編譯不檢測
//06.解析方式的不同含義:
//  有無符號:signed/unsigned
//  儲存原理:補碼原理/階碼原理
//07.指標變數所佔用的記憶體尺寸只跟編譯平臺有關:
//  Win32平臺:4位元組-->Win64平臺:8位元組
int main03(void)
{
    //int * p;//沒有物件的野指標,定義指標變數的時候必須進行指標變數的初始化操作
    //printf("%p \n", p);//列印指標變數當中所儲存的記憶體地址-->編譯不通過

    //解析方式的不同特點:
    //  signed/unsigned(只針對於10進位制的整數)+float指數形式儲存(階碼實質儲存)
    int num1 = 10;
    int num2 = 20;
    //float * pNum;
    //pNum = &num1;
    //printf("%f", *pNum);//指標變數的型別決定了指標變數對其所儲存的地址數值的解析步長(位元組尺寸)以及解析方式(補碼階碼)
    int * pNum;
    pNum = &num1;
    printf("%d \n", *pNum);//對記憶體地址數值的正確解析步長和正確解析方式
    printf("%d \n", sizeof(pNum));//指標變數所佔用的位元組長度在Win32平臺之下統一佔用4個位元組的記憶體尺寸,任何型別的地址都一樣採用int型別的統一位元組尺寸儲存
    printf("%d \n", sizeof(*pNum));//尺寸,方式

    system("pause");
}

//08.指標認識規律:
//  *pNum<=>num
int main04(void)
{
    int num = 20;
    int data = 30;
    int * pNum = &num;//int型別的一級指標變數儲存了int型別的普通變數的記憶體地址
    //&num = 1;//記憶體地址不允許操作,由於&num這個表示式是在CPU的暫存器當中所進行的操作,然而C語言是不能夠直接修改暫存器當中的資料的
    //pNum = &data;
    num = 3;
    printf("%d,%d \n", *pNum, num);//*pNum和num是完全等價的
    *pNum = 7;
    printf("%d,%d \n", *pNum, num);

    system("pause");
}
///02.間接賦值.c
#include <stdio.h>
#include <stdlib.h>

//01.函式形參具有副本機制:
//  1.傳值和傳指的資料層面意思都一樣;
//      只不過對同樣的數值有著不同的解析方式!
//  1.函式的副本機制總是存在;
//      無論是傳值還是傳地!
void change05(int num)//函式形參具有副本機制
{
    //函式形參具有副本機制,因此,無論是傳值還是傳指,都具備這樣的特性:
    //  傳值傳遞的是副本,傳指傳遞的是地址,因此出現不一樣的修改效果
    //  實質:都是副本,只不過對待同樣的數值解析方式不同而已
    num = 3;
    printf("change05 = %d \n", num);
}

int main05(void)
{
    int num = 10;
    change05(num);
    printf("%d \n", num);

    system("pause");
}

void change06(int * pNum)
{
    *pNum = 3;
}

int main07(void)
{
    int num = 10;
    change06(&num);//&num-->表示式-->沒有記憶體實體-->位於暫存器-->C語言不具備修改暫存器的許可權
    printf("%d \n", num);

    system("pause");
}

程式片段(02):01.Run.c
內容概要:

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

//01.外掛修改數值原理
//  1.注入DLL模組兒
//  2.根據指標進行跨函式訪問記憶體
_declspec(dllexport) void go()
{
    int * p = (int *)0xae0720;
    *p = 77;
}

//02.遍歷記憶體查詢原理:
//  1.單位元組記憶體遍歷
//  2.待查詢尺寸讀取
_declspec(dllexport) void run()
{
    typedef char * Char;
    for (Char  pStart = 0xae000000,pEnd = 0xae100000; pStart < pEnd; ++pStart)//單位元組遍歷指定記憶體範圍
    {
        int * p = (int *)p;//指標型別轉換,待查詢尺寸讀取資料進行匹配,以便於相同數值進行記憶體檢索
        if (77 == *p)
        {
            *p = 86;
        }
    }
}

程式片段(03):01.void.c+02.空指標.c
內容概要:Void與空型別指標

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

//01.空型別與空型別指標:
//  空型別:void
//  空型別指標:void *
//02.指標型別:
//  解析步長+解析方式
//03.空型別指標變數:
//  可以儲存任何型別的指標
int main01(void)
{
    //error C2182:"a":非法引用"void"型別 void
    int num = 10;
    double db = 19.3;
    //void num;//不合法-->沒有明確資料型別-->編譯器既不知道分配多大步長的記憶體塊兒,也不知道按照何種方式進行分配!
    void * vp = &num;//合法->Win平臺(32/64)編譯器明確指標所應使用的記憶體位元組尺寸-->不需要明確解析方式-->因為只是一個數值意義的地址
    vp = &db;//空型別的指標可以儲存任何型別變數(普通變數,一級指標變數,二級指標變數,三級指標變數,多級指標變數等等...)的地址,因為編譯器決定了地址儲存尺寸
    //printf("%d \n", *vp);//:error C2100:非法的間接定址(問題原因通常是採用空型別的指標對該指標所儲存的地址進行解析)->既不明確解析尺寸,也不明確解析方式,所以出錯
    //空型別的指標既不可以間接取值,也不可以間接賦值(也就是隻能儲存記憶體地址,而不能根據記憶體地址進行間接訪問操作)
    //*vp = 10;
    printf("%f \n", *((double *)vp));//將空型別的指標轉化為具體指標型別,然後就達到了既明確了指標的解析步長,也明確瞭解析方式

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

//01.結構體變數:
//  用於組織多種不同型別的變數
struct MyStruct//包含四根指標,花心的妹子,可以進行動態的擴充
{//一個由多個一級指標變數所構成的單個普通結構體變數
    int *p1;
    int *p2;
    int *p3;
    int *p4;
};

//02.空指標(NULL):
//  實質:(void *)0
//  作用:標識任何指標變數沒有儲存記憶體地址
//  特點:空指標所指向的記憶體實體不允許訪問,否則出現訪問異常
int main02(void)
{
    //標記指標變數是否儲存記憶體地址
    double db = 10.9;
    double 劉海華 = 10.9;
    double * pDb = NULL;//空指標(NULL):標識任何指標變數沒有儲存記憶體地址
    while (NULL == pDb)
    {
        //炮之
        pDb = &劉海華;
    }
    *pDb = 1;//空指標所指向的記憶體實體不允許進行操作

    system("pause");
}

程式片段(04):01.指標的宣告.c
內容概要:指標的宣告

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

//01.宣告指標變數:
//  資料型別+指標級數+指標變數
int main01(void)
{
    int * p1;
    int * p2;

    system("pause");
}

//02.特殊宣告格式:
//  格式:資料型別 * p, num;
//  特點:p是指標變數,num是普通變數
int main02(void)
{
    int numA, numB, numC;
    double * pNumA, pNumB, pNumC;
    printf("%d \n", sizeof(pNumA));//在Win32平臺之下,所有型別,所以級數的指標變數都佔用4個位元組的記憶體尺寸
    printf("%d \n", sizeof(pNumB));

    system("pause");
}

//03.巨集定義(巨集替換)和別名定義的區別:
//  巨集定義:第二整塊兒替換後置
//  別名定義:最後整塊兒替換前置
#define 整數  int
typedef int INT;
int main03(void)
{
    整數 x = 3;//替換後置
    INT y = 3;//替換前置
    printf("%d, %d \n", x, y);

    system("pause");
}

//04.別名定義規律:
//  變數定義:int * pNum
//  添關鍵字:typedef int * pNum;
//      特點:變數名變為型別別名
#define 指標 double *
typedef double * DBP;
int main04(void)
{
    指標 p1;
    DBP p2;
    //printf("%p, %p \n", p1, p2);
    printf("%d, %d \n", sizeof(p1), sizeof(p2));

    system("pause");
}

//05.特別注意:
//  1.巨集定義(巨集替換)和別名定義之間的區別
//  2.預編譯與真實別名
int main05(void)
{
    指標 p1, p2;//實質:double *p1, p2, p3;//4,8
    p1 = 0x123;
    p2 = 0x123;
    DBP p3, p4;//doube *型別
    p3 = 0x123;
    p4 = 0x123;
    printf("%d, %d \n", sizeof(p1), sizeof(p2));
    printf("%d, %d \n", sizeof(p3), sizeof(p4));

    system("pause");
}

程式片段(05):01.銀行.c+02.指標陣列.c
內容概要:資料地址對映

///01.銀行.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

//01.C語言編譯器特點:
//  未初始化的普通變數->編譯通過
//  未初始化的指標變數->編譯不過
//02.資料地址對映使用:
//  1.不允許改變原始記憶體實體的情況,實現排序
//  2.比較資料實體,交換指標變數
//  注:
//      1.只有整型資料才能使用位運算
//      2.升序的反面成立,既需要進行交換
int main01(void)
{
    int num1;//記憶體實體
    int num2;
    scanf("%d%d", &num1, &num2);//從從小到大
    int * p1 = &num1;//對映指標
    int * p2 = &num2;
    if (*p1 > *p2)//升序的反面成立,就需要進行交換
    {
        //p1 = p1 ^p2;//只有整型才能使用位運算
        int * ptemp = p1;
        p1 = p2;
        p2 = ptemp;
    }
    printf("%d, %d \n", *p1, *p2);

    system("pause");
}

//03.指標變數宣告與const關鍵字使用:
//  規律:const在指標變數名稱前面,也就是星號("*")的右邊
//      就表明該指標變數本身是一個偽常量,也就是該指標變數
//      不能直接進行修改,但是可以通過間接方式進行修改
//  特點:const int * p<=>int const * p
//      只需要關注const與星號("*")之間的相對位置
//  用處:限定訪問許可權
//      讀取許可權和修改許可權的綜合配對
//void accessControl(const int * p);
void accessControl(int const * p)//報表許可權:限定訪問許可權,間接操作的記憶體實體只能讀取,不能進行修改
{//const在指標當中所能起到的作用
    p = NULL;//通過指標變數間接訪問的記憶體實體是個真常量,不允許進行間接修改,但是當前指標變數可以進行修改
    printf("*p = %d \n", *p);//不能根據空指標進行記憶體實體訪問,既不能讀取也不能寫入(作業系統規定)-->編譯不報錯,但是執行報錯
    //*p = 10;//編譯報錯
}

int main02(void)
{
    int num = 100;
    int * pNum = &num;
    accessControl(pNum);
    printf("num = %d \n", num);

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

//int a[8];
//const int *p[8];
int a[8] = { 1,8,2,7,3,6,4,5 };//int型別的陣列 
const int * p[8] = { &a[0],&a[1],&a[2],a + 3,a + 4,a + 5,a + 6,a + 7 };//指標陣列
void main()
{
    printf("原來的陣列資料:\n");
    for (int i = 0; i < 8; i++)
    {
        printf("%d\n", a[i]);
    }
    printf("原來的指標陣列指向資料:\n");
    for (int i = 0; i < 8; i++)
    {
        printf("%d\n", *p[i]);
    }

    for (int i = 0; i < 8 - 1; i++)//最後一個資料不需要冒泡
    {
        for (int j = 0; j < 8 - 1 - i; j++)//冒泡數與與i形成對應關係
        {
            //指標陣列當中儲存的是指標,然後通過指標進行資料的獲取,通過比較指標所對應的資料,然後進行資料交換
            if (*p[j]>*p[j+1])//這裡使用指標陣列進行比較資料,用陣列指標進行資料獲取比較,然後進行資料交換 
            {//指標交換
                int *ptemp = p[j];
                p[j] = p[j + 1];
                p[j + 1] = ptemp;
            }
        }
    }

    printf("陣列資料:\n");
    for (int i = 0; i < 8; i++)
    {
        printf("%d\n", a[i]);
    }
    printf("指標陣列指向資料:\n");
    for (int i = 0; i < 8; i++)
    {
        printf("%d\n", *p[i]);
    }

    system("pause");
}

程式片段(06):01.輸入指標.c
內容概要:地址輸入

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

int main01(void)
{
    int num = 10;
    int data = 20;
    printf("%p, %p \n", &num, &data);
    int * pNum = &pNum;
    //對指標變數進行手動地址輸入:
    //  格式:scanf("%p", &pNum);
    //  特點:不需要攜帶0x字首,而且必須是大寫英文字母
    scanf("%p", pNum);//地址型別陣列需要使用%p這種輸入格式控制符+指標變數的地址
    *pNum = 20 + 3;//作業系統所使用的地址,胡亂進行訪問的時候容易出現系統問題
    printf("num = %d, data = %d \n", num, data);

    system("pause");
}

程式片段(07):01.Screen.c
內容概要:螢幕輸出圖片

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

int main(void)
{
    int i = 0;
    while (1)
    {
        HWND win = GetDesktopWindow();
        HDC winDc = GetWindowDC(win);//獲取指定視窗的輸出介面
        HDC memDc = CreateCompatibleDC(0);//獲取記憶體儲存的操作介面
        HBITMAP bitMap = (HBITMAP)LoadImage(win, TEXT("1.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
        SelectObject(memDc, bitMap);//選定圖形影象裝置-->設定顯示什麼
        BitBlt(winDc, 0, 0, i++, i++, memDc, i++, i++, SRCCOPY);
        Sleep(100);//模擬0.1秒
        ++i;
        if (3 == i)//到達3次
        {
            i = 0;//重置0次
        }
    }

    system("pause");
}

程式片段(08):test.c
內容概要:Test1

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

#define EN 1024
int flag = 0;
char * filePath = "E:\\Resource\\TestData\\BigDB\\Test.txt";

//陣列賦值
int intArr[EN];
void initArr(int intArr[EN])
{
    //time_t te;
    //unsigned int seed = (unsigned int)(time(&te));
    //srand(seed);
    srand((unsigned int)(time(NULL)));
    for (int i = 0; i < EN; ++i)
    {
        intArr[i] = rand() % EN + 1;
    }
}

//陣列顯示
void showArr(int intArr[EN])
{
    for (int i = 0; i < EN; ++i)
    {
        printf("%4d \n", intArr[i]);
    }
}

//氣泡排序
void bubbleSortArr(int intArr[EN])
{
    for (int i = 0; i < EN - 1; ++i)
    {
        for (int j = 0; j < EN - 1 - i; ++j)
        {
            if (intArr[j] > intArr[j + 1])
            {
                intArr[j] = intArr[j] ^ intArr[j + 1];
                intArr[j + 1] = intArr[j] ^ intArr[j + 1];
                intArr[j] = intArr[j] ^ intArr[j + 1];
            }
        }
    }
}

//選擇排序
void selectSortArr(int intArr[EN])
{
    int minIndex = 0;
    int minNum = 0;
    for (int i = 0; i < EN - 1; ++i)
    {
        minIndex = i;
        minNum = intArr[i];
        for (int j = i + 1; j < EN; ++j)
        {
            if (minNum > intArr[j])
            {
                minIndex = j;
                minNum = intArr[j];
            }
        }
        if (i != minIndex)
        {
            intArr[i] = intArr[i] ^ intArr[minIndex];
            intArr[minIndex] = intArr[i] ^ intArr[minIndex];
            intArr[i] = intArr[i] ^ intArr[minIndex];
        }
    }
}

//插入排序
void insertSortArr(int intArr[EN])
{
    int currentIndex = 0;
    int currentValue = 0;
    for (int i = 1; i < EN; ++i)
    {
        currentIndex = i;
        currentValue = intArr[currentIndex];
        while (0 < currentIndex && intArr[currentIndex - 1] > currentValue)
        {
            intArr[currentIndex] = intArr[currentIndex - 1];
            --currentIndex;
        }
        intArr[currentIndex] = currentValue;
    }
}

//二分查詢
int binarySearch(int intArr[EN], int value)
{
    int minIndex = 0;
    int midIndex = 0;
    int maxIndex = EN - 1;
    while (minIndex <= maxIndex)
    {
        midIndex = (minIndex + maxIndex) / 2;
        if (value == intArr[midIndex])
        {
            return midIndex;
        }
        else if (value < intArr[midIndex])
        {
            maxIndex = midIndex - 1;
        }
        else
        {
            minIndex = midIndex + 1;
        }
    }
    return -1;
}

//拉格朗日查詢
int lagrangeSearch(int intArr[EN], int value)
{
    int minIndex = 0;
    int ratioIndex = 0;
    int maxIndex = EN - 1;
    while (minIndex <= maxIndex)
    {
        //midIndex = minIndex + (maxIndex - minIndex) / 2;
        ratioIndex = minIndex + (maxIndex - minIndex)*(value - intArr[minIndex]) / (intArr[maxIndex] - intArr[minIndex]);
        if (value == intArr[ratioIndex])
        {
            return ratioIndex;
        }
        else if (value < intArr[ratioIndex])
        {
            maxIndex = ratioIndex - 1;
        }
        else
        {
            minIndex = ratioIndex + 1;
        }
    }
    return -1;
}

int main01(void)
{
    initArr(intArr);
    showArr(intArr);

    insertSortArr(intArr);
    printf("binSearch Index = %d \n", binarySearch(intArr, 880));
    printf("lagrangeSearch index = %d \n", lagrangeSearch(intArr, 880));

    system("pause");
}

相關文章