天天看點

12.C語言提高(二)

1.二維數組的本質

二維數組的本質是一個數組指針,放寬來說多元數組的本質也是一個數組指針

int arr[i][j]

(arr+i)代表第i行的位址 二級指針

*(arr+i)代表第i行首元素的位址 一級指針 ,第i行位址和第i行首元素位址雖然是相同的

但是指針的表示形式不同

*(arr+i)+j == arr[i][j] 代表第i行第j列元素的位址

((arr+i)+j) 代表第i行第j列元素的值

12.C語言提高(二)

二維數組與指針.png

#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void main() {
    int arr[3][5];
    int i = 0;
    int j = 0;
    int temp = 0;
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 5; j++) {
            arr[i][j] = temp++;
        }
    }
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 5; j++) {
            printf("%d ", arr[i][j]);
        } 
    }
    printf("\n");
    //觀察這兩行列印,看位址有什麼特點,arr+1相對于arr移動了
    //20,這剛好等于二維數組一行的位元組數(5*4(一個int4個位元組))
    printf("arr %d,arr+1 %d\n",arr,arr+1);
    //&arr+1相對于&arr移動了60個位元組,剛好等于一個二維數組
    //的總位元組數(5*4*3)
    printf("&arr %d,&arr+1 %d\n",&arr,&arr+1);

    //定義一個指向數組的指針變量,指向以int[5]為元素的二維數組
    int(*p)[5];

    //指向arr
    p = arr;

    //列印,你會發現p[i][j]和arr[i][j]列印結果是一樣的,其實二維數組的本質
    //就是一個數組指針,arr和p是等價的
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 5; j++) {
            printf("%d ", p[i][j]);
        }
    }

    //(arr+i)代表第i行的位址   二級指針
    //*(arr+i)代表第i行首元素的位址  一級指針  ,第i行位址和第i行首元素位址雖然是相同的
    //但是指針的表示形式不同
    //*(arr+i)+j   == arr[i][j]  代表第i行第j列元素的位址
    //*(*(arr+i)+j) 代表第i行第j列元素的值
    system("pause");
}
           
2.數組做函數參數的退化問題

1.C語言中隻會以機械式的值拷貝方式傳遞參數(實參把值傳遞給形參)。

下邊兩個方法參數不同,但是列印結果是相同的,sizeof(數組)得到的值都是4,為什麼?一個char 一個int,這是因為一維數組做函數參數會退化為一個一維指針,是以結果是一樣的,解釋一下C語言中隻會以機械式的值拷貝方式傳遞參數這句話,當一個數組作為位址傳遞到一個方法中,實參向形參傳遞值的時候,并沒有在記憶體中重新建立一個數組,将數組元素一個一個拷貝進去,而是将實參數組的位址傳遞給了形參,形參也指向了那個數組,這就是值傳遞,位址值的傳遞

原因:c語言的高效性展現在這裡,這樣做更高效

int fun(char a[20],size_t b){
    printf("%d  %d",b,sizeof(a));
}

int fun(int a[20],size_t b){
    printf("%d  %d",b,sizeof(a));
}
           

2.二維數組做參數

//一維數組做參數退化過程
void fun(int a[5])   ---> void fun(inta[]) ---> void fun(int *a)
//二維數組做參數退化過程
void fun(int a[3][5]) ---> void fun(int a[][5]) --->void fun(int (*a)[5])
           
數組做函數參數的等效性

一維數組 char a[30] 等價的指針參數 char*a

指針數組 char *a[30] 等價的指針參數 char *a

二維數組 char a[20][30] 等價的指針參數 char(a)[30]

4.三種判斷字元串到達結尾的方式

'\0' 數字0 NULL都可以作為判斷字元串結尾的方式,原因在于stdlib.h中有這樣的定義

12.C語言提高(二)

NULL定義.png

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


void main() {
    char *a[5] = {
        "helloe",
        "world",
        "\0"
    };

    char *b[5] = {
        "today",
        "yesterday",
        0
    };

    char *c[5] = {
        "good",
        "bad",
        NULL
    };

    for (int i = 0; a[i] != NULL;i++) {
        printf("%s", a[i]);
    }

    printf("\n");
    for (int i = 0; b[i] != NULL; i++) {
        printf("%s", b[i]);
    }
    printf("\n");
    for (int i = 0; c[i] != NULL; i++) {
        printf("%s", c[i]);
    }
    system("pause");
}

           
5.結構體做形參和結構體指針做形參

