c语言----------内存管理

news2025/1/16 21:08:06

内存管理

目录

  • 一。作用域
    • 1.1 局部变量
    • 1.2 静态(static)局部变量
    • 1.3 全局变量
    • 1.4 静态(static)全局变量
    • 1.5 extern全局变量声明
    • 1.6 全局函数和静态函数
    • 1.7 总结
  • 二。内存布局
    • 2.1 内存分区
    • 2.2 存储类型总结
    • 2.3内存操作函数
      • 1) memset()
      • 2) memcpy()
      • 3) memmove()
      • 4) memcmp()
    • 2.4 堆区内存分配和释放
      • 1)malloc()
      • 2)free()
  • 3 内存分区代码分析
    • 1) 返回栈区地址
    • 2) 返回data区地址
    • 3) 值传递1
    • 4) 值传递2
    • 5) 返回堆区地址

一。作用域

C语言变量的作用域分为:

  • 代码块作用域(代码块是{}之间的一段代码)
  • 函数作用域
  • 文件作用域

1.1 局部变量

局部变量也叫auto自动变量(auto可写可不写),一般情况下代码块{}内部定义的变量都是自动变量,它有如下特点:

  • 在一个函数内定义,只在函数范围内有效
  • 在复合语句中定义,只在复合语句中有效
  • 随着函数调用的结束或复合语句的结束局部变量的声明声明周期也结束
  • 如果没有赋初值,内容为随机
#include <stdio.h>

void test()
{
	//auto写不写是一样的
	//auto只能出现在{}内部
	auto int b = 10; 
}

int main(void)
{
	//b = 100; //err, 在main作用域中没有b

	if (1)
	{
		//在复合语句中定义,只在复合语句中有效
		int a = 10;
		printf("a = %d\n", a);
	}

	//a = 10; //err离开if()的复合语句,a已经不存在
	
	return 0;
}

1.2 静态(static)局部变量

  • static局部变量的作用域也是在定义的函数内有效
  • static局部变量的生命周期和程序运行周期一样,同时staitc局部变量的值只初始化一次,但可以赋值多次
  • static局部变量若未赋以初值,则由系统自动赋值:数值型变量自动赋初值0,字符型变量赋空字符
#include <stdio.h>

void fun1()
{
	int i = 0;
	i++;
	printf("i = %d\n", i);
}

void fun2()
{
	//静态局部变量,没有赋值,系统赋值为0,而且只会初始化一次
	static int a;
	a++;
	printf("a = %d\n", a);
}

int main(void)
{
	fun1();
	fun1();
	fun2();
	fun2();
	
	return 0;
}

1.3 全局变量

  • 在函数外定义,可被本文件及其它文件中的函数所共用,若其它文件中的函数调用此变量,须用extern声明
  • 全局变量的生命周期和程序运行周期一样

1.4 静态(static)全局变量

  • 在函数外定义,作用范围被限制在所定义的文件中
  • 不同文件静态全局变量可以重名,但作用域不冲突
  • static全局变量的生命周期和程序运行周期一样,同时staitc全局变量的值只初始化一次

1.5 extern全局变量声明

extern int a;声明一个变量,这个全局变量在别的文件中已经定义了,这里只是声明,而不是定义。

1.6 全局函数和静态函数

在C语言中函数默认都是全局的,使用关键字static可以将函数声明为静态,函数定义为static就意味着这个函数只能在定义这个函数的文件中使用,在其他文件中不能调用,即使在其他文件中声明这个函数都没用。

对于不同文件中的staitc函数名字可以相同。

注意

  • 允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰。
  • 同一源文件中,允许全局变量和局部变量同名,在局部变量的作用域内,全局变量不起作用。
  • 所有的函数默认都是全局的,意味着所有的函数都不能重名,但如果是staitc函数,那么作用域是文件级的,所以不同的文件static函数名是可以相同的。

1.7 总结

类型	      			作用域				生命周期
auto变量				一对{}内		当前函数
static局部变量			一对{}内		整个程序运行期
extern变量				整个程序		整个程序运行期
static全局变量			当前文件		整个程序运行期
extern函数				整个程序		整个程序运行期
static函数				当前文件		整个程序运行期
register变量			一对{}内			当前函数
全局变量				整个程序		 整个程序运行期

二。内存布局

2.1 内存分区

