一、概述

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

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

划分的区域主要分为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\n", &global_a);
printf("mem-addr g-b -> %p\n", &global_b); printf("mem-addr a -> %p\n", &a);
printf("mem-addr b -> %p\n", &b); printf("mem-addr sa -> %p\n", &sa);
printf("mem-addr sb -> %p\n", &sb); printf("mem-addr gca -> %p\n", &gca);
printf("mem-addr gcb -> %p\n", &gcb); printf("mem-addr ca -> %p\n", &ca);
printf("mem-addr cb -> %p\n", &cb); printf("mem-addr malloc(64) -> %p\n", p); // 字符串常量 全局区
printf("mem-addr iteral -> %p\n", &"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\n", buffer); // memset(目标变量地址, 统一变更的存储值, 要替换的个数);
memset(buffer, 'c', 20);
printf("use memset to change buffer, now buffer is %s\n", buffer); // 主要用途是清空内存的存储值
memset(buffer, 0, 64);
printf("after use memset(buffer, 0, 64) to clear mem, now buffer is %s\n", 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("\n");
} void functionMemCopy() {
// 复制内存地址的存储值
char origin[64] = "aaa, \0 jjj";
char update[64] = {0}; // 可以使用 strcpy 实现字符复制
strcpy(update, origin); // 但是遇到\0会停止复制
printfArray(update, sizeof(update) / sizeof(char)); memset(update, 0, sizeof(update));
printfArray(update, sizeof(update) / sizeof(char)); memcpy(update, origin, sizeof(update)); // 使用内存复制则无视\0字符转移直接复制
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("\n");
} 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("\n");
memcpy(arr + 2, arr + 3, 3 * sizeof(int)); for (int i = 0; i < 5; ++i) {
printf("%d ", arr[i]);
}
printf("\n");
// -----------------------------------------------------------
int arr2[5] = {10, 20, 30, 40, 50};
for (int i = 0; i < 5; ++i) {
printf("%d ", arr2[i]);
}
printf("\n");
memmove(arr2 + 2, arr2 + 3, 3 * sizeof(int));
for (int i = 0; i < 5; ++i) {
printf("%d ", arr2[i]);
}
printf("\n"); // 演示的效果一样,但是使用memcpy可能会有不一致的情况,memmove效率比cpy低,但是操作安全
} int main() {
functionMemMove();
return 0;
}

存储值比较?

void functionMemCompare() {
char str1[32] = "hello\0word";
char str2[32] = "hello\0words"; if (strcmp(str1, str2) == 0) { // strcmp(str1, str2) == 0
printf("str1 == str2 false\n");
} else {
printf("str1 == str2 true\n");
} if (memcpy(str1, str2, sizeof(str1)) == 0) { // strcmp(str1, str2) == 0
printf("str1 == str2 false\n");
} else {
printf("str1 == str2 true\n");
}
} 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\n", 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\n", p, *p); memset(p, 0, sizeof(int));
printf("this mem-addr is %p, value is %d\n", p, *p); *p = 233;
printf("this mem-addr is %p, value is %d\n", p, *p); if (p != NULL) {
free(p);
} printf("this mem-addr is %p, value is %d\n", p, *p); *p = 123;
printf("this mem-addr is %p, value is %d\n", 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\n", &num, num);
return &num; // 函数结束之后 局部变量出栈,内存空间释放, 程序不能再对这个空间进行任何的读写操作
} void attentionForMemOperate() {
// 不要返回局部变量的地址
int * illegalPointer = theLocalVariableMemoryAddress(); // 但是事实上C语言依然可以同这个非法指针进行控制 // 打印操作:
printf("illegalPointer -> %p value -> %d\n", illegalPointer, *illegalPointer);
printf("illegalPointer -> %p value -> %d\n", illegalPointer, *illegalPointer);
printf("illegalPointer -> %p value -> %d\n", illegalPointer, *illegalPointer);
printf("illegalPointer -> %p value -> %d\n", illegalPointer, *illegalPointer);
} int main() {
attentionForMemOperate();
return 0;
}

五、指针形参实参问题

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

六、安全的销毁指针

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