看下邊的函數,為什麼通過結構體指派失敗,而通過結構體指針就成功了,我來畫一下記憶體結構示意圖,就明白了

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

typedef struct Worker {
    char *name;
    int age;
    int id;
} Worker;


void copy(Worker from,Worker to){
    to = from;
}

void copy2(Worker *from, Worker *to) {
    *to = *from;
}
void main() {
    Worker w1 = {"zhangsan",12,1};

    Worker w2;
    Worker w3;
    //通過等号,會将結構體中的值拷貝到w2中
    w2 = w1;
    printf("w2.name=%s", w2.name);

    //通過函數進行指派
    //copy(w1, w3);
    //運作直接報錯。使用了未初始化的局部變量w3,但是在copy函數中
    //我們已經指派了
    //printf("w3.name=%s", w3.name);

    //換一種方式,通過傳遞結構體指針,列印成功
    copy2(&w1,&w3);
    system("pause");
}

           

針對第一個copy函數

結構體通過等号指派,做的是記憶體中值的拷貝,不是位址,這一點要和數組做形參差別開。因為是值拷貝,是以w2指派給to之後,二者在除了值相同,是沒有聯系的,to的改變不會影響到w2,是以列印w2失敗

12.C語言提高(二)

結構體變量做形參.png

針對第二個copy2函數

12.C語言提高(二)

結構體指針做形參.png

6.結構體做參數
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

typedef struct Worker {
    char *name;
    int age;
    int id;
} Worker;

void printWorker(Worker *worker, int num) {
    for (int i = 0; i < num; i++) {
        //printf("age:%d\n", (*(worker+i)).age);
        //printf("age:%d\n", (worker + i)->age);
        printf("age:%d\n", worker[i].age);
    }
}

void sortWorker(Worker *worker, int num) {
    int i, j;
    Worker tmp;
    for (i = 0; i < num; i++) {
        for (j = i + 1; j < num; j++) {
            /*if ((worker + i)->age > (worker + j)->age) {
                tmp = *(worker + i);
                *(worker + i) = *(worker + j);
                *(worker + j) = tmp;
            }*/
            if (worker[i].age >worker[j].age) {
                tmp = worker[i];
                worker[i] = worker[j];
                worker[j] = tmp;
            }
        }
    }
}

void main() {
    int i = 0, num = 3;
    Worker Array[3];
    for (i = 0; i < num; i++) {
        printf("請輸入age:");
        scanf("%d", &(Array[i].age));
    }
    printf("\n排序前\n:");
    printWorker(Array, num);
    sortWorker(Array, num);
    printf("\n排序後\n:");
    printWorker(Array, num);
    system("pause");
}


           

7.結構體二級指針做參數

注意三個createWorker方法

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

typedef struct Worker {
    char *name;
    int age;
    int id;
} Worker;

Worker *createWorker1(int num) {
    Worker *tmp = (Worker*)malloc(sizeof(Worker)*num);
    if (tmp == NULL) {
        return NULL;
    }
    return tmp;
}

int createWorker2(Worker **worker, int num) {
    Worker *tmp = NULL;
    tmp = (Worker*)malloc(sizeof(Worker)*num);
    if (tmp == NULL) {
        return -1;
    }
    //這樣做的效果是讓二級指針Worker **worker指向了tmp,并沒有改變
    //Worker *pWorker這個指針的指向,可以分析一下,但函數執行時,定義了一個
    //二級指針worker指向了Worker *pWorker,worker = &tmp這樣操作改變的隻是
    //臨時變量worker,對pWorker沒有影響,是以後邊操作pWorker導緻崩潰
    worker = &tmp;
    return 0;
}

int createWorker3(Worker **worker, int num) {
    Worker *tmp = NULL;
    tmp = (Worker*)malloc(sizeof(Worker)*num);
    if (tmp == NULL) {
        return -1;
    }
    *worker = tmp;
    return 0;
}

void printWorker(Worker *worker, int num) {
    for (int i = 0; i < num; i++) {
        //printf("age:%d\n", (*(worker+i)).age);
        //printf("age:%d\n", (worker + i)->age);
        printf("age:%d\n", worker[i].age);
    }
}

