zoukankan      html  css  js  c++  java
  • 【C】Re08 内存

    一、概述

    程序运行之后,所有的数据加载到内存上

    内存会被操作系统进行分区处理,

    划分的区域主要分为4个:

    【1、代码文本区 text】

    存放开发者编写的代码文本,二进制内容形式

    【2、静态全局区 StaticGlobal】 数据区 + 未初始化数据区(data + bss)

    存放各种形式的变量和常量(全局变量,静态变量,常量)

    常量会在静态全局区中单独划分一个区域存储

    【3、栈区 Stack】 

    该区域存储容量小。

    存放局部变量,函数的形参,函数返回值(大于4字节的,小于4字节存放在寄存器中)

    【4、堆区 Heap】

    该区域存储容量大。

    malloc函数申请的内存会放在堆中

    #include <stdio.h>
    #include <stdlib.h>
    
    // 全局变量 全局区
    int global_a = 100; // mem-addr g-a -> 0000000000403010
    int global_b = 200; // mem-addr g-b -> 0000000000403014
    
    // 全局常量 全局区
    const int gca = 100; // 0000000000404000
    const int gcb = 200; // 0000000000404004
    
    void fun() {
    
        // 局部变量 栈区
        int a = 100; //  mem-addr a -> 000000000061FDEC
        int b = 200; //  mem-addr b -> 000000000061FDE8
    
        // 静态局部变量 全局区
        static int sa = 100; // mem-addr sa -> 0000000000403018
        static int sb = 200; // mem-addr sb -> 000000000040301C
    
        // 局部常量 栈区
        const int ca = 100; // 000000000061FDDC
        const int cb = 200; // 000000000061FDD8
    
        // 堆区
        char * p = malloc(64); // mem-addr malloc(64) -> 00000000001D1460
    
        printf("mem-addr g-a -> %p
    ", &global_a);
        printf("mem-addr g-b -> %p
    ", &global_b);
    
        printf("mem-addr a -> %p
    ", &a); 
        printf("mem-addr b -> %p
    ", &b);
    
        printf("mem-addr sa -> %p
    ", &sa);
        printf("mem-addr sb -> %p
    ", &sb);
    
        printf("mem-addr gca -> %p
    ", &gca);
        printf("mem-addr gcb -> %p
    ", &gcb);
    
        printf("mem-addr ca -> %p
    ", &ca);
        printf("mem-addr cb -> %p
    ", &cb);
    
        printf("mem-addr malloc(64) -> %p
    ", p);
    
        // 字符串常量 全局区
        printf("mem-addr iteral -> %p
    ", &"hello c-language"); // mem-addr iteral -> 0000000000404072
    }
    
    int main() {
        fun();
    
        return 0;
    }

    二、Memset & Memcpy

    memset函数可以直接对内存的存储值进行写入操作

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void functionMemset() {
        // 用于设置内存地址的存储值
        char buffer[64] = "This is a buffer data, can be byte or character";
        printf("buffer is %s
    ", buffer);
    
        // memset(目标变量地址, 统一变更的存储值, 要替换的个数);
        memset(buffer, 'c', 20);
        printf("use memset to change buffer, now buffer is %s
    ", buffer);
    
        // 主要用途是清空内存的存储值
        memset(buffer, 0, 64);
        printf("after use memset(buffer, 0, 64) to clear mem, now buffer is %s
    ", buffer);
    }
    
    int main() {
        functionMemset();
        return 0;
    }

    memcpy 是对内存的存储值进行操作

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void printfArray(char charArr[], int size) {
        for (int i = 0; i < size; ++i) {
            printf("%d ", charArr[i]);
        }
        printf("
    ");
    }
    
    void functionMemCopy() {
        // 复制内存地址的存储值
        char origin[64] = "aaa,  jjj";
        char update[64] = {0};
    
        // 可以使用 strcpy 实现字符复制
        strcpy(update, origin); // 但是遇到会停止复制
        printfArray(update, sizeof(update) / sizeof(char));
    
        memset(update, 0, sizeof(update));
        printfArray(update, sizeof(update) / sizeof(char));
    
        memcpy(update, origin, sizeof(update)); // 使用内存复制则无视字符转移直接复制
        printfArray(update, sizeof(update) / sizeof(char));
    
        // 用途2 给数组进行赋值
        int arr[5] = {1, 2, 3, 4, 5};
        int arr2[5];
    
        memcpy(arr2, arr, sizeof(arr2));
        int size = sizeof(arr2) / sizeof(int);
    
        for (int i = 0; i < size; ++i) {
            printf("%d, ", arr2[i]);
        }
    
        printf("
    ");
    }
    
    int main() {
        functionMemCopy();
        return 0;
    }

    三、Memmove & Memcmp

    移动存储值

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void functionMemMove() {
    
        // 存储值移动的方式1,使用memcpy实现
        int arr[5] = {10, 20, 30, 40, 50};
    
        for (int i = 0; i < 5; ++i) {
            printf("%d ", arr[i]);
        }
        printf("
    ");
        memcpy(arr + 2, arr + 3,  3 * sizeof(int));
    
        for (int i = 0; i < 5; ++i) {
            printf("%d ", arr[i]);
        }
        printf("
    ");
        // -----------------------------------------------------------
        int arr2[5] = {10, 20, 30, 40, 50};
        for (int i = 0; i < 5; ++i) {
            printf("%d ", arr2[i]);
        }
        printf("
    ");
        memmove(arr2 + 2, arr2 + 3,  3 * sizeof(int));
        for (int i = 0; i < 5; ++i) {
            printf("%d ", arr2[i]);
        }
        printf("
    ");
    
        // 演示的效果一样,但是使用memcpy可能会有不一致的情况,memmove效率比cpy低,但是操作安全
    }
    
    int main() {
        functionMemMove();
        return 0;
    }

    存储值比较?

    void functionMemCompare() {
        char str1[32] = "helloword";
        char str2[32] = "hellowords";
    
        if (strcmp(str1, str2) == 0) { // strcmp(str1, str2) == 0
            printf("str1 == str2 false
    ");
        } else {
            printf("str1 == str2 true
    ");
        }
    
        if (memcpy(str1, str2, sizeof(str1)) == 0) { // strcmp(str1, str2) == 0
            printf("str1 == str2 false
    ");
        } else {
            printf("str1 == str2 true
    ");
        }
    }
    
    int main() {
        functionMemCompare();
        return 0;
    }

    四、Malloc函数

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    // C 库函数 void * malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针。
    void functionMalloc() {
        // 申请的内存区域是堆区区域,里面的内容是随机的,随机应该成上一个被释放的内存地址存储过的值
        int * intTypePointer = malloc(4);
    
        // 查看该地址存放的值
        printf("this mem-addr is %p, value is %d
    ", intTypePointer, *intTypePointer);
    
        // 一些写法的寓意
        int * arr = malloc(sizeof(int) * 10); // 表示申请的是一个数组,元素个数为10
    
        // 注意! malloc 申请的内存空间,直到程序结束之前会一直占用 如果需要释放该内存空间,则调用函数free实现
    
        // malloc有可能申请不到内存空间,因此需要判断一下指针是否存在地址值;
        if (intTypePointer == NULL) { // 为空指针的情况有内存空间申请过大
            printf("Null Pointer");
        }
    }
    void functionMalloc2() {
        int * p = malloc(sizeof(int));
    
        printf("this mem-addr is %p, value is %d
    ", p, *p);
    
        memset(p, 0, sizeof(int));
        printf("this mem-addr is %p, value is %d
    ", p, *p);
    
        *p = 233;
        printf("this mem-addr is %p, value is %d
    ", p, *p);
    
        if (p != NULL) {
            free(p);
        }
    
        printf("this mem-addr is %p, value is %d
    ", p, *p);
    
        *p = 123;
        printf("this mem-addr is %p, value is %d
    ", p, *p);
    }
    int main() {
        functionMalloc2();
        return 0;
    }

    五、内存操作的注意事项

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int * theLocalVariableMemoryAddress() {
        int num = 100;
        printf("num -> %p value -> %d
    ", &num, num);
        return &num; // 函数结束之后 局部变量出栈,内存空间释放, 程序不能再对这个空间进行任何的读写操作
    }
    
    void attentionForMemOperate() {
        // 不要返回局部变量的地址
        int * illegalPointer = theLocalVariableMemoryAddress();
    
        // 但是事实上C语言依然可以同这个非法指针进行控制
    
        // 打印操作:
        printf("illegalPointer -> %p value -> %d
    ", illegalPointer, *illegalPointer);
        printf("illegalPointer -> %p value -> %d
    ", illegalPointer, *illegalPointer);
        printf("illegalPointer -> %p value -> %d
    ", illegalPointer, *illegalPointer);
        printf("illegalPointer -> %p value -> %d
    ", illegalPointer, *illegalPointer);
    }
    
    int main() {
        attentionForMemOperate();
        return 0;
    }

     五、指针形参实参问题

    // 同级指针修饰内存失败
    void allocateSpace(int * doublePointer) {
    
        doublePointer = malloc(sizeof(int));
    
        *doublePointer = 1000;
    
        printf("*doublePointer = %d
    ", *doublePointer);
    }
    
    void allocateSpace2(int ** doublePointer) {
    
        *doublePointer = malloc(sizeof(int));
    
        **doublePointer = 1000;
    
        printf("*doublePointer = %d
    ", **doublePointer);
    }
    
    int main() {
        int * pointer = NULL;
        
        allocateSpace2(&pointer);
        printf("*pointer = %d
    ", *pointer);
    
        allocateSpace(pointer);
        printf("*pointer = %d
    ", *pointer); // 还是形参和实参的问题,实参出栈之后没有变化
        return 0;
    }

    六、安全的销毁指针

    void freePointer(int ** pointer) {
        if (*pointer != NULL) {
            free(pointer); // 释放之后 还需要把指针赋值为空,调用者不可以再访问指针了
            *pointer = NULL;
        }
    }

     

  • 相关阅读:
    10.flask博客项目实战五之用户登录功能
    09.flask博客项目实战四之数据库
    08.flask博客项目实战三之表单
    07.flask博客项目实战二之模板使用
    06.flask博客项目实战一之项目框架搭建
    05.flask数据库
    04.flask表单
    03.flask模板
    idea 灵异事件之maven 缓存
    如何查看Spring Boot 默认的数据库连接池类型
  • 原文地址:https://www.cnblogs.com/mindzone/p/13949151.html
Copyright © 2011-2022 走看看