C代码经过预处理、编译、汇编、链接4步后生成一个可执行程序。
在 Windows 下,程序是一个普通的可执行文件,以下列出一个二进制可执行文件的基本情况:
在这里插入图片描述

通过上图可以得知,在没有运行程序前,也就是说程序没有加载到内存前,可执行程序内部已经分好3段信息,分别为代码区(text)、数据区(data)和未初始化数据区(bss)3 个部分(有些人直接把data和bss合起来叫做静态区或全局区)。

代码区
存放 CPU 执行的机器指令。通常代码区是可共享的(即另外的执行程序可以调用它),使其可共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。代码区通常是只读的,使其只读的原因是防止程序意外地修改了它的指令。另外,代码区还规划了局部变量的相关信息。

全局初始化数据区/静态数据区(data段)
该区包含了在程序中明确被初始化的全局变量、已经初始化的静态变量(包括全局静态变量和局部静态变量)和常量数据(如字符串常量)。

未初始化数据区(又叫 bss 区)
存入的是全局未初始化变量和未初始化静态变量。未初始化数据区的数据在程序开始执行之前被内核初始化为 0 或者空(NULL)。

程序在加载到内存前,代码区和全局区(data和bss)的大小就是固定的,程序运行期间不能改变。然后,运行可执行程序,系统把程序加载到内存,除了根据可执行程序的信息分出代码区(text)、数据区(data)和未初始化数据区(bss)之外,还额外增加了栈区、堆区。

在这里插入图片描述

  • 栈区(stack)
    栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。

  • 堆区(heap)
    堆是一个大容器,它的容量要远远大于栈,但没有栈那样先进后出的顺序。用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。

  • 未初始化数据区(BSS)
    加载的是可执行文件BSS段,位置可以分开亦可以紧靠数据段,存储于数据段的数据(全局未初始化,静态未初始化数据)的生存周期为整个程序运行过程。

  • 全局初始化数据区/静态数据区(data segment)
    加载的是可执行文件数据段,存储于数据段(全局初始化,静态初始化数据,文字常量(只读))的数据的生存周期为整个程序运行过程。

  • 代码区(text segment)
    加载的是可执行文件代码段,所有的可执行代码都加载到代码区,这块内存是不可以在运行期间修改的。

2.2 存储类型总结

类型			作用域			生命周期			存储位置
auto变量		一对{}内	当前函数				栈区
static局部变量	一对{}内	整个程序运行期			初始化在data段,未初始化在BSS段
extern变量		整个程序	整个程序运行期			初始化在data段,未初始化在BSS段
static全局变量	当前文件	整个程序运行期			初始化在data段,未初始化在BSS段
extern函数		整个程序	整个程序运行期			代码区
static函数		当前文件	整个程序运行期			代码区
register变量	一对{}内	当前函数				运行时存储在CPU寄存器
字符串常量		当前文件	整个程序运行期			data段

代码实例

	#include <stdio.h>
	#include <stdlib.h>
	
	int e;
	static int f;
	int g = 10;
	static int h = 10;
	int main()
	{
	printf("--栈--\n");
	printf("--堆--\n");
	printf("--BSS--\n");
	printf("--data--\n");
	printf("--text--\n");
	int a;
	int b = 10;

	char* k = NULL;

	static int c;

	static int d = 10;

	char* i = "test";


	printf("&a\t %p\t //局部未初始化变量---栈\n", &a);
	printf("&b\t %p\t //局部初始化变量---栈\n", &b);

	k = (char*)malloc(10);
	printf("k\t %p\t //动态分配的内存---堆\n", k);

	printf("&e\t %p\t //全局未初始化变量---BSS\n", &e);
	printf("&c\t %p\t //静态局部未初始化变量--BSS\n", &c);
	printf("&f\t %p\t //全局静态未初始化变量---BSS\n", &f);

	printf("&g\t %p\t //全局初始化变量---data\n", &g);
	printf("&d\t %p\t //静态局部初始化变量---data\n", &d);
	printf("&h\t %p\t //全局静态初始化变量---data\n", &h);

	printf("i\t %p\t //只读数据(文字常量区)---data\n", i);

	return 0;
	}

2.3内存操作函数

1) memset()

#include <string.h>
void *memset(void *s, int c, size_t n);
功能:将s的内存区域的前n个字节以参数c填入
参数:
	s:需要操作内存s的首地址
	c:填充的字符,c虽然参数为int,但必须是unsigned char , 范围为0~255
	n:指定需要设置的大小