【C】Re08 内存的更多相关文章

  1. 故障重现(内存篇2),JAVA内存不足导致频繁回收和swap引起的性能问题

    背景起因: 记起以前的另一次也是关于内存的调优分享下   有个系统平时运行非常稳定运行(没经历过大并发考验),然而在一次活动后,人数并发一上来后,系统开始卡. 我按经验开始调优,在每个关键步骤的加入如 ...

  2. In-Memory:在内存中创建临时表和表变量

    在Disk-Base数据库中,由于临时表和表变量的数据存储在tempdb中,如果系统频繁地创建和更新临时表和表变量,大量的IO操作集中在tempdb中,tempdb很可能成为系统性能的瓶颈.在SQL ...

  3. In-Memory:内存优化表的事务处理

    内存优化表(Memory-Optimized Table,简称MOT)使用乐观策略(optimistic approach)实现事务的并发控制,在读取MOT时,使用多行版本化(Multi-Row ve ...

  4. 试试SQLSERVER2014的内存优化表

    试试SQLSERVER2014的内存优化表 SQL Server 2014中的内存引擎(代号为Hekaton)将OLTP提升到了新的高度. 现在,存储引擎已整合进当前的数据库管理系统,而使用先进内存技 ...

  5. 故障重现, JAVA进程内存不够时突然挂掉模拟

    背景,服务器上的一个JAVA服务进程突然挂掉,查看产生了崩溃日志,如下: # Set larger code cache with -XX:ReservedCodeCacheSize= # This ...

  6. 死磕内存篇 --- JAVA进程和linux内存间的大小关系

    运行个JAVA 用sleep去hold住 package org.hjb.test; public class TestOnly { public static void main(String[] ...

  7. 【知识必备】内存泄漏全解析,从此拒绝ANR,让OOM远离你的身边,跟内存泄漏say byebye

    一.写在前面 对于C++来说,内存泄漏就是new出来的对象没有delete,俗称野指针:而对于java来说,就是new出来的Object放在Heap上无法被GC回收:而这里就把我之前的一篇内存泄漏的总 ...

  8. C++内存对齐总结

    大家都知道,C++空类的内存大小为1字节,为了保证其对象拥有彼此独立的内存地址.非空类的大小与类中非静态成员变量和虚函数表的多少有关. 而值得注意的是,类中非静态成员变量的大小与编译器内存对齐的设置有 ...

  9. java: web应用中不经意的内存泄露

    前面有一篇讲解如何在spring mvc web应用中一启动就执行某些逻辑,今天无意发现如果使用不当,很容易引起内存泄露,测试代码如下: 1.定义一个类App package com.cnblogs. ...

  10. 查看w3wp进程占用的内存及.NET内存泄露,死锁分析

    一 基础知识 在分析之前,先上一张图: 从上面可以看到,这个w3wp进程占用了376M内存,启动了54个线程. 在使用windbg查看之前,看到的进程含有 *32 字样,意思是在64位机器上已32位方 ...

随机推荐

  1. php分组查询和聚合函数 数据显示的顺序

      // 分组查询和聚合函数         // 分组查询 : 将指定字段中的数据,按照不同的具体数值,进行分组         //           数据相同的分在一个分组中          ...

  2. realtek高清晰音频管理器 WIN10

    在WIN10里已经改名了: Realtek Audio Console . 在安装realtek声卡驱动后,Realtek Audio Console 会自动安装.

  3. 限速上传文件到腾讯对象存储cos的脚本

    官网:https://cloud.tencent.com/document/product/436/12269 安装包,这里用的python2.7 # pip install -U cos-pytho ...

  4. Docker入门系列之四:Docker镜像

    在本文中,您将学习如何加快Docker构建周期并创建轻量级镜像.遵循之前的文章中的食物隐喻,我们将沙拉隐喻为Docker镜像,同时减少Docker镜像的数量. 在本系列的第3部分中,我们介绍了十几个D ...

  5. Aspect切面进行统一参数处理demo

    Aspect切面进行统一参数处理demo //导入 implementation('org.springframework:spring-aspects:5.3.22') import com.exa ...

  6. springboot3使用validation进行参数验证

    前言   今天学习了使用validation整合springboot进行字段的校验,体验下来感觉很不错,有了validation可以省下一大堆控制器里面的数据校验,例如前端发送了一个请求到我们后端,请 ...

  7. python的requirements.txt_维护项目依赖包

    pycharm没有类似maven用于管理依赖包的工具,当一个项目在新的环境运行前,需要将对应依赖的包下载回来,如果一个个下载,会出现缺漏或版本号不对应的情况,这个时候可以用requirements.t ...

  8. 化合物同位素理论同位素分布计算软件Isopro 3.0

    大家好,今天分享一款软件,即可以计算化合物理论同位素分布的软件Isopro 3.0.在做质谱的实验时,特别对合成的化合物进行质量表征时,往往要求ppm绝对值在5以内,对质谱的分辨率要求很高.对于小分子 ...

  9. DownUnderCTF 2024 - Forensics

    DownUnderCTF 2024 - Forensics Baby's First Forensics 他们整个上午都在试图破坏我们的基础设施!他们正试图获得更多关于我们秘密袋鼠的信息!我们需要您的 ...

  10. Django生成数据库表时报错 __init__() missing 1 required positional argument: 'on_delete'

    原因: 在django2.0后,定义外键和一对一关系的时候需要加上on_delete选项,此参数为了避免两个表里的数据不一致问题,不然会报错 例如: owner=models.ForeignKey(U ...