【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位方 ...
随机推荐
- Java中try catch finally 关键字
异常处理中的几个常用关键字(try catch finally throw throws) 异常处理 java中提供一套异常处理机制,在程序发生异常时,可以执行预先设定好的处理程序, 执行完成后,程序 ...
- 算法金 | 再见!!!KNN
大侠幸会,在下全网同名「算法金」 0 基础转 AI 上岸,多个算法赛 Top 「日更万日,让更多人享受智能乐趣」 KNN算法的工作原理简单直观,易于理解和实现,这使得它在各种应用场景中备受青睐. 我们 ...
- 通过 Canal 将 MySQL 数据实时同步到 Easysearch
Canal 是阿里巴巴集团提供的一个开源产品,能够通过解析数据库的增量日志,提供增量数据的订阅和消费功能.使用 Canal 模拟成 MySQL 的 Slave,实时接收 MySQL 的增量数据 bin ...
- 使用 eBPF 在云中实现网络可观测性
可观测性是一种了解和解释应用当前状态的能力,也是一种知道何时出现问题的方法.随着在 Kubernetes 和 OpenShift 上以微服务形式进行云部署的应用程序越来越多,可观察性受到了广泛关注.许 ...
- 微信小程序自动化_从环境搭建到自动化代码实现过程
前期准备 微信小程序作为现在流行的一种应用载体,很多小伙伴都有对其做自动化测试的需求,由于腾讯系 QQ.微信等是基于腾讯自研 X5 内核,不是谷歌原生 webview,所以调试会有些许差异(现在很多 ...
- Coap 协议学习:1-有关概念
COAP协议简介 不像人接入互联网的简单方便,由于物联网设备大多都是资源限制型的,有限的CPU.RAM.Flash.网络宽带等.对于这类设备来说,想要直接使用现有网络的TCP和HTTP来实现设备实现信 ...
- vulhub - INFOSEC PREP: OSCP
vulhub - INFOSEC PREP: OSCP 信息收集 nmap 192.168.157.0/24 nmap -sT --min-rate 10000 -p- 192.168.157.162 ...
- 树莓派4B-MAX9814麦克风模块
树莓派4B-MAX9814麦克风模块 硬件需求 树莓派 MAX9814模块 杜邦线 MAX9814模块 电子特性 实验电路板 实验电路局部 典型工作特性 引角接线 代码展示 import RPi.GP ...
- MySQL与Redis数据双写一致性工程落地案例
复习-面试题 多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个 互斥锁来锁住它. 其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存. 后面的线程 ...
- Spring Cloud提供者actuator依赖
<!-- actuator依赖 --> <dependency> <groupId>org.springframework.boot</groupId> ...