void sortWorker(Worker *worker, int num) {
    int i, j;
    Worker tmp;
    for (i = 0; i < num; i++) {
        for (j = i + 1; j < num; j++) {
            /*if ((worker + i)->age > (worker + j)->age) {
            tmp = *(worker + i);
            *(worker + i) = *(worker + j);
            *(worker + j) = tmp;
            }*/
            if (worker[i].age >worker[j].age) {
                tmp = worker[i];
                worker[i] = worker[j];
                worker[j] = tmp;
            }
        }
    }
}
void main() {
    int i = 0, num = 3;
    Worker *pWorker = NULL;
    //在堆記憶體中申請空間傳回記憶體位址
    //pWorker = createWorker1(num);
    //createWorker2(&pWorker, num);
    createWorker3(&pWorker, num);
    for (i = 0; i < num; i++) {
        printf("請輸入age:");
        scanf("%d", &(pWorker[i].age));
    }
    printf("\n排序前\n:");
    printWorker(pWorker, num);
    sortWorker(pWorker, num);
    printf("\n排序後\n:");
    printWorker(pWorker, num);
    system("pause");
}


           
8.練習,将前兩個記憶體中的元素拷貝到一個新的二級指針指向的空間,并排序
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/*
把第一種記憶體模型第二種記憶體模型結果copy到第三種記憶體模型中,并排序列印
*/
int sort(char **myp1, int num1, char(*myp2)[30], int num2, char ***myp3, int *num3) {
    char **p3 = NULL;
    int i = 0, j = 0, k = 0;
    int tmplen = 0;
    char *tmpP = NULL;
    //p3指向的記憶體空間用于接收指向兩個數組的值,是以他的大小應該是前兩個空間的和
    p3 = (char **)malloc((num1 + num2) * sizeof(char*));
    if (p3 == NULL) {
        return -1;
    }

    //将第一個數組的值拷貝到申請的空間中
    for (i = 0; i < num1; i++) {
        //得到myp1指向的第i個元素的長度(+1是結束符空間)
        tmplen = strlen(myp1[i]) + 1;
        //開辟等長的空間
        p3[i] = (char *)malloc(tmplen * sizeof(char));
        //拷貝到開辟的空間中
        strcpy(p3[i], myp1[i]);
    }
    printf("i = %d\n", i);
    //同樣道理拷貝myp2
    for (j = 0; j < num2; j++,i++){
        tmplen = strlen(myp2[j]) + 1;
        p3[i] = (char *)malloc(tmplen * sizeof(char));
        if (p3[i] == NULL) {
            return -3;
        }
        strcpy(p3[i], myp2[j]);
    }

    //排序
    tmplen = num1 + num2;
    //*num3 = num1+num2;
    for (i = 0; i < tmplen; i++) {
        for (j = i + 1; j < tmplen; j++) {
            if (strcmp(p3[i], p3[j]) > 0) {
                tmpP = p3[i];
                p3[i] = p3[j];
                p3[j] = tmpP;
            }
        }
    }

    //間接指派
    *num3 = tmplen;
    *myp3 = p3;
    return 0;
}

void sortFree1(char **p, int len) {
    int i = 0;
    if (p == NULL) {
        return;
    }
    for (i = 0; i < len; i++) {
        free(p[i]);
    }
    free(p);
}

/*
    把二級指針指向的二維記憶體釋放掉,同時間接修改了實參的值
*/
void sortFree2(char ***myp,int len) {
    int i = 0;
    char **p = NULL;
    if (myp == NULL) {
        return;
    }
    //還原成二級指針
    p = *myp;
    if (p == NULL) {
        return;
    }
    for (i = 0; i < len; i++) {
        free(p[i]);
    }
    //間接指派是指針存在的最大意義
    *myp = NULL;
}
void main() {
    int ret = 0;
    char *p1[] = {"aaaaaaa","cccccc","bbbbbb"};
    char buf2[10][30] = {"11111","333333","222222"};
    char **p3 = NULL;

    int len1, len2, len3=0, i = 0;
    len2 = 3;
    len1 = sizeof(p1) / sizeof(*p1);
    printf("len1=%d", len1);
    ret = sort(p1, len1, buf2, len2, &p3, &len3);
    if (ret != 0) {
        printf("func sort err:%d \n",ret);
        return ret;
    }
    for (i = 0; i < len3; i++) {
        printf("%s\n", p3[i]);
    }

    system("pause");
}


           

繼續閱讀