c内存分布和存储类型

c内存分布和存储类型

1.C语言中的内存管理

​ C语言程序在编译后需要载入内存中才能开始运行。内存中对于数据的划分不是随机的,而是根据这个数据的性质分段进行划分的。某段内存区域只会存储相应的数据。
具体来说,C语言对于内存空间的划分可以分为以下几个区域:

1)代码区:这段区域主要用来存储编译后的函数体的二进制代码,以及会用到的字符串常量。该区域是只读的。
2)数据区:这个区域主要存储已初始化的全局变量、静态变量、一般常量。
3)BSS区:这个区域主要存储未初始化的全局变量、静态变量。
4)堆区:由程序员手动申请、手动释放回收。若程序员不手动释放,则在程序结束后由操作系统回收。所对应的函数是malloc()、calloc()、free()等。
5)栈区:由系统自动分配、自动释放回收,存放函数的参数值、局部变量等.
6)命令行参数区:存放环境变量等,例如main()函数的传递的参数值。
其中,堆区与栈区的内存是在程序执行时由系统分配的。当该程序需要分配内存时才会分配,不需要时不会分配(或者分配后直接回收)。而BSS区、数据区、代码区是在程序执行开始阶段就由编译器分配内存,这三个区域的内存在程序运行时会一直存在,不会被临时回收。

示例:程序中列举了常见的数据及数据所在的存储区域。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int a=0;//全局变量初始化区
char *p1;//全局变量未初始化区

int main()
{
	int b;//栈区
	char s[]="123456";//s在栈区
	char *p2;//栈区
	char *p3="123456";//p3在栈区,"123456"在代码区
	static int c = 0;//data区
	p1=(char*)malloc(10);//堆区
	p2=(char*)malloc(10);//堆区
	printf("a address is %p\n",&a);
	printf("p1 address is %p\n",&p1);
	printf("b address is %p\n",&b);
	printf("s address is %p\n",s);
	printf("p2 address is %p\n",&p2);
	printf("123456 address is %p\n",p3);
	printf("123456 address is %p\n",&"123456");
	printf("c address is %p\n",&c);
	printf("malloc1 address is %p\n",p1);
	printf("malloc2 address is %p\n",p2);
	return 0;
}

2.存储类型

2.1auto

​ : 自动类型 (让系统自动分配区域)

它说明的变量都是局部于某个程序范围内的,只能在某个程序范围内使用,通常在函数体内或函数中的复合语句内

int i = 10; // auto int i = 10;缺省状态下 默认是auto类型
在程序执行时创建,离开时自动销毁, 内存位置为堆栈

2.2register

: 寄存器类型

提高运行效率 CPU上寄存器数目很少,所以很珍贵
	
register float pi = 3.14;   //可能会申请失败!失败会自动转为auto类型

2.3static

:静态存储类型

2.3.1静态局部变量

​ 有时我们希望函数中的局部变量在函数调用后不消失而是继续保留原值,即它占用的存储空间不被系统释放。在下一次调用该函数时,该变量还保存着上次函数使用结束后的值。这时我们可以使用static关键字来定义一个静态局部变量。
用法:static 变量类型 变量名;

static char c;
static int a;

说明:

1)静态局部变量的存储空间在静态存储区,与函数的堆栈区不在同一个区域,因此函数运行完毕后静态局部变量不会被释放。
2)静态局部变量只赋一次初值。下次使用静态局部变量的时候直接使用上次保存下来的值而不会重新赋值。
3)如果静态局部变量未赋初值,则系统自动赋初值0(int型等)或’\0’(char型)。而其他变量则不会这样。其他的变量若未赋初值,则变量内会保存一个随机的数。
4)虽然静态局部变量在函数被系统释放后仍然存在,但它仍不可被其他函数调用。因为本质上讲它还是一个局部变量,只能在本函数内使用而不能在其他函数内使用。
示例:
#include<stdio.h>
void f()
{
	int a=1;
	static int b=1;
	a++;
	b++;
	printf("a是%d\nb是%d\n",a,b);
}
int main()
{
	f();
	f();
	f();c
	f();
	f();//调用5次f()函数
	return 0;
}
在示例程序中,尽管a和b都是局部变量,但变量b是静态局部变量,它会保存上一次函数运行后的值。因此每次运行f()函数,变量a的值都会初始化一次,而变量b则会使用上一次f()结束后的值。
练习:使用static变量,编程分别输出从16的阶乘值。
#include<stdio.h>
int jiecheng(int n)
{
	static int f=1;
	f=f*n;
	return f;
}
int main()
{c
	int i;
	for(i=1;i<=6;i++)
	{
		printf("%d!=%d\n",i,jiecheng(i));
	}
	return 0;
}
2.3.2外部变量

​ 将外部变量的作用域限制在本文件中//请对比extern关键字的第二条
有时程序设计需要,某些外部变量只能在本文件中使用而不允许在其他文件中使用。这时可以在定义外部变量时加static声明。

示例:
//文件file1.c
static int A;
……
//文件file2.c
void fun()
{
	A++;//出错
}
这种加上了static声明,只能在本文件中使用的外部变量称为“静态外部变量”。静态外部变量的作用是可以将某些变量“屏蔽”起来,从其他文件角度看这个变量“不可见、不可使用”,这样就保护了这个外部变量,防止程序运行出错。
2.3.3静态函数
	如果一个函数只能被本文件的其他函数调用而不允许被其他文件的函数调用,这样的函数称为“内部函数”。定义内部函数时,在函数名和类型名前加static关键字。
