C語言基礎(chǔ)二

一、靜態(tài)開辟內(nèi)存

#include <stdio.h>
#include <unistd.h> // 小寫的
// 函數(shù)進(jìn)棧 定義一個int arr[5];  定義一個 int i;  (靜態(tài)的范疇)
// 進(jìn)棧
void staticAction() {
    int arr[5]; // 靜態(tài)開辟 棧區(qū) (棧成員)
    for (int i = 0; i <5; ++i) {
        arr[i] = i;
        printf("%d, %p\n", *(arr + i), arr + i);
    }
} // 函數(shù)的末尾會彈棧(隱士):執(zhí)行完畢會彈棧  會釋放所有的棧成員

// 靜態(tài)開辟。棧區(qū)
int main() {
    // int arr[10 * 1024 * 1024]; // 10M * 4 = 40M  會棧溢出
    // int arr[1 * 1024 * 1024]; 會棧溢出
    int arr[(int)(0.2 * 1024 * 1024)]; //  不會棧溢出
    // 棧區(qū):占用內(nèi)存大小 最大值: 大概 2M  大于2M會棧溢出  平臺有關(guān)系的
    // 堆區(qū):占用內(nèi)存大小 最大值: 大概80%  40M沒有任何問題,基本上不用擔(dān)心 堆區(qū)很大的
                               // 大概80%: Windows系統(tǒng) 給我們的編譯器給予的空間  的 百分之百八十
    while (9) {
        sleep(100);
        staticAction(); // 調(diào)用開辟20
    }
    return (0);
}

二、動態(tài)開辟

1、malloc()

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void dynamicAction() {
    int *arr = malloc(1 * 1024 * 1024); //堆區(qū)開辟
    //沒釋放前arr的內(nèi)存地址不會變,堆區(qū)開辟的每次都會新開辟一個,釋放后,堆區(qū)的內(nèi)存會復(fù)用
    printf("arr自己的內(nèi)存地址:%p,堆區(qū)開辟的內(nèi)存地址:%p\n", &arr, arr);

    free(arr); //堆區(qū)開辟的空間,必須釋放
    //懸空指針是指向的地址被釋放掉了 沒有置為 NULL,置為NULL重新指向
    arr = NULL; //重新指向一塊內(nèi)存地址

    printf("dynamicAction函數(shù)2 堆區(qū)開辟的內(nèi)存地址:%p\n", arr);

    //開發(fā)過程中,不能出現(xiàn),野指針,懸空指針
   // int * p; //野指針 沒有地址的
    int * p = NULL;
}
// malloc 在堆區(qū)開辟的內(nèi)存空間 , (動態(tài)的范疇)
int main() {
    while (9) {
         sleep(100);
        dynamicAction();
    }
    return 0;
}
動態(tài)開辟內(nèi)存.png

2、使用場景

//動態(tài)開辟的場景
int main() {
    // 靜態(tài)開辟的內(nèi)存空間大小,是不能修改的,如果不需要動態(tài)修改空間大小,
    // 當(dāng)然使用 棧區(qū) 【盡量使用 靜態(tài)開辟的,如果實在是需要動態(tài)改變,才使用下面】

    int num;
    printf("請輸入數(shù)的個數(shù):");
    scanf("%d", &num);
    int *arr = malloc(sizeof(int) * num);
    int print_num;
    for (int i = 0; i < num; ++i) {
        printf("請輸入第%d個的值:", i);
        scanf("%d", &print_num);
        arr[i] = print_num;
        printf("當(dāng)前元素的值:%d,地址:%p", *(arr + i), arr + i);
    }
    return 0;
}

3、realloc()

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

// 動態(tài)開辟之realloc
int main() {

    int num;
    printf("請輸入個數(shù)");
    // 獲取用戶輸入的值
    scanf("%d", &num);

    // 5個值
    int * arr = (int *) malloc(sizeof(int) * num);
    for (int i = 0; i < num; ++i) {
        arr[i] = (i + 10001); // arr[i]的內(nèi)部隱式 == *(arr+i)
    }
    printf("開辟的內(nèi)存指針: %p\n", arr);

    // 打印 內(nèi)容
    for (int i = 0; i < num; ++i) {
        printf("元素的值:%d, 元素的地址:%p\n",*(arr + i),(arr + i));
    }

    // ====== 在堆區(qū)開辟新的空間  加長空間大小
    // 新增
    int new_num;
    printf("請輸入新增加的個數(shù)");
    scanf("%d", &new_num);

    // 原來的大小4 + 新增加的大小4  =  總大小 8
    // void *realloc (void *前面開辟的指針, size_t總大小);
    int * new_arr = (int *) realloc(arr, sizeof(int) * (num + new_num));

    if (new_arr) { // new_arr != NULL 我才進(jìn)if  【非0即true】
        int j = num; // 4開始
        for (; j < (num + new_num); j++) { // 5 6 7 8
            arr[j] = (j + 10001);
        }

        printf("新 開辟的內(nèi)存指針: %p\n", new_arr);

        // 后 打印 內(nèi)容
        for (int i = 0; i < (num + new_num); ++i) {
            printf("新 元素的值:%d, 元素的地址:%p\n",*(arr + i),(arr + i) );
        }
    }

    // 我已經(jīng)釋放
    free(new_arr);
    new_arr = NULL;
    
    //  重復(fù)釋放/重復(fù)free VS會奔潰,   CLion會優(yōu)化(發(fā)現(xiàn)不奔潰)   [錯誤的寫法]
    /*free(new_arr);
    new_arr = NULL;*/

    // 必須釋放【規(guī)則】
    /*if (arr) {
        free(arr);   // 如果不賦值給NULL,就是懸空指針了
        arr = NULL;
    }*/

    if (new_arr) {
        free(new_arr);   // 如果不賦值給NULL,就是懸空指針了
        new_arr = NULL;
    }
    return 0;
}

