主頁 > 軟體設計 > pointer_to_the_advanced(指標進階)——重置版

pointer_to_the_advanced(指標進階)——重置版

2021-10-05 09:12:36 軟體設計

文章目錄

  • 回顧指標
    • 舉個例子回憶一下
  • 1.字符指標
    • 程式一:
    • 程式二:
    • 程式三:
    • 程式四:
    • 程式五:
    • 程式六:
  • 指標陣列 - 本質上是一個陣列
    • 程式一:
    • 程式二:
    • 程式三:
  • 這里我們回顧一下 二級指標
  • 陣列指標 - 指標
    • 程式一:
    • 程式二:
  • &陣列名 VS 陣列名
    • 舉個例子
  • 陣列指標的用法 : 一般用在 二維陣列
    • 程式一:
    • 在 明白 陣列指標 的真正用途之前,我們需要觀察一個程式
  • 程式二(陣列指標的用法):
  • 小匯總
  • 陣列引數、指標引數
    • 一維陣列傳參
    • 二維陣列傳參
  • 一級指標傳參
    • 思考: 當 一個函式 的 引數部分 為 一級指標的時候,函式能接受什么引數?
  • 二級指標的傳參
    • 程式一(傳二級指標變數本身和一級指標變數地址):
    • 程式二(傳 一級指標變數的地址):
    • 程式三(傳 二級指標變數的本身):
    • 程式四(傳 指標陣列 的 陣列名):
    • 思考:當函式的引數為二進制的時候,可以接收什么引數?
  • 函式指標
    • 程式一:
    • 程式二:
    • 程式二:
    • 閱讀兩段有趣的代碼
      • 代碼1
      • 代碼2(簡化 嵌套函式指標的寫法)
    • 函式指標實戰
  • 函式指標陣列
    • 練習(寫一個函式指標 pf,能夠指向 my_strcpy)
    • 接下來我們用函式指標陣列 實作計算器
      • 正常方式
      • 函式指標陣列方式
  • 指向 函式指標陣列 的 指標
  • 函式指標陣列 與 函式指標陣列的指標 (詳解)
  • 回呼函式
    • 實體:
  • qsort 函式 的使用
    • 先來回顧一下冒泡排序 :只能排整形資料
    • qsort 函式 - 庫函式 - 排序 - quick sort (快速排序)
      • 先來了解一下, qsort 的 結構型別
      • void * 介紹
      • 進入實戰
        • qsort(陣列名,該陣列元素個數,該陣列單個元素記憶體大小,(函式指標,比較兩個元素的 所用函式 函式 的 地址 )函式指標的 兩個引數 是:待比較的兩個元素的地址
      • 改進冒泡排序(回呼函式)
  • 本文結束

回顧指標

1,指標就是個變數,用來存放地址,地址 唯一標識 一塊 記憶體空間,
2.指標的大小是固定的 4 / 8位元組(32位平臺 / 64位平臺),
3.指標是有型別的 ,指標的型別決定了指標的 +- 整數的步長,指標解參考操作的時候的權限(一次訪問幾個位元組內容)
4.指標的運算

舉個例子回憶一下

#include<stdio.h>
void test(int* arr)// 這里的 arr 是 陣列首元素地址的一份拷貝
{
    printf("%d\n",sizeof(arr[0]));// 計算的陣列的一個元素大小,輸出為 4
     printf("%d\n",sizeof(arr));// 計算的是地址的大小(這里不能算是陣列的地址,因為 該地址是test傳過來的  首元素地址  的一份拷貝),輸出為 4
//  32位系統     
     sizeof(arr)在這里是求指標(地址)大小(4 byte)    
     sizeof(arr[0])在這里求的是一個元素的大小,int型別(4位元組)
      如果是64位系統,指標大小為 8 位元組,輸出此時就為 2
}

int  main()
{
    int arr[10] = { 0 };
    test(arr);//傳過去的是陣列首元素地址,因為不是單獨 與 sizeof 和 & 運算子連用
    return 0;
}

?

1.字符指標

在指標的型別 中 我們知道有一種 指標型別 為 字符指標 char*·

程式一:

#include<stdio.h>
int main()
{
    char ch = 'w';
    char* pc = &ch;
    *pc =='w';
    return 0;
}

?

程式二:

#include<stdio.h>
int main()
{
    char arr[] = "abcdef";
    char* pc = arr;//這里存入的是首元素 a 的地址
    printf("%s\n",arr);//abcdef
    printf("%s\n",pc);//abcdef   兩者都是向后列印,直到遇到'\0'停止
    %s 就是 根據 給的地址位置開始向后列印的,知道遇到'\0'停止
    return 0;
}

?

程式三:

#include<stdio.h>
int main()
{ 
    char* p = "abcdef";// "abcdef" 雙引號引起來的abcdef\0,是一個常量字串
    // 上運算式的意思是,把 a 的地址 存入指標變數p里面去
    printf("%c\n",*p);// *p == a
    // 輸出為 a
    printf("%s\n",p);//從存入p的這個地址(首元素a的地址)開始往后列印知道遇到 '\0'
    // 即輸出為 abcdef
    return 0;
}

?

程式四:

#include<stdio.h>
int main()
{
    const char* p = "abcdef";// 最穩妥寫法,就是在 * 前面加上 const
    *p = 'w';// 這時候你想改都改不了,況且 "abcdef" 是一個常量字串,也改不了
    printf("%s\n", p); //你會發現 沒有任何輸出,程式崩潰,違規操作
    //還有一個原因 abcdef\0 是一個常量字串,是不可以被改變的(const:是變數具有常量屬性)
    return 0;
}

?

程式五:

#include<stdio.h>
int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "abcdef";                     
    if (arr1 == arr2)//這里是兩個不同陣列的陣列名(不同的首元素地址),是否相同
         //肯定不同,兩個不同陣列的起始空間肯定不一樣
    {
        printf("hehe\n");
    }
    else
    {
        printf("haha\n");// 所以輸出這條陳述句
    }
    return 0;
}