static int fun(int a,int b)
则该函数fun(int a,int b)只能被这个文件中的其他函数所调用,不能被其他文件的函数所调用。

2.4.extern

2.4.1外部参照引用型

1)在一个文件内扩展外部变量的作用域
如果一个外部变量不在文件开头定义,则其有效范围是从这个变量定义处到文件结束。在定义开始前的函数不能使用这个外部变量。如果需要使用,则可在引用这个变量前加extern关键字进行“外部变量声明”。例如:
示例:
int f()
{
	extern int a;//外部变量声明
	……
}
int a=10;//外部变量定义
这样就相当于扩展了外部变量a的使用范围,在函数f()中也可以使用变量a了。
关键字extern:对外部变量进行“外部变量声明”,表示把该外部变量的作用域扩展至此位置。

​ 注意:

⒈extern仅仅起到变量的声明的作用,而不是变量的定义。在示例中,如果没有int a=10;(即没有定义一个外部变量),则不能够使用extern int a;变量。extern声明外部变量可以有很多,但外部变量定义只允许有一个(否则算重复定义变量,编译报错)。
⒉我们提倡将外部变量写在所有需要引用它的函数之前(或直接写在文件开头,预处理指令(文件包含(#include)和宏定义(#define)和条件编译(#ifdef/#ifndef等))下面)。
⒊变量的声明不需要建立存储空间,而变量定义则需要建立存储空间。

2.4.2将外部变量的作用域扩展到其他文件中

一个C程序可以由多个文件组成。如果程序由多个文件组成,在其中一个文件中需要引用另一个文件中已知的外部变量,可以使用extern关键字将这个外部变量的使用范围扩展到需要使用的文件中。

c示例:
//文件file1.c
int A;
……
//文件file2.c
extern A;
……
这样就可以在文件file2.c中使用文件file1.c的外部变量A
3)定义一个外部函数
//请对比static关键字的第三条
如果一个函数不仅可以被本文件的其他函数调用,而且可以被其他文件的函数调用,这样的函数称为“外部函数”。定义外部函数时,在函数名和类型名前加extern关键字。
extern int fun(int a,int b)
则该函数fun(int a,int b)可以被其他文件调用

3.动态内存分配

:函数malloc()和free()

malloc函数是memoryallocation的缩写,中文译为“动态内存分配”。当我们需要动态地申请一块内存空间的时候,我们可以使用malloc()函数在内存中开辟一块空间。

函数原型:void* malloc(unsigned int size);
参数:需要申请的内存大小(单位:字节)(注意size是unsigned int类型,即没有负数。malloc(-1)这种用法是非法的)
返回值:void*型指针(成功)或NULL(失败)
说明:
程序是在内存中运行的。其中栈区是系统负责开辟和回收的,而堆区是我们程序开发人员手动申请与回收的。例如函数调用时,系统在栈区开辟空间,将局部变量等数据存放进这块区域。当该函数调用完毕,系统将该区域回收。这也是函数的局部变量在该函数调用完毕后无法再次使用的原因。而堆区不是系统负责的,是由程序开发人员负责开辟和回收的。堆区是预留给程序开发人员来使用的存储数据的区域。

malloc()函数就是在堆区开辟一块内存空间。如果我们使用了堆区的变量,即使函数调用完毕,堆区的变量仍存在。申请堆区的变量并使用完毕后要记得手动释放,否则可能造成内存空间不足等情况。
释放一块内存使用free()函数

函数原型:void free(void *p)
参数:需要释放的地址
返回值:无
malloc()函数和free()函数在头文件stdlib.h中,使用前不要忘记包含头文件。

示例:使用malloc()函数申请一块空间存放一个数组,并在使用完毕后回收这块区域
#include<stdio.h>
#include<stdlib.h>
#include<memory.h>//malloc函数头文件
#define MAX 10

int *get_memory(unsigned int a)
{
	int *p = (int*)malloc(a);
	if(p==NULL)//申请失败
	{
		printf("申请空间失败");
		return NULL;
	}
	return p;
}

int main()
{
	int *a = get_memory(MAX*sizeof(int));
	if(!a)
	{
		printf("申请空间失败!\n");
		return 0;
	}
	int i;
	for(i=0;i<MAX;i++)
	{
		a[i]=i;
	}
	for(i=0;i<MAX;i++)
	{
		printf("%d ",a[i]);
	}
	printf("\n");
	free(a);//释放这块内存空间
	a = NULL;//及时将指针置空防止出现野指针
	return 0;
}

在示例中虽然get_memory()函数在执行完毕后被系统回收,但它申请的空间在堆区并没有被系统回收,因此在main()函数中仍可使用这块空间。
注意在使用完毕后一定要free()这块空间。之后需要对指针置空避免出现野指针。

注意:free()函数释放一个指针不可连续释放多次。如果把示例程序的后几行改成:
free(a);
free(a);//多加一行free(a)
……

则程序运行会崩溃。为什么?
答:虽然调用free()函数将a指向的内存空间释放掉了,但指针a并不是空指针,此时指针a属于野指针,存放了一块未知内存地址。如果第二次free()的话则会释放这块未知内存地址的内存空间,造成崩溃。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值