// 面試題: realloc 為什么一定要傳入 arr指針,為什么要傳總大小
//第一個參數(shù)arr指針是為了尋找前面開辟的空間的首地址,總大小是要開辟的總空間,是為了防止開辟的時候空間被占用;如圖,一般情況下,新開辟都是正常的;如果新開辟的被占用了,則會重新開辟一塊原來大小的空間,將原來的復(fù)制過來,然后在這后面接著開辟空間,相當(dāng)于是正常開辟,首地址不變,重新開辟的話首地址會發(fā)生改變。

動態(tài)realloc.png

三、字符串

1、使用

int main() {
    //第一種
//    char str[] = {'Z', 'h', 'a', 'n', 'g'}; //Zhtng0{/
    char str[] = {'Z', 'h', 'a', 'n', 'g','\0'}; //Zhtng printf打印的時候,必須以\0結(jié)尾
    str[2] = 't';
    printf("str:%s",str); 

    //第二種
    char *str2 = "Zhang"; //隱式的加了\0
    str2[2] = 't'; //不能這修改,會奔潰
    printf("str2:%s",str2);
    return 0;
}

如圖所示,第一種方式是將字符串復(fù)制到棧區(qū)進(jìn)行修改;第二種是字符串地址指向全局或者靜態(tài)區(qū),不能訪問。

字符串01.png

2、獲取長度


int getLength(char *string) {
    int count = 0;
    while (*string) { // *string != '\0'
        string++;
        count++;
    }
    return count;
}
int main() {
    char str[] = {'a', 'b', 'c', 'd'};
    int length = getLength(str);
    printf("str長度:%d", length);
    return 0;
}

3、字符串轉(zhuǎn)換、比較

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
    // 字符串轉(zhuǎn)換 
    char * num = "1"; // 字符串
    num = "12.68";

    // 【int】
    int result =  atoi(num);
    if (result) { // 非0即ture  不是0進(jìn)入if,  0就是轉(zhuǎn)換失敗了
        printf("恭喜你轉(zhuǎn)換成功:%d\n", result);
    } else {
        printf("轉(zhuǎn)換失敗!\n");
    }

    // 【double】
    double resultD =  atof(num);
    printf("恭喜你轉(zhuǎn)換成功:%lf\n", resultD);


    // 字符串的比較
    char * str1 = "Yefan";
    char * str2 = "yefan";

    // int resultC = strcmp(str1, str2); // strcmp = 區(qū)分大小寫
    int resultC = strcmpi(str1, str2); // strcmpi = 不區(qū)分大小寫
    if (!resultC) { // 0代表是相等的, 非0代表是不相等的
        printf("相等");
    } else {
        printf("不相等");
    }
    return 0;
}

4、字符串查找、包含、拼接

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
nt mainT5() {

    char * text = "name is Derry";
    char * subtext = "D";

    char * pop = strstr(text, subtext);

    // 怎么去 字符串查找
    if (pop) { // 非NULL,就進(jìn)入if,就查找到了
        printf("查找到了,pop的值是:%s\n", pop);
    } else {
        printf("沒有查找到,subtext的值是:%s\n", subtext);
    }
    // 包含了D嗎
    if (pop) {
        printf("包含了\n");
    } else {
        printf("沒有包含\n");
    }
    // printf("pop地址%p, text地址:%p,\n", pop, text);

    // 求取位置?  數(shù)組是一塊連續(xù)的內(nèi)存空間,沒有斷層,所以可以-
    int index = pop - text; // pop="Derry" - text"name is Derry"
    printf("%s第一次出現(xiàn)的位置是:%d\n", subtext, index); // 我的D在第8個位置

    // 指針是可以:++ --  +=  -=

    // 拼接 ========================
    char destination[25]; // 容器 25的大小 已經(jīng)寫死了
    char * blank = "--到--", *CPP="C++", *Java= "Java";

    strcpy(destination, CPP); // 先Copy到數(shù)組里面去
    strcat(destination, blank); // 然后再拼接
    strcat(destination, Java); // 然后再拼接
    printf("拼接后的結(jié)果:%s\n", destination); // C++--到--Java
    return 0;
}

5、大小寫轉(zhuǎn)換

#include <stdio.h>
#include <ctype.h>
// 指針的理解
void lower(char * dest, char * name) {
    char * temp = name; // 臨時指針,你只能操作,臨時指針,不能破壞name指針
    while (*temp) {
        *dest = tolower(*temp);
        temp ++; // 挪動指針位置 ++
        dest ++; // 挪動指針位置 ++  目的是為了 挪動一個存儲一個 挪動一個存儲一個 ...
    }
    // printf '\0'
    *dest = '\0'; // 避免printf打印系統(tǒng)值

    printf("不能破壞 name:%s\n", name); // temp的好處就是,不會破壞name
}
// 全部變成小寫 derry
int mainT6() {
    char * name = "DerrY";

    // 先定義結(jié)果
    char dest[20];
    lower(dest, name);
    printf("小寫轉(zhuǎn)換后的結(jié)構(gòu)是:%s\n", dest);
    return 0;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容