?

程式六:

#include<stdio.h>
int main()
{


    建議下面兩句運算式在 * 前面防疫 const  ,這樣就更保險,無法通過解參考去修改常量字串的內容
     這樣寫,雖然意義不大(常量字串本身并不能被修改),語法更為準確
    const char* p1 = "abcdef";
    const char* p2 = "abcdef";
    這里把常量字串 abcdef\0 的首元素(a)地址分別存入 2 個指標變數
    是因為abcdef\0是常量字串,不可改變,因此沒有必要創建2個,直接共用一個,即 p1 == p2
    if (p1 == p2)
    {
        printf("hehe\n");// 所以輸出這條陳述句
    }
    else
    {
        printf("haha\n");
    }
    return 0;
}

?

指標陣列 - 本質上是一個陣列

在pointer文章中,我們也學了指標陣列,指標陣列 指的是一個 存放指標 的陣列

程式一:

#include<stdio.h>
int main()
{
    int arr[10] = { 0 };// 整形陣列
    char ch[5] = { 0 };// 字符陣列
    int* parr[4];// 這就是一個存放 整形指標 的陣列,簡稱 指標陣列 
    char* pch[5];// 這就是一個存放 字符指標 的陣列,簡稱 指標陣列 
    return 0;
}

?

程式二:

#include<stdio.h>
int main()
{

    int a = 10;
    int b = 20;
    int c = 30;
    int d = 40;
                                  
    int* arr[4] = { &a, &b, &c, &d }; 
    等價于     //int* pa = &a;
              //int* pb = &b;
              //int* pc = &c;
              //int* pd = &d;
    int i = 0;
    for (i = 0; i < 4; i++)
    {
        printf("%d ",*arr[i]);// 10 20 30 40
    }
    return 0;   // 指標陣列很少怎么用 ,下面程式將告訴你,指標陣列怎么使用
}

?

程式三:

#include<stdio.h>
int main()
{
    int arr1[] = { 1, 2, 3, 4, 5 };
    int arr2[] = { 2, 3, 4, 5, 6 };
    int arr3[] = { 3, 4, 5, 6, 7 };//以上三個運算式,就是在說我有三陣列,內容是,,,,
    int* parr[] = { arr1, arr2, arr3 };
    // 把上面 三個陣列 的  陣列名/陣列首元素地址  存入這個 指標陣列
    
    int i = 0;
    for (i = 0; i < 3; i++)// 遍歷 指標陣列 parr 的元素
    {
        int j = 0;
        for (j = 0; j < 5; j++)// 遍歷 指標陣列 parr 的 元素 所指向的 陣列 的 元素
        {
            printf("%d ",*(parr[i] + j));
        }
        printf("\n");
    }
    return 0;  
}

?

這里我們回顧一下 二級指標

int* arr1[]; //一級整形指標陣列.
int** arr[];// 二級整形指標陣列
ichar* arr2[]; //一級字符指標陣列
char** arr2[]; //二級字符指標陣列


?

陣列指標 - 指標

程式一:

#include<stdio.h>
int main()
{
    int* p = NULL;// 整形指標 - 指向 整形 的指標   作用:可以 存放  整形的地址
    char* pc = NULL;//字符指標 - 指向 字符 的指標  作用:可以 存放  字符的地址 


陣列指標 - 指向 陣列 的指標   作用:可以 存放  陣列的地址
    //int arr[10] = { 0 };  整形陣列
    // arr - 首元素地址
    // &arr[0] - 首元素的地址 
    // &arr - 陣列的地址

    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int(* p)[10] = &arr;// 把陣列的地址存起來
    // 為什么不能去掉(),因為 [] 的優先級 比 * 高,去掉(),p就和陣列[10]先結合,就成 指標陣列(存放指標的陣列) 了
    // 加 () 把 * 和 p 先結合,使 p 成為一個指標 (另外請注意 這里不是解參考,只是說明 p 是一個指標)
    // 把 *p 去掉,還剩int [10],意思就是說該指標(p)指向 一個 元素個數為 10 的陣列,且陣列每個元素的型別為整形
    // 即 上運算式 int(*p)[10] 就是陣列指標
    return 0;
}

?

程式二:

#include<stdio.h>
int main()
{
    char* arr[5];
    //如何把上運算式的陣列存入 陣列指標?
    如下
    char*(*pa)[5]=&arr;
    // 先用()把 * 和 pa(指標變數名) 結合起來,使 pa 為一個指標,也就是說 * 告訴我們 pa 是個指標
    //再在后面加[5],意思是 指標 指向一個元素個數為5的陣列
    // 又因為 指標 指向的陣列 的  元素型別 為 char*,所以在前面補上

    int arr2[10] = { 0 };
    int (*pa2)[10] = &arr2;
    return 0;
}


?

&陣列名 VS 陣列名

陣列名 絕大部分時候 都是為 首元素地址

只有兩種情況例外  &陣列名  和  sizeof(陣列名)
在這兩種情況下的陣列名,代表是整個陣列,取出的是陣列的地址(與陣列首元素地址相同,但意義不同)

舉個例子

&arr+1 - 直接 跳過一整個 陣列的位元組
比如 int arr[10],他的大小是40個位元組,&arr+1 陣列地址會加上40
如果是 arr+1 它就只跳過一個元素,意思就是 跳過第一個元素,地址指向第二個元素


?

陣列指標的用法 : 一般用在 二維陣列

