C语言基础二

一、静态开辟内存

#include 
#include  // 小写的
// 函数进栈 定义一个int arr[5];  定义一个 int i;  (静态的范畴)
// 进栈
void staticAction() {
    int arr[5]; // 静态开辟 栈区 (栈成员)
    for (int i = 0; i <5; ++i) {
        arr[i] = i;
        printf("%d, %p\n", *(arr + i), arr + i);
    }
} // 函数的末尾会弹栈(隐士):执行完毕会弹栈  会释放所有的栈成员

// 静态开辟。栈区
int main() {
    // int arr[10 * 1024 * 1024]; // 10M * 4 = 40M  会栈溢出
    // int arr[1 * 1024 * 1024]; 会栈溢出
    int arr[(int)(0.2 * 1024 * 1024)]; //  不会栈溢出
    // 栈区:占用内存大小 最大值: 大概 2M  大于2M会栈溢出  平台有关系的
    // 堆区:占用内存大小 最大值: 大概80%  40M没有任何问题,基本上不用担心 堆区很大的
                               // 大概80%: Windows系统 给我们的编译器给予的空间  的 百分之百八十
    while (9) {
        sleep(100);
        staticAction(); // 调用开辟20
    }
    return (0);
}

二、动态开辟

1、malloc()

#include 
#include 
#include 
void dynamicAction() {
    int *arr = malloc(1 * 1024 * 1024); //堆区开辟
    //没释放前arr的内存地址不会变,堆区开辟的每次都会新开辟一个,释放后,堆区的内存会复用
    printf("arr自己的内存地址:%p,堆区开辟的内存地址:%p\n", &arr, arr);

    free(arr); //堆区开辟的空间,必须释放
    //悬空指针是指向的地址被释放掉了 没有置为 NULL,置为NULL重新指向
    arr = NULL; //重新指向一块内存地址

    printf("dynamicAction函数2 堆区开辟的内存地址:%p\n", arr);

    //开发过程中,不能出现,野指针,悬空指针
   // int * p; //野指针 没有地址的
    int * p = NULL;
}
// malloc 在堆区开辟的内存空间 , (动态的范畴)
int main() {
    while (9) {
         sleep(100);
        dynamicAction();
    }
    return 0;
}
动态开辟内存.png

2、使用场景

//动态开辟的场景
int main() {
    // 静态开辟的内存空间大小,是不能修改的,如果不需要动态修改空间大小,
    // 当然使用 栈区 【尽量使用 静态开辟的,如果实在是需要动态改变,才使用下面】

    int num;
    printf("请输入数的个数:");
    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,地址:%p", *(arr + i), arr + i);
    }
    return 0;
}

3、realloc()

#include 
#include 

// 动态开辟之realloc
int main() {

    int num;
    printf("请输入个数");
    // 获取用户输入的值
    scanf("%d", &num);

    // 5个值
    int * arr = (int *) malloc(sizeof(int) * num);
    for (int i = 0; i < num; ++i) {
        arr[i] = (i + 10001); // arr[i]的内部隐式 == *(arr+i)
    }
    printf("开辟的内存指针: %p\n", arr);

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

    // ====== 在堆区开辟新的空间  加长空间大小
    // 新增
    int new_num;
    printf("请输入新增加的个数");
    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 我才进if  【非0即true】
        int j = num; // 4开始
        for (; j < (num + new_num); j++) { // 5 6 7 8
            arr[j] = (j + 10001);
        }

        printf("新 开辟的内存指针: %p\n", new_arr);

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

    // 我已经释放
    free(new_arr);
    new_arr = NULL;
    
    //  重复释放/重复free VS会奔溃,   CLion会优化(发现不奔溃)   [错误的写法]
    /*free(new_arr);
    new_arr = NULL;*/

    // 必须释放【规则】
    /*if (arr) {
        free(arr);   // 如果不赋值给NULL,就是悬空指针了
        arr = NULL;
    }*/

    if (new_arr) {
        free(new_arr);   // 如果不赋值给NULL,就是悬空指针了
        new_arr = NULL;
    }
    return 0;
}

// 面试题: realloc 为什么一定要传入 arr指针,为什么要传总大小
//第一个参数arr指针是为了寻找前面开辟的空间的首地址,总大小是要开辟的总空间,是为了防止开辟的时候空间被占用;如图,一般情况下,新开辟都是正常的;如果新开辟的被占用了,则会重新开辟一块原来大小的空间,将原来的复制过来,然后在这后面接着开辟空间,相当于是正常开辟,首地址不变,重新开辟的话首地址会发生改变。

动态realloc.png

三、字符串

1、使用

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

    //第二种
    char *str2 = "Zhang"; //隐式的加了\0
    str2[2] = 't'; //不能这修改,会奔溃
    printf("str2:%s",str2);
    return 0;
}

如图所示,第一种方式是将字符串复制到栈区进行修改;第二种是字符串地址指向全局或者静态区,不能访问。

字符串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、字符串转换、比较

#include 
#include 
#include 
int main() {
    // 字符串转换 
    char * num = "1"; // 字符串
    num = "12.68";

    // 【int】
    int result =  atoi(num);
    if (result) { // 非0即ture  不是0进入if,  0就是转换失败了
        printf("恭喜你转换成功:%d\n", result);
    } else {
        printf("转换失败!\n");
    }

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


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

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

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

#include 
#include 
#include 
nt mainT5() {

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

    char * pop = strstr(text, subtext);

    // 怎么去 字符串查找
    if (pop) { // 非NULL,就进入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);

    // 求取位置?  数组是一块连续的内存空间,没有断层,所以可以-
    int index = pop - text; // pop="Derry" - text"name is Derry"
    printf("%s第一次出现的位置是:%d\n", subtext, index); // 我的D在第8个位置

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

    // 拼接 ========================
    char destination[25]; // 容器 25的大小 已经写死了
    char * blank = "--到--", *CPP="C++", *Java= "Java";

    strcpy(destination, CPP); // 先Copy到数组里面去
    strcat(destination, blank); // 然后再拼接
    strcat(destination, Java); // 然后再拼接
    printf("拼接后的结果:%s\n", destination); // C++--到--Java
    return 0;
}

5、大小写转换

#include 
#include 
// 指针的理解
void lower(char * dest, char * name) {
    char * temp = name; // 临时指针,你只能操作,临时指针,不能破坏name指针
    while (*temp) {
        *dest = tolower(*temp);
        temp ++; // 挪动指针位置 ++
        dest ++; // 挪动指针位置 ++  目的是为了 挪动一个存储一个 挪动一个存储一个 ...
    }
    // printf '\0'
    *dest = '\0'; // 避免printf打印系统值

    printf("不能破坏 name:%s\n", name); // temp的好处就是,不会破坏name
}
// 全部变成小写 derry
int mainT6() {
    char * name = "DerrY";

    // 先定义结果
    char dest[20];
    lower(dest, name);
    printf("小写转换后的结构是:%s\n", dest);
    return 0;
}

你可能感兴趣的:(C语言基础二)