返回值:s的首地址
int a[10];
	printf("-----------\n");
	memset(a, 0, sizeof(a));
	for (int i = 0; i < 10; i++)
	{
		printf("%c\n", a[i]);
	}
	printf("-----------\n");
	memset(a, 97, sizeof(a));
	for (int i = 0; i < 10; i++)
	{
		printf("%c\n", a[i]);
	}

2) memcpy()

#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);
功能:拷贝src所指的内存内容的前n个字节到dest所值的内存地址上。
参数:
	dest:目的内存首地址
	src:源内存首地址,注意:dest和src所指的内存空间不可重叠,可能会导致程序报错
	n:需要拷贝的字节数
返回值:dest的首地址
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int b[10];

memcpy(b, a, sizeof(a));
int i = 0;
for (i = 0; i < 10; i++)
{
	printf("%d, ", b[i]);
}
printf("\n");

//memcpy(&a[3], a, 5 * sizeof(int)); //err, 内存重叠

3) memmove()

memmove()功能用法和memcpy()一样,区别在于:dest和src所指的内存空间重叠时,memmove()仍然能处理,不过执行效率比memcpy()低些。

4) memcmp()

#include <string.h>
int memcmp(const void *s1, const void *s2, size_t n);
功能:比较s1和s2所指向内存区域的前n个字节
参数:
	s1:内存首地址1
	s2:内存首地址2
	n:需比较的前n个字节
返回值:
	相等:=0
	大于:>0
	小于:<0
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int b[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

int flag = memcmp(a, b, sizeof(a));
printf("flag = %d\n", flag);

2.4 堆区内存分配和释放

1)malloc()

#include <stdlib.h>
void *malloc(size_t size);
功能:在内存的动态存储区(堆区)中分配一块长度为size字节的连续区域,用来存放类型说明符指定的类型。分配的内存空间内容不确定,一般使用memset初始化。
参数:
	size:需要分配内存大小(单位:字节)
返回值:
成功:分配空间的起始地址
失败:NULL
#include <stdlib.h> 
#include <stdio.h>
#include <string.h>

int main()
{
	int count, *array, n;
	printf("请输入要申请数组的个数:\n");
	scanf("%d", &n);

	array = (int *)malloc(n * sizeof (int));
	if (array == NULL)
	{
		printf("申请空间失败!\n");
		return -1;
	}
	//将申请到空间清0
	memset(array, 0, sizeof(int)*n);

	for (count = 0; count < n; count++) /*给数组赋值*/
		array[count] = count;

	for (count = 0; count < n; count++) /*打印数组元素*/
		printf("%2d", array[count]);

	free(array);

	return 0;
}

2)free()

#include <stdlib.h>
void free(void *ptr);
功能:释放ptr所指向的一块内存空间,ptr是一个任意类型的指针变量,指向被释放区域的首地址。对同一内存空间多次释放会出错。
参数:
ptr:需要释放空间的首地址,被释放区应是由malloc函数所分配的区域。
返回值:无

3 内存分区代码分析

1) 返回栈区地址

#include <stdio.h>
int *fun()
{
	int a = 10;
	return &a;//函数调用完毕,a释放
}

int main(int argc, char *argv[])
{
	int *p = NULL;
	p = fun();
	*p = 100; //操作野指针指向的内存,err

	return 0;
}

2) 返回data区地址

#include <stdio.h>

int *fun()
{
	static int a = 10;
	return &a; //函数调用完毕,a不释放
}

int main(int argc, char *argv[])
{
	int *p = NULL;
	p = fun();
	*p = 100; //ok
	printf("*p = %d\n", *p);

	return 0;
}

3) 值传递1

#include <stdio.h>
#include <stdlib.h>

void fun(int *tmp)
{
	tmp = (int *)malloc(sizeof(int));
	*tmp = 100;
}

int main(int argc, char *argv[])
{
	int *p = NULL;
	fun(p); //值传递,形参修改不会影响实参
	printf("*p = %d\n", *p);//err,操作空指针指向的内存

	return 0;
}

4) 值传递2

#include <stdio.h>
#include <stdlib.h>

void fun(int *tmp)
{
	*tmp = 100;
}