程式一:

#include<stdio.h>
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10};
    int (*pa)[10] = &arr;
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        //printf("%d ",(*pa)[i]);// *pa 就相當于陣列名  *pa  ==  arr
        printf("%d ",* (*pa+i) );//等價于 printf("%d ",*(arr+i))
    }
    return 0; // 但 陣列指標 不是這么用的,以上只是讓你對它理解更深一點
}

在 明白 陣列指標 的真正用途之前,我們需要觀察一個程式

#include<stdio.h>
int main()
{
    int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int i = 0;
    int* p = arr;// 指標變數p 接收的是 陣列的首元素地址,即 p == arr
    for (i = 0; i < 10;i++)
    {
        printf("%d ",*(p+i));// 通過陣列首元素的地址,來遍歷陣列元素
    }
    printf("\n");
  上下兩個回圈表達大額效果是相同的
    for (i = 0; i < 10; i++)
    {
        printf("%d ", *(arr + i));
    }
    printf("\n");



    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);// arr[i] == *(arr+i) == *(p+i) == p[i]
    }
    printf("\n");
  上下兩個回圈表達大額效果是相同的
    for (i = 0; i < 10; i++)
    {
        printf("%d ", p[i]);// arr[i] == p[i]
    }

    //以上所有寫法都是等價的,
    return 0;
}

?

程式二(陣列指標的用法):

#include<stdio.h>
void print1(int arr[3][5], int x, int y)
{
    int i = 0;
    int j = 0;
    for (i = 0; i < x; i++)// 雙重循環遍歷二維陣列
    {
        for (j = 0; j < y; j++)
        {
            printf("%d ",arr[i][j]);// printf("%d ". *(*(arr+i)+j) );
        }
        printf("\n");
    }
}

void print2(int(*p)[5], int x, int y)//由于 二維陣列轉過的是 首元素地址(一維陣列的地址),需要一個一維陣列指標來接收
{
    int i = 0;
    int j = 0;
    for (i = 0; i < x; i++)// t通過雙重回圈,來遍歷二維陣列
    {
        
        for (j = 0; j < y; j++)
        {
            //printf("%d ", *(*(p+i)+j));// p 是 一維陣列的地址 (一行),*p 就是找到了第一行的資料,也就是一維陣列的陣列名  
                                       // *( p + i),i=0,還是第一行,i=1 是第二行, i=2 第三行
                                       // *(p+i)+j  就是第幾行 第幾個元素
            printf("%d ",p[i][j]);
// *(*(p+i)+j)== p[i][j] 先用一個括號將 p 和 i括起來,得到一維陣列的首元素地址,在對其解參考得到"陣列名",
再用一個括號將其和 j 括起來,得到 i 行 第 j 個元素的地址,在對其 解參考,得到該元素的值,并將其列印(注意! []* 的優先級高)
            //printf("%d ", *(p[i]+j))     
            // *(p+i) == arr[i] == p[i]
            // *(p[i]+j) == p[i][j]
        }
        printf("\n");
    }
}

int main()
{
    int arr[3][5] = { { 1, 2, 3, 4, 5 }, { 2, 3, 4, 5, 6 }, {3, 4, 5, 6, 7} };
    print1(arr,3,5);
     這里的 陣列名 就是 首元素【{12, 34, 5}】地址
     首先我們要 把二維陣列 看作 一維陣列,把 第一行{1,2,3,4,5}資料,也就是我們的第一個元素(首元素),看作一個一維陣列 int a[5]
    //以此類推 第二行資料 就是 第二個元素(也是一個一維陣列), 第三行資料 就是 第三個元素(也是一個一維陣列)
    print2(arr,3,5);// 那么 傳過去的是陣列首元素地址,而且是一個一維陣列的地址
    return 0; 
}

?

小匯總

int arr[10] //arr是一個整形陣列,具有10個元素.

int* parr1[10];//parr1是指標陣列,首先它是一個陣列,具有10個元素,且每個元素的型別是(int*) ,

int(*parr2)[10]; ???parr2是一個整形陣列指標
用括號讓 * 與 parr2 先結合
所以parr2是一個指標,該指標指向了一個陣列,陣列有10個元素,每個元素的型別是int

int( * parr3 [10] ) [5]
因為()的優先級最高,所以先判斷()里的內容
又因為 * 和[],[]的優先級更高,所以 parr3先與[]結合,所以parr3是個陣列
把 parr3[10]去掉,還剩下int(* )[5] ,就是它的元素型別.

例子: int arr[10];去掉arr[10],乘下的 int,就是它的型別(整形)

那int(* )[5]是個什么型別?
仔細觀察一下,你會發現,它和陣列指標 int(*p)[10] 一樣
.那么、我們可以說 parr3是個陣列,元素有10個,每個元素都是一個整形陣列指標,這個指標能指向5個元素,每個元素的型別是int.的整形陣列,(我們稱 ? int( * )[5] ?為 整形陣列指標型別,簡稱 陣列指標型別 )
在這里插入圖片描述


?

陣列引數、指標引數

在寫代碼的時候難免要把 [ 陣列 ] 或者 [ 指標 ]傳給函式,那函式的引數該如何設計?

一維陣列傳參

#include<stdio.h>
void test(int arr[])//  OK
{}

void test(int arr[10])// OK
{}

void test(int* arr)//  OK
{}





void test2(int* *arr)// OK
{}


void test2(int* arr[20])// OK  這里 20 跟上運算式一樣可以省略
{}

int main()
{
    int arr[10] = { 0 };
    int* arr2[20] = { 0 };
    test(arr);
    test2(arr2);
    return 0;
}

?

二維陣列傳參

