【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;
    }
}

 

原文地址:https://www.cnblogs.com/mindzone/p/13949151.html