int main(int argc, char *argv[])
{
	int *p = NULL;
	p = (int *)malloc(sizeof(int));

	fun(p); //值传递
	printf("*p = %d\n", *p); //ok,*p为100

	return 0;
}

5) 返回堆区地址

#include <stdio.h>
#include <stdlib.h>

int *fun()
{
	int *tmp = NULL;
	tmp = (int *)malloc(sizeof(int));
	*tmp = 100;
	return tmp;//返回堆区地址,函数调用完毕,不释放
}

int main(int argc, char *argv[])
{
	int *p = NULL;
	p = fun();
	printf("*p = %d\n", *p);//ok

	//堆区空间,使用完毕,手动释放
	if (p != NULL)
	{
		free(p);
		p = NULL;
	}

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2277704.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习-归一化

文章目录 一. 归一化二. 归一化的常见方法1. 最小-最大归一化 (Min-Max Normalization)2. Z-Score 归一化&#xff08;标准化&#xff09;3. MaxAbs 归一化 三. 归一化的选择四. 为什么要进行归一化1. 消除量纲差异2. 提高模型训练速度3. 增强模型的稳定性4. 保证正则化项的有效…

STC的51单片机LED点灯基于KEIL

前言&#xff1a; 该文源于回答一个朋友的问题&#xff0c;代码为该朋友上传&#xff0c;略作修改&#xff0c;在此说明问题以及解决问题的思路&#xff0c;以减少新手错误。 电路图&#xff1a; 该位朋友未上传电路图&#xff0c;说明如下&#xff1a; stc8g1k08a-sop8控制…

手撕Transformer -- Day6 -- DecoderBlock

手撕Transformer – Day6 – DecoderBlock 目录 手撕Transformer -- Day6 -- DecoderBlockTransformer 网络结构图DecoderBlock 代码Part1 库函数Part2 实现一个解码器Block&#xff0c;作为一个类Part3 测试 参考 Transformer 网络结构图 Transformer 网络结构 DecoderBlock 代…

【功能测试总结】

功能测试 1. 功能测试用例1.1 设计用例容易出现的问题 2. 如何写用例2.1 什么是好的用例2.2 测试用例设计常见方法 3. 用例分级 1. 功能测试用例 1.1 设计用例容易出现的问题 基础功能点用例覆盖不全/描述不清 描述不清 什么是正常内容&#xff0c;仅看用例能否知道该输入什么…

Mac玩Steam游戏秘籍!

Mac玩Steam游戏秘籍&#xff01; 大家好&#xff01;最近有不少朋友在用MacBook玩Steam游戏时遇到不支持mac的问题。别担心&#xff0c;我来教你如何用第三方工具Crossover来畅玩这些不支持的游戏&#xff0c;简单又实用&#xff01; 第一步&#xff1a;下载Crossover 首先&…

基于Springboot + vue实现的旅游网站

&#x1f942;(❁◡❁)您的点赞&#x1f44d;➕评论&#x1f4dd;➕收藏⭐是作者创作的最大动力&#x1f91e; &#x1f496;&#x1f4d5;&#x1f389;&#x1f525; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;欢迎留言讨论 &#x1f525;&#x1f525;&…

题解 CodeForces 430B Balls Game 栈 C/C++

题目传送门&#xff1a; Problem - B - Codeforceshttps://mirror.codeforces.com/contest/430/problem/B翻译&#xff1a; Iahub正在为国际信息学奥林匹克竞赛&#xff08;IOI&#xff09;做准备。有什么比玩一个类似祖玛的游戏更好的训练方法呢&#xff1f; 一排中有n个球…

Vue3播放视频报ReferenceError: SharedArrayBuffer is not defined

解决办法 前端本地测试vue.config.js server: {headers: {"Cross-Origin-Opener-Policy": "same-origin","Cross-Origin-Embedder-Policy": "require-corp",}, }, 后端vue.js生产环境 跨域隔离 是一种现代Web安全策略&#xff0c;…

Android BottomNavigationView不加icon使text垂直居中,完美解决。

这个问题网上千篇一律的设置iconsize为0&#xff0c;labale固定什么的&#xff0c;都没有效果。我的这个基本上所有人用都会有效果。 问题解决之前的效果&#xff1a;垂直方向&#xff0c;文本不居中&#xff0c;看着很难受 问题解决之后&#xff1a;舒服多了 其实很简单&…

微调神经机器翻译模型全流程

MBART: Multilingual Denoising Pre-training for Neural Machine Translation 模型下载 mBART 是一个基于序列到序列的去噪自编码器&#xff0c;使用 BART 目标在多种语言的大规模单语语料库上进行预训练。mBART 是首批通过去噪完整文本在多种语言上预训练序列到序列模型的方…

基于32QAM的载波同步和定时同步性能仿真,包括Costas环的gardner环

目录 1.算法仿真效果 2.算法涉及理论知识概要 3.MATLAB核心程序 4.完整算法代码文件获得 1.算法仿真效果 matlab2022a仿真结果如下&#xff08;完整代码运行后无水印&#xff09;&#xff1a; 仿真操作步骤可参考程序配套的操作视频。 2.算法涉及理论知识概要 载波同步是…

设计模式-工厂模式/抽象工厂模式

工厂模式 定义 定义一个创建对象的接口&#xff0c;让子类决定实列化哪一个类&#xff0c;工厂模式使一个类的实例化延迟到其子类&#xff1b; 工厂方法模式是简单工厂模式的延伸。在工厂方法模式中&#xff0c;核心工厂类不在负责产品的创建&#xff0c;而是将具体的创建工作…

【机器学习】零售行业的智慧升级:机器学习驱动的精准营销与库存管理

我的个人主页 我的领域&#xff1a;人工智能篇&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;&#x1f44d;点赞 收藏❤ 在当今数字化浪潮汹涌澎湃的时代&#xff0c;零售行业正站在转型升级的十字路口。市场竞争的白热化使得企业必须另辟蹊径&#xff0…

day_2_排序算法和树

文章目录 排序算法和树排序算法算法稳定性排序算法☆ 冒泡排序冒泡思路冒泡步骤代码实现效率优化 ☆ 选择排序排序思路排序步骤代码实现 ... 树01-树的基本概念02-树的相关术语03-二叉树的种类04-二叉树的存储05-树的应用场景_数据库索引06-二叉树的概念和性质07-广度优先遍历0…

蓝桥杯刷题第二天——背包问题

题目描述 有N件物品和一个容量是V的背包。每件物品只能使用一次。第i件物品的体积是Vi价值是Wi。 求解将哪些物品装入背包&#xff0c;可使这些物品的总体积不超过背包容量&#xff0c;且总价值最大。 输出最大价值。 输入格式 第一行两个整数&#xff0c;N&#xff0c;V&am…

Linux x86_64 程序动态链接之GOT 和 PLT

文章目录 前言一、动态链接二、位置无关代码三、GOT 和 PLT3.1 GOT3.2 PLT3.3 延时绑定3.4 示例 四、demo演示五、延迟绑定技术和代码修补参考资料 前言 这篇文章描述了&#xff1a;Linux x86_64 程序静态链接之重定位&#xff0c;接来本文描述Linux x86_64 程序动态链接之GOT…

学习记录-责任链模式验证参数

学习记录-责任链模式验证参数 1.什么是责任链模式 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为设计模式&#xff0c;它允许将请求沿着一个处理链传递&#xff0c;直到链中的某个对象处理它。这样&#xff0c;发送者无需知道哪个对象将处理…

练习:MySQL单表查询与多表查询

一.单表查询 创建worke数据库&#xff0c;在数据库底下创建worker表 mysql> create database worke; Query OK, 1 row affected (0.00 sec)mysql> show databases; -------------------- | Database | -------------------- | information_schema | | mysql …

HarmonyOS NEXT应用开发边学边玩系列:从零实现一影视APP (四、最近上映电影滚动展示及加载更多的实现)

在HarmonyOS NEXT开发环境中&#xff0c;可以使用多种组件和库来构建丰富且交互友好的应用。本文将展示如何使用HarmonyOS NEXT框架和nutpi/axios库&#xff0c;从零开始实现一个简单的影视APP的首页&#xff0c;主要关注最近上映电影的滚动展示及加载更多功能的实现。 开源项目…

卷积神经05-GAN对抗神经网络

卷积神经05-GAN对抗神经网络 使用Python3.9CUDA11.8Pytorch实现一个CNN优化版的对抗神经网络 简单的GAN图片生成 CNN优化后的图片生成 优化模型代码对比 0-核心逻辑脉络 1&#xff09;Anacanda使用CUDAPytorch2&#xff09;使用本地MNIST进行手写图片训练3&#xff09;…