#include<stdio.h>
void test(int arr[3][5]) // OK 
{}
void test(int arr[][5]) // OK 維陣列 的 行 是能省略的
{}


void test(int arr[3][]) // NO 二維陣列 的 列是不能省略的
{}
void test(int arr[][])  // NO 二維陣列 的 列是不能省略的
{}

void test(int* arr)  // NO 這只能用來接收一維陣列的元素地址(陣列的地址,要用陣列指標來接收,而不是陣列一個一級指標),不能接收二維陣列的傳參
{}
void test(int* *arr)  //  二維陣列的陣列名 是 首元素地址(第一行的地址,可以將其當做一個一維陣列的地址】)
{}                    // 一個陣列的地址,是不 能放進 二級指標的
                      // 二級指標 是用來 存放 一級指標變數 的地址
                      //所以該寫法也是錯的 NO


void test(int (*arr)[5]) // ok
 ()使 * 和 arr 先結合,使 arr 成為指標,用它來接收二維陣列的地址(首元素地址)
將二維陣列的首元素地址 就第一行資料的地址(可以看作  一維陣列的地址 ,所以需要一個 陣列指標 來接收),
該指標指向一個有五個元素的陣列(二維陣列的一行資料),且每個元素的型別為 int 
{}


int main()
{
    int  arr[3][5] = { 0 };
    test(arr);// 二維陣列 的 陣列名 是 首元素地址(第一行的地址資料的地址,將其當做 一個 一維陣列的陣列名)
}

在這里插入圖片描述


?

一級指標傳參

#include<stdio.h>
void print(int* p,int sz)
{
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ",*(p+i));
    }
}

int main()
{
    int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int* p = arr;
    int sz = sizeof(arr) / sizeof(arr[0]);
    print(p,sz);//一級指標p 傳給函式
    return 0;
}

思考: 當 一個函式 的 引數部分 為 一級指標的時候,函式能接受什么引數?

#include<stdio.h>
void tset(int* p)// 這里 的一級指標變數 p  是 print 函式 中 一級指標變數 的 一份拷貝
{
    ;
}
void print(int* p,int sz)
{
    int a = 10;
    int* p = &a;
    test(&a);// ok
    test(p);// ok,這里傳的是 一級指標變數本身,而不是 一級指標 的 地址,所以不需要 二級指標來接收
    return 0;
}
能接受 一級指標變數本省 和 一個整形資料的地址

?

二級指標的傳參

程式一(傳二級指標變數本身和一級指標變數地址):

#include<stdio.h>

void test(int* *ptr)
{
    printf("num = %d\n",**ptr);
}

int main()
{
    int n = 10;
    int* p = &n;
    int* *pp = &p;// 最右邊的*,說明pp是一個指標,該指標指向 p;左邊 int*是型別,是該指標指向 p 的型別
    test(pp);// ok,這里傳的是 二級指標變數本省,所以接收只需要 一個 二級指標,相當于 將其拷貝一份資料
    test(&p);// ok 這里傳的是 一級指標變數 的 地址,一個一級指標 的 地址,需要一個二級指標來接收
    return 0;
}

?

程式二(傳 一級指標變數的地址):

#include<stdio.h>
void test(int* * p)
{
    printf("hehe");
}
int main()
{
    int* ptr = 0;
    test(&ptr);// 這里就是典型的,傳址(傳一級指標變數的地址),需要一個二級指標來接收
    return 0;
}

程式三(傳 二級指標變數的本身):

#include<stdio.h>

void test(int* * p)
// 因為 test函式傳的是二級指標變數的本身,而不是二級指標變數的地址,所以我們只需用一個相同型別的二級指標來接收test的傳參
// 這里 的 二級指標變數 p, 相當于 是 二級指標變數 pp 的一份臨時拷貝
{
    printf("hehe");
}

int main()
{
    int* ptr = 0;
    int* *pp = &ptr;
    test(pp);// 這里傳的是 二級指標變數本身
    return 0;
}

程式四(傳 指標陣列 的 陣列名):

#include<stdio.h>
void test(int* * p)
{
    printf("hehe");
}

int main()
{
    int* arr[10];
    test(arr);
//指標陣列 也可以,這里傳的是陣列名(首元素地址),因為 指標陣列里 存放的元素的型別是 int* ,也就是說 首元素地址 其實是一個 一級指標變數(元素)的地址,一個 一級指標變數的地址,需要一個 二級指標變數 來接收
    return 0;
}

?

思考:當函式的引數為二進制的時候,可以接收什么引數?

一級 指標變數 的地址

二級指標變數的本身

存放 一級指標陣列 的 陣列名

?

函式指標

陣列指標,是一個指向陣列的指標
那么,函式指標,是一個指向函式的指標 ,一個存放 函式的地址 的指標,

程式一:

#include<stdio.h>

int add(int x, int y)
{
    int z = 0;
    z = x + y;
    return z;
}

int main()
{
    int a = 10;
    int b = 20;
    printf("%d\n", add(a, b));


    printf("%p\n",&add);
    printf("%p\n", add);// 這兩句程式,輸出的結果是一樣的,
    //因為  函式名 和 &函式名 都是函式的地址, 而 函式 只有一個,所以函式的地址是唯一的
    return 0;
}

?

程式二:

#include<stdio.h>

int add(int x, int y)
{
    int z = 0;
    z = x + y;
    return z;
}

