一、概述

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

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

划分的区域主要分为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. ABC346

    D 枚举是哪一位相同,情况为 \(00\) 还是 \(11\),然后用前缀和和后缀和求一下即可. \(pre_{j,i}\) 表示第一位为 \(j\),前 \(i\) 位的每两个相同的字符均不相同的情 ...

  2. js获取指定日期的前一天/后一天

    date代表指定日期,格式:2018-09-27 day代表天数,-1代表前一天,1代表后一天 // date 代表指定的日期,格式:2018-09-27// day 传-1表始前一天,传1表始后一天 ...

  3. # android studio启动虚拟机长时间无响应,无法启动

    问题 虚拟设备长时间不响应,无法启动设备 方案 根据 android studio启动虚拟器失败 尝试删除锁文件 失败,.android 目录下不存在锁文件 电脑内存或计算配置不足 查看了模拟器需要的 ...

  4. Postman 的 Basic Auth 如何通过 Feign 实现

    Postman 的 Basic Auth: 分析 根据以上图片分析: Postman 的 Authorization 实际为: header 中添加 Authorization: ******* ** ...

  5. 技嘉BIOS超频设置操作路径

    关闭超线程 频率电压控制 > 进阶处理器设置 > Hyper_THreading 关小核心 频率电压控制 > GIGABYTE PerfDrive > Ecore Disabl ...

  6. 京东云上centos8.2 安装 consul1.11.1

    做个笔记下 -- 前言 部分内容有参考网友的,但是地址不记得了! 安装内容基本参考官网的和上一个网友的 官网地址: https://www.consul.io/downloads 以下是使用root方 ...

  7. 用基础Array数组实现动态数组、链表、栈和队列

    代码地址: https://gitee.com/Tom-shushu/Algorithm-and-Data-Structure.git 一.ArrayList自定义封装 package com.zho ...

  8. Xilinx ZYNQ-7000 平台简介

    平台介绍 Zynq7000是赛灵思公司(Xilinx)推出的行业第一个可扩展处理平台Zynq系列.旨在为视频监视.汽车驾驶员辅助以及工厂自动化等高端嵌入式应用提供所需的处理与计算性能水平. 在2010 ...

  9. scarpy基础

    1. 创建项目 scrapy startproject 项目名称 2. 进入项目 cd 项目名称 3. 创建爬虫 scrapy genspider 名字 域名 4. 可能需要start_urls,修改 ...

  10. 基于RK3588的8K视频解码显示案例分享!引领超高清工业视频时代

    8K.4K.2K显示对比 2K分辨率:也称为全高清(FULL HD),它具有1920 x 1080像素的分辨率.这是目前大多数消费者电视和电脑显示器的标准分辨率,可以提供良好的图像质量. 4K分辨率: ...