【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位方 ...
随机推荐
- css摩天轮
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8 ...
- ETL工具-nifi干货系列 第十七讲 nifi Input Port&Out Port 实战教程
1.端口(Port),包含输入端口(Input Port)和输出端口(Out Port ) 使用一个或多个处理组构建的数据流需要一种方式将处理组连接到其他数据流组件. 处理组和处理组之间可以通过使用端 ...
- NOIP 2023 三值逻辑
problem 我们定义 \(\text{T}\) 对应 \(n + 1\),\(\text{U}\) 对应 \(n + 2\),\(\text{F}\) 就是 \(-\text{T}\). 现在我们 ...
- 荣耀无5G开关,荣耀手机,荣耀80GT
荣耀无5G开关,荣耀手机,荣耀80GT. Magic OS 版本号是:7.0.0.138(C00E135R2P6). 解决方法: 1.进入设置-关于手机-连续点击7次版本号. 会提示,开发者选项已开启 ...
- Ansible-playbook剧本进阶
剧本高级特性篇 循环 在写 playbook 的时候发现了很多 task 都要重复引用某个相同的模块,比如一次启动10个服务,或者一次拷贝10个文件,如果按照传统的写法最少要写10次,这样会显得 pl ...
- Java中PDF的转换(图片)与展示
解决的问题 有些时候我们需要在项目中展示PDF,但是直接在浏览器中加入PDF展示的插件,存在兼容性问题,某些浏览器显示效果不理想,所以我们可以将PDF转为图片,然后已图片的方式展示,效果很好. 那么怎 ...
- monaco-editor 的 Language Services
我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品.我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值. 本文作者:修能 这是一段平平无奇的 SQL 语法 SELECT id ...
- mysql加解密,substring substring_index函数
mysql加解密,substring substring_index函数 SELECT to_base64(AES_ENCRYPT('测试串','key12345678')) ;SELECT AES_ ...
- 【IEEE 出版】 第三届能源与电力系统国际学术会议 (ICEEPS 2024)
[连续2届会后4-5个月EI检索,检索稳定!特邀院士.Fellow 报告!]第三届能源与电力系统国际学术会议 (ICEEPS 2024)以"创造更加柔性.智能的能源电力系统"为主题 ...
- Atcoder Beginner Contest 321 G - Electric Circuit 题解 - 状压dp | 指定最低位
为了更好的阅读体验,请点击这里 题目链接:G - Electric Circuit 看到了 \(N\) 的数据范围,因此是显然的状压 dp. 不妨设 \(f_S\) 为仅使用 \(S\) 集合中的所有 ...