int main()
{
    int arr[10] = { 0 };// 整形陣列
    int(*p)[10] = &arr;// 整形陣列的地址,需要一個 整形陣列指標來接收

    int (*pa)(int,int) = add;
// 函式指標,先用()把 * 和 pa 先結合,是 pa 是一個指標(指標才能存放地址),
// 該指標 指向的函式 有兩個整形引數,然后該函式型別是 int
// 只需要告訴函式的引數型別就行,x 和 y 寫不寫都無所謂   
    printf("%d\n",(*pa)(2, 3));// 5
    (*pa) == add,就是說 add 的地址,存入 函式指標 pa 中,我們再通過 解參考,找到add 函式,并對其呼叫,
    就 好像一個函式宣告,告你說,這樣也能呼叫 add 函式
    return 0;
}

程式二:

#include<stdio.h>

void print(char* str)
{
    printf("%s\n", str);
}

int main()
{
    void(*p)(char*) = print;
    //這里不是解參考,我們用(),先讓 * 和 p 結合,使p為一個指標
    //該指標 指向函式的引數的型別是 char*
    //函式回傳型別是 void
    //這個時候 p 就是我們的所謂的函式指標

    (*p)("hello word!");
    等價于
    print("hello word!")
    return 0;
}
你可以這么認為 函式指標 就是 函式名

?

閱讀兩段有趣的代碼

