【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\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 # // 函数结束之后 局部变量出栈,内存空间释放, 程序不能再对这个空间进行任何的读写操作
} 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 内存的更多相关文章
- 故障重现(内存篇2),JAVA内存不足导致频繁回收和swap引起的性能问题
背景起因: 记起以前的另一次也是关于内存的调优分享下 有个系统平时运行非常稳定运行(没经历过大并发考验),然而在一次活动后,人数并发一上来后,系统开始卡. 我按经验开始调优,在每个关键步骤的加入如 ...
- In-Memory:在内存中创建临时表和表变量
在Disk-Base数据库中,由于临时表和表变量的数据存储在tempdb中,如果系统频繁地创建和更新临时表和表变量,大量的IO操作集中在tempdb中,tempdb很可能成为系统性能的瓶颈.在SQL ...
- In-Memory:内存优化表的事务处理
内存优化表(Memory-Optimized Table,简称MOT)使用乐观策略(optimistic approach)实现事务的并发控制,在读取MOT时,使用多行版本化(Multi-Row ve ...
- 试试SQLSERVER2014的内存优化表
试试SQLSERVER2014的内存优化表 SQL Server 2014中的内存引擎(代号为Hekaton)将OLTP提升到了新的高度. 现在,存储引擎已整合进当前的数据库管理系统,而使用先进内存技 ...
- 故障重现, JAVA进程内存不够时突然挂掉模拟
背景,服务器上的一个JAVA服务进程突然挂掉,查看产生了崩溃日志,如下: # Set larger code cache with -XX:ReservedCodeCacheSize= # This ...
- 死磕内存篇 --- JAVA进程和linux内存间的大小关系
运行个JAVA 用sleep去hold住 package org.hjb.test; public class TestOnly { public static void main(String[] ...
- 【知识必备】内存泄漏全解析,从此拒绝ANR,让OOM远离你的身边,跟内存泄漏say byebye
一.写在前面 对于C++来说,内存泄漏就是new出来的对象没有delete,俗称野指针:而对于java来说,就是new出来的Object放在Heap上无法被GC回收:而这里就把我之前的一篇内存泄漏的总 ...
- C++内存对齐总结
大家都知道,C++空类的内存大小为1字节,为了保证其对象拥有彼此独立的内存地址.非空类的大小与类中非静态成员变量和虚函数表的多少有关. 而值得注意的是,类中非静态成员变量的大小与编译器内存对齐的设置有 ...
- java: web应用中不经意的内存泄露
前面有一篇讲解如何在spring mvc web应用中一启动就执行某些逻辑,今天无意发现如果使用不当,很容易引起内存泄露,测试代码如下: 1.定义一个类App package com.cnblogs. ...
- 查看w3wp进程占用的内存及.NET内存泄露,死锁分析
一 基础知识 在分析之前,先上一张图: 从上面可以看到,这个w3wp进程占用了376M内存,启动了54个线程. 在使用windbg查看之前,看到的进程含有 *32 字样,意思是在64位机器上已32位方 ...
随机推荐
- Azkaban快速入门
先说一些废话 因为之前自己工作中有用过Azkaban作为自动化任务调度工具,所以想参考自己之前的使用经验,总结一下关于Azkaban的使用,方便大家使用Azkaban快速实现企业级自动化任务 如何选择 ...
- 基于 Easysearch kNN 搭建即时图片搜索服务
环境准备 启动 Easysearch 服务: # Make sure your vm.max_map_count meets the requirement sudo sysctl -w vm.max ...
- EF EntityFramework 强制从数据库中取数据,而不是上下文
场景:插入了一条数据到数据库,这条数据会有其它程序修改,接着程序想获取最新数据.此时不加额外处理,取的仍是旧的. t_task ta = new t_task(); ta.item_id = item ...
- (三)xpath爬取4K高清美女壁纸
功能:通过xpath爬取彼岸图网的高清美女壁纸 url = 'http://pic.netbian.com/4kmeinv/' 1. 通过url请求整张页面的数据 2.通过页面的标签定位图片所在的位置 ...
- es6.6.1 索引的增加,查询,修改,删除
1.新增 test2/user2/1/_create PUT操作{"name":"qiqi","age":17} 2.查询 test2/us ...
- python3读csv文件,出现UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd0 in position 0: invalid con
使用csv.reader(file)读csv文件时,出现如下错误:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd0 in positio ...
- vue项目中一些常用的插件
@riophae/vue-treeselect:带选择的树组件 https://vue-treeselect.js.org/ ele-calendar:日历组件 https://www.npmjs.c ...
- 记一次反向代理过滤sql注入
公司有一php系统,由于该系统是购买的,并且没人懂php,无法通过修改代码过滤sql注入问题 代码如下: public class Program { public static void Main( ...
- 记录用C#写折半查找算法实现
折半查找算法 前言 最近要考试了,重新回顾一下之前学的算法,今天是折半查找,它的平均比较次数是Log2 n 思想 给定一个有序数组A[0..n-1],和查找值K,返回K在A中的下标. 折半查找需要指定 ...
- Hbase第二课:Hbase架构与基础命令
目录 HBase架构与基础命令 一.了解HBase 1.1 HBase概述 1.2 HBase处理数据 1.3 HBase与HDFS 二.HBase相关概念 2.1 分布式数据库 2.2 列式存储 2 ...