代碼1

    void (*)() - 函式指標型別
    
 (  void (*)()0, 最外面的括號 是 強制型別轉換符號,就是把 0 進行強制型別轉換( int -> 函式指標型別  )
 到那時候 0 就是一個函式的地址

( * ( void ( * ) ( ) ) 0) 
 *解參考,呼叫 0 地址處的該函式【void (*)(),假設該式子等價于  void(*p)(int,int),這樣你們應該能理解】
 就是呼叫 地址 0 處 的 函式 void(*p)(int,int)

?

代碼2(簡化 嵌套函式指標的寫法)

void(*signa1( int, void(*)(int))) (int);
signal這是一個函式宣告
signal 函式的引數有兩個,第一個是 int ,第二個是函式指標,該函式指標指向的函式的引數型別是int,回傳型別是void
 signal 函式 的 回傳型別 也是一個 函式指標,該函式指標指向的函式的引數型別是int,回傳型別是void
 然后我們把上面 三個去掉(函式 signal,和 它的兩個引數)
 剩下: void (*)(int) 這又是一個函式指標型別,是函式signa1(int, void(*)(int))的回傳型別




typedef  void(* pfun_t)(int); 現在這個時候 pfun_t 就是函式指標型別
// pfun_t   簡寫(重命名)是不能放后面的,要放在(*這里)

上下兩者 對 函式名 重命名的寫法是不相同的

typedef unsigned int uint;  // 簡寫是可以直接寫在后面的


 簡化      void(*signa1(int, void(*)(int)))(int);

typedef  void(*pfun_t)(int);

pfun_t signa1(int, pfun_t);

?

函式指標實戰

#include<stdio.h>
int add(int x, int y)

{
    int z = 0;
    z = x + y;
    return z;
}

int main()
{
    int arr[10] = { 0 };
    int(*p)[10] = &arr;

    int (*pa)(int,int) = add;// 函式指標,先用()把 * 和 pa 先結合,是 pa 是一個指標(指標才能存放地址),指向函式的兩個整形引數,然后該函式型別是 int
    //          只需要告訴函式的引數型別就行,x 和 y 寫不寫都無所謂   
    
    printf("%d\n",(*pa)(2, 3));// 5
    printf("%d\n", (**pa)(2, 3));// 5
    printf("%d\n", (***pa)(2, 3));// 5
    照理說 第一次 *pa 解參考,通過 pa的存入的地址,找到add 函式,第二次 **pa,以*pa為地址再找,以此類推,
     但由 上三個運算式 的輸出結果顯示,* 沒有起到應有的 作用
     那么我們可不可以 去掉 * 呢?
     
    printf("%d\n", pa(2, 3));// 5 
     結果證明 可以
     原因在程式一中:
      printf("%p\n",&add);
      printf("%p\n", add);// 這兩句程式,輸出的結果是一樣的,
    //因為  函式名 和 &函式名 都是函式的地址, 而 函式 只有一個,所以函式的地址是唯一的
       那么,再加上 pa本來就是等函式的地址(pa == &add == add),所以 在使用函式指標時,可以直接寫 函式指標變數

     有的時候會有些人會加上 * 的原因,這樣可讀性高,讓讀者 能明白 pa 其實是一個指標
     但 加上了 * ,要注意加上(),要不然會出現問題,
    例 *pa(2,3),這時pa會先于(2,3)結合,形成一次函式呼叫,然后再解參考,就是說 對 呼叫結果 5 進行解參考,不是我們想要的結果 
    return 0;
}


?

函式指標陣列

#include<stdio.h>

int add(int x, int y)// 加法
{
    return x + y;
}

int sub(int x, int y)// 減法
{
    return x - y;
}

int mul(int x, int y)// 乘法
{
    return x * y;
}

int div(int x, int y) //除法
{
    return x / y;
}

int main()
{
    int* arr[5];//指標陣列  arr是一個陣列,有5個元素,且每個元素的型別為int*,即每個元素都是一個整形指標、
    
    int(*pa)(int ,int) = add;//  我們有4 個函式要被呼叫,要是 一個接著一個接著這樣會麻煩
    
    這時需要一個陣列,這個陣列可以存放4個函式的地址 - 函式指標的陣列(前提是它們的引數和型別是完全一致的)
    int(*parr[4])(int,int) = {add,sub,mul,div};// 函式指標陣列
    將上式拆成 2 部分
    int (*) (int,int)    
    parr[4]
    表達的意思是 parr 先和 [4] 結合,成為一個陣列,元素有4個,每個元素 都是一個函式指標

    int i = 0;
    for(i = 0; i < 4; i++)
    {
        printf("%d ",parr[i](2, 3));// 加法 5, 減法 -1  乘法 6 除法 0
    }
    return 0;
}


?

練習(寫一個函式指標 pf,能夠指向 my_strcpy)

首先我們要知道 我們的自定義函式的型別: char* my_strcpy(char* dest, const char* src);
然后我們對它進行改造:char* ( * pf)(char * ,const char * ) = my_strcpy;

#include<stdio.h>
#include<assert.h>
char* My_strcopy(char* destination,const char* source)//這里本質上是個指標
{
	assert(destination&&source);
	char* ret = destination;
	while (*destination++ = *source++);
	return ret;
}

int  main()
{
	char arr[30] = { 0 };
	char arr1[] = "abc";
	char arr2[] = "def";
	char arr3[] = "adcde";
	char arr4[] = "abcdef";
	char* arr5[4] = { arr1, arr2, arr3, arr4 };
	char*(*pf)(char*, const char*) = My_strcopy;
	for (int i = 0; i < 4; i++)
	{
		printf("%s\n", pf(arr,arr5[i]));
	}
	return 0;
}

寫一個函式指標陣列,能存放 4個 my_strcpy 函式的地址
char* ( * pfarr[4])(char*, const char*);

     這個我就不寫,就跟 指標陣列一樣,給個打括號放函式名就行了

?

接下來我們用函式指標陣列 實作計算器

正常方式

#include<stdio.h>
void menu()
{
	printf("**************************\n");
	printf("** 1,add      2.sub ******\n");
	printf("** 3,mul      4,div ******\n");
	printf("*****   0.退出      ******\n");
	printf("**************************\n");
}

int add(int x, int y)
{
	return x + y;
}
int sub(int x, int y)
{
	return x - y;
	}
	int mul(int x, int y)
	{
	    return x * y;
	}
	int div(int x, int y) 
	{
	    return x / y;
	}
	
	int main()
	{
	    int input = 0;
	    int x = 0;
	    int y = 0;
	    do
	    {
	        menu();
	        printf("請選擇");
	        scanf("%d",&input);
	        switch (input)
	        {
	        case 1:
	            printf("請輸入兩個運算元:");
	            scanf("%d%d", &x, &y);
	            printf("%d\n",add(x,y));
	            break;
	        case 2:
	            printf("請輸入兩個運算元:");
	            scanf("%d%d", &x, &y);
	            printf("%d\n",sub(x,y));
	            break;
	        case 3:
	            printf("請輸入兩個運算元:");
	            scanf("%d%d", &x, &y);
	            printf("%d\n",mul(x,y));
	            break;
	        case 4:
	            printf("請輸入兩個運算元:");
	            scanf("%d%d", &x, &y);
	            printf("%d\n",div(x,y));
	            break;
	        case 0:
	            printf("退出程式");
	            break;
	        default:
	            printf("輸入錯誤,請重新選擇:");
	            break;
	         
	        }
	    } while (input);
	    return 0;
	}

?

函式指標陣列方式

#include<stdio.h>
void menu()
{
    printf("**************************\n");
    printf("** 1,add      2.sub ******\n");
    printf("** 3,mul      4,div ******\n");
    printf("*****   0.退出      ******\n");
    printf("**************************\n");
}

int add(int x, int y)
{
    return x + y;
}
int sub(int x, int y)
{
    return x - y;
}
int mul(int x, int y)
{
    return x * y;
}
int div(int x, int y)
{
    return x / y;
}

int main()
{
    int input = 0;
    int x = 0;
    int y = 0;
    int(*parr[])(int, int) = { 0, add, sub, mul, div };// parr 是一個函式指標陣列
    // 函式指標陣列的用途 又稱 轉移表
    do
    {
        menu();
        printf("請選擇");
        scanf("%d", &input);
        if (input >= 1)
        {
            printf("請輸入運算元:");
            scanf("%d%d", &x, &y);
            printf("%d\n", parr[input](x, y));
        }
        else if (input == 0)
        {
            printf("退出程式\n");
            break;
        }
        else
        {
            printf("選擇錯誤,請重新輸入");
        }
    } while (input);
    return 0;
}

?

指向 函式指標陣列 的 指標

int arr[10];
int(*p)[10] = &arr; // p就是一個指向 整形陣列 的指標
//這就是一個整形陣列指標

int* (*p)[10] = &arr;// p 就是一個指向 整形指標陣列 的 指標


int add(int x, int y)
{
    return x + y;
}

int(*pf)(int,int) = add;//pf 是 函式指標
int(*pf[4])(int,int)// 這是一個 函式指標陣列
int(*(*pf)[4])(int,int)// pf 就是一個 指向函式指標陣列的 指標
()讓 pf 和 * 先結合,使其成為一個指標,去掉他們還剩 int(*()[4])(int,int)很明顯就是一個 函式指標陣列

?

函式指標陣列 與 函式指標陣列的指標 (詳解)

函式指標陣列
/先寫個陣列pfarr[5]
pfarr[5];

// 再用()把 * 和陣列 pfarr[5],結合 -指標陣列
(*parr[5]);

// 再寫函式引數型別與回傳型別
int(*parr[5])(int, int);// pafarr 是一個 函式指標 的 陣列


指向  函式指標陣列 的 指標
*ppfarr = &pfarr;// 函式指標陣列 的 地址,存起來,
//ppfarr就是 函式指標陣列 的 指標

//再將其替換入內,記得加括號
即:
int(*(*ppfarr)[5])(int, int) = &pfarr;
// 首先用()把 pparr  先和 * 結合,即pparr是一個指標

//該指標向外一看,指向一個陣列 [5],  pparr 指標指向 一個陣列

//去掉 陣列 和 指標 : int(*)(int,int)  這是一個函式指標型別

//陣列每個元素,都是 函式指標型別

// 所以 ppfarr 就是一個指向 函式指標 陣列 的 指標


int(*p)(int, int); // 函式指標
int(*p[5])(int, int); // 函式指標陣列
int(*(*p)[5])(int, int);// 函式指標陣列 的 指標
函式指標陣列的指標 - 首先它是指標,其次 它是一個陣列,最后是 一個函式指標

?

回呼函式

回呼函式:
是一個通過函式指標呼叫的函式,如果你把函式的指標(地址)作為引數傳遞給另一個函式
當這個指標 被用來 呼叫 其所指向的函式時,我們就說這是回呼函式,

回呼函式不是由 該函式 的 實作方  直接呼叫,而是在 特定的事件 或 條件 發生時 由 另外的一方 呼叫 的
用于 對該 事件 或 條件 進行回應,

簡單來說,就是通過 把 實作功能函式的地址,交給另一哥 函式 ,由它去呼叫我們的 功能函式,
不能直接呼叫 功能函式,(就是說,你想玩電腦,得經過父母的同意,不然你玩個屁,)

實體:

#include<stdio.h>
void menu()
{
    printf("**************************\n");
    printf("** 1,add      2.sub ******\n");
    printf("** 3,mul      4,div ******\n");
    printf("*****   0.退出      ******\n");
    printf("**************************\n");
}


// 這些是你上電腦,想玩的游戲
int add(int x, int y)
{
    return x + y;
}
int sub(int x, int y)
{
    return x - y;
}
int mul(int x, int y)
{
    return x * y;
}
int div(int x, int y) 
{
    return x / y;
}

void calc(int(*pf)(int))// 這就是你父母,你父母同意了(函式收到相應地址,使用相應的功能),你才能玩
{
    int x = 0;
    int y = 0;
    printf("請選擇兩個運算元:");
    scanf("%d%d",&x,&y);
    printf("%d\n", pf(x, y));
}

int main()
{
    int input = 0;
    do
    {
        menu();
        printf("請選擇");
        scanf("%d",&input);
        switch (input)
        {
        case 1:
            calc(add);//回呼函式
            這里 就好比,你向你父母申請,請打開麥克風交流,,,
            break;
        case 2:
            calc(sub);
            break;
        case 3:
            calc(mul);
            break;
        case 4:
            calc(div);
            break;
        case 0:
            printf("退出程式");
            break;
        default:
            printf("輸入錯誤,請重新選擇:");
            break;
         
        }
    } while (input);
    return 0;
}


?

qsort 函式 的使用

先來回顧一下冒泡排序 :只能排整形資料

#include<stdio.h>
void bubble_sort(int*arr, int sz)
{
    int i = 0;
    for(i = 0; i < sz - 1; i++)//  整個陣列,只用 排 元素的總個數 - 1,因為最后不需要排
                                 //  就好比 432  1,前面的 比 1 大的數,都排到前面去了,
                                 //  1 它自己就只能 stay here
    {
        //一趟冒泡排序
        int j = 0;
        for (j = 0; j < sz - 1 - i; j++)
        //那 一個數 和 它屁股后面的數 進行比較
         {
            if (arr[j] > arr[j + 1])
            // 如果 它 比 它屁股 大,它就前進一位,然后再跟它目前的屁股,再比一次,直到,他沒屁股大,停止,然后屁股再去跟它后面的人去比
            // 第一次要比 九次,
            // 它一次比完之后,一開始 跟它比的 屁股的哪一位,他就要開始比了,
            // 由于 屁股這位,跟它比過了,他就不會去比了,因為結果都知道,還比個錘子喲!
            // 所以 屁股 這位 比的次數 比它 少一次,
            // 而 屁股后面那位,跟 它們都比過了,它就比九次 少2次
            // 以此類推
            {   
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
}

int main()
{
    int arr[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    bubble_sort(arr,sz);
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}


qsort 函式 - 庫函式 - 排序 - quick sort (快速排序)

先來了解一下, qsort 的 結構型別

void qsort(void *base,目標陣列(需要排序的陣列)
           size_t num, 陣列元素的個數
           size_t width,元素大小(以位元組為單位)
          int(*compare)(const void *e1, const void *e2)//函式指標,該函式是一個 比較函式
          
          );
          


?

void * 介紹

void *  : 萬能指標,能接受任何 型別的資料,但不能呼叫,如果要呼叫,只能強制型別轉換,才能使用

#include<stdio.h>
#include<stdlib.h>
int compare(const void* e1, const void* e2)//用來比較兩個整形值
                                           // void* 無型別指標,
                                           // void* 型別的指標 = 可以 接收 任意型別 的地址(指標),,但不能進行解參考
                                           // 俗稱 萬能指標
                                           
{
    ;
}

int main()
{
    int a = 10;
    void* p = &a;
    *p = 0;// void* 無法進行 解參考操作
    // 因為 指標的型別,決定它解參考操作時,一次可以訪問多少個位元組
    // 又因為 void* 是一個無型別指標,所以 在它 解參考操作時,無法確定一次訪問多少個位元組
    return 0;
}


進入實戰

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

int compare(const void* e1, const void* e2)
{
    return *(int*)e1 - *(int*)e2;// 如果兩個宿相等,    回傳  0
                                 // 第一個 小于 第二個  回傳  復數
}                                // 第一個 大于 第二個  回傳  正數

void test()
{
    int arr[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
    int sz = sizeof(arr) / sizeof (arr[0]);
    qsort(arr, sz, sizeof(arr[0]), compare); // qsort 排序 arr,arr有10個元素,一個元素記憶體的大小,比較兩個元素的大小,回傳
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
}





int compare_f(const void*e1, const void*e2)
{
    return (int)(*(float*)e1 - *(float*)e2);

    if (*(float*)e1 == *(float*)e2)
    {
        return 0;
    }
    else if (*(float*)e1 > *(float*)e2)
    {
        return 1;
    }
    else
    {
        return -1;
    }
}

void test2()
{
    float f[] = { 9.0, 8.0, 7.0, 6.0, 5.0, 4.0};
    int sz = sizeof(f) / sizeof(f[0]);
    qsort(f, sz, sizeof(f[0]), compare_f);
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%.1f ", f[i]);
    }
}




struct stu
{
    char name[20];
    int age;
};

int compare_by_age(const void*e1, const void*e2)
{
    return ((struct stu*) e1)->age - ((struct stu*)e2)->age;
}

void test3()
{
     struct stu s[3] = { { "zhangsan", 20 }, { "lisi", 30 }, { "wangwu", 10 } };
     int sz = sizeof(s) / sizeof(s[0]);
     qsort(s, sz, sizeof(s[0]), compare_by_age);
     int i = 0;
     for (i = 0; i < sz; i++)
     {
         printf("%d ", s[i].age);
     }
}



int compare_by_name(const void*e1, const void*e2)
{
    return strcmp( ((struct stu*) e1)->name,((struct stu*)e2)->name);// 名字比較,就是字串比較,不能用大于,等于和小于來比較
}          // 這里需要 用到 strcmp 函式
           // 回傳值跟 大小等于的回傳值是一樣的,0 ,正數,負數

void test4()
{
    struct stu s2[3] = { { "zhangsan", 20 },{ "lisi", 30 }, { "wangwu", 10 } };
    int sz = sizeof(s2) / sizeof(s2[0]);
    qsort(s2, sz, sizeof(s2[0]), compare_by_name);
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%s ", s2[i].name);
    }
    
}


int main()
{
    test();
    printf("\n");
    test2();
    printf("\n");
    test3();
    printf("\n");
    test4();
    return 0;
}

qsort(陣列名,該陣列元素個數,該陣列單個元素記憶體大小,(函式指標,比較兩個元素的 所用函式 函式 的 地址 )函式指標的 兩個引數 是:待比較的兩個元素的地址


改進冒泡排序(回呼函式)

#include<stdio.h>
//實作 bubble_sort函式 的程式員,他 是否知道 未來排序 的 資料型別  -  不知道
//程式員也不知道 待 比較 的 兩個元素 的 型別
swap(char* buf1, char*buf2, int width)
{ 我們的 叫換函式,是通過一個位元組,一個位元組的進行交換
運用了 qsort 引數中的 元素大小,進行實作的
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

int compare_int(const void*e1, const void*e2)
{
	return *(int*)e1 - *(int*)e2;
}

void bubble_sort(void*base, int sz, int width, int(*compare)(void*e1, void*e2))// 模擬 qsort 函式
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			//兩個元素比較,前者比后者大,compare 會回傳一個 大于 0 的數字,小于(回傳一個差值(負的))相等(回傳0)
			大于,回傳 一個 正數,條件成立,執行 if 陳述句,進行交換兩個元素的位置,其它情況,則不做改動,
			if (compare((char*)base + j*width, (char*)base + (j + 1)*width)>0)// 這里實在呼叫int 函式名(const void*e1, const void*e2)
			j 一開始 是 0, base 就是 第一個元素, base+(j+1),就是第二個元素
			把他們交給 compare 函式 進行比較,如果 第一個元素 比 第二個元素 大
			那就交換,否則,不交換,反上去,下回 j 就是 1,base 就第二個元素,base+(j+1)就是第三個元素
			以此類推,
注意!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
((char*)base + j*width,char*  和 width(寬度【一個元素的大小】),這里我使用了 qsort 函式引數中 元素個數 和 元素大小,這兩點來實作我們的 元素遍歷,j*width 等于 跳過幾個元素(一個元素的大小 是 width,那 乘上 j,不就是跳過 j 個元素嘛,


			{
				//交換
				由我們自制的交換函式去做
				swap((char*)base + j*width, (char*)base + (j + 1)*width, width);
			}
		}
	}

}


void test()
{
	int arr[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	// 使用 bubble_sort 的程式員 一定要知道自己排序的是什么資料
	//他就應該知道 如何比較待排序陣列中的元素
	bubble_sort(arr, sz, sizeof(arr[0]), compare_int);
}


struct stu
{
	char name[20];
	int age;
};

int compare_by_age(const void*e1, const void*e2)
{
	return ((struct stu*) e1)->age - ((struct stu*)e2)->age;// 因為 箭頭 的 優先級,強制型別轉換
}

void test2()
{
	struct stu s[3] = { { "zhangsan", 20 }, { "lisi", 30 }, { "wangwu", 10 } };
	int sz = sizeof(s) / sizeof(s[0]);
	bubble_sort(s, sz, sizeof(s[0]), compare_by_age);
}

int compare_by_name(const void*e1, const void*e2)
{
	return strcmp(((struct stu*) e1)->name, ((struct stu*)e2)->name);// 名字比較,就是字串比較,不能用大于,等于和小于來比較
}          // 這里需要 用到 strcmp 函式
//  
void test3()
{
	struct stu s[3] = { { "zhangsan", 20 }, { "lisi", 30 }, { "wangwu", 10 } };
	int sz = sizeof(s) / sizeof(s[0]);
	bubble_sort(s, sz, sizeof(s[0]), compare_by_name);
}

int main()
{
	test();
	printf("\n");
	test2();
	printf("\n");
	test3();
	return 0;
}

本文結束

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/305508.html

標籤:其他

上一篇:海明碼/漢明碼的計算和糾錯

下一篇:實作簡易通訊錄(動態增長版)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more