补充动态内存分配和位运算.docx
- 文档编号:4043372
- 上传时间:2022-11-27
- 格式:DOCX
- 页数:12
- 大小:21.54KB
补充动态内存分配和位运算.docx
《补充动态内存分配和位运算.docx》由会员分享,可在线阅读,更多相关《补充动态内存分配和位运算.docx(12页珍藏版)》请在冰豆网上搜索。
补充动态内存分配和位运算
动态内存分配
一、为什么用动态内存分配
如果要存储数量比较多的同类型或同结构的数据的时候,总是使用一个数组。
比如说我们要存储一个班级学生的某科分数,总是定义一个float型数组
floatscore[30];
如果要使用指针变量,总是要让指针变量指向一个已经存在了的普通变量。
float*p=score;
但是,在使用数组的时候,总有一个问题困扰着我们:
数组应该有多大?
还有一个困惑,能不能不用数组名(地址常数)作中介,直接让p指向一个数组空间的首地址呢?
回答是肯定的。
在很多的情况下,你并不能确定要使用多大的数组,比如上例,你可能并不知道该班级的学生的人数,那么你就要把数组定义得足够大。
这样,你的程序在运行时就申请了固定大小的你认为足够大的内存空间。
即使你知道该班级的学生数,但是如果因为某种特殊原因人数有增加或者减少,你又必须重新去修改程序,扩大数组的存储范围。
这种分配固定大小的内存分配方法称之为静态内存分配。
但是这种内存分配的方法存在比较严重的缺陷,特别是处理某些问题时:
在大多数情况下会浪费大量的内存空间,在少数情况下,当你定义的数组不够大时,可能引起下标越界错误,甚至导致严重后果。
那么有没有其它的方法来解决这样的外呢体呢?
有,那就是动态内存分配。
所谓动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。
动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。
从以上动、静态内存分配比较可以知道动态内存分配相对于景泰内存分配的特点:
1、不需要预先分配存储空间;
2、分配的空间可以根据程序的需要扩大或缩小。
二、如何实现动态内存分配及其管理
要实现根据程序的需要动态分配存储空间,就必须用到以下几个函数
1、malloc函数
malloc函数的原型为:
void*malloc(unsignedintsize)函数的类型为通用型指针
其作用是在内存的动态存储区中分配一个长度为size的连续空间。
其参数是一个无符号整形数,返回值是一个指向所分配的连续存储域的起始地址的指针。
还有一点必须注意的是,当函数未能成功分配存储空间(如内存不足)就会返回一个NULL指针。
所以在调用该函数时应该检测返回值是否为NULL并执行相应的操作。
下例是一个动态分配的程序:
#include
#include
main()
{
inti,m;
int*array;/*array是一个整型指针,也可以
理解为指向一个整型数组的首地址*/
scanf(“%d”,&m);
array=(int*)malloc(m*sizeof(int));/*把通用型指针强制转换为整形指针*/
/*标准函数sizeof()用来测试指定数据类型的字节数。
可以测试任意数据类型,包括结构体和共用体。
该函数返回一个整数(这里在DOS下是2,windows是4)。
也可以直接写成array=(int*)malloc(40);
效果等价于array[10],但使用效率不同。
*/
if(array==NULL){
printf("不能成功分配存储空间。
");
exit
(1);/*退出运行*/
}
for(i=0;i<10;i++)/*给数组赋值*/
array[i]=i+100;
for(i=0;i<10;i++)/*打印数组元素*/
printf("%2d",array[i]);
}
上例中动态分配了10个整型存储区域,然后进行赋值并打印。
例中
array=(int*)malloc(10*sizeof(int);
if(array==NULL)
常常合并成一条语句。
if((array=(int*)malloc(10*sizeof(int)))==NULL)
语句的执行过程分为以下几步:
1)分配10个整型的连续存储空间,并返回一个指向其起始地址的整型指针
2)把此整型指针地址赋给array
3)检测返回值是否为NULL
2、free函数
由于内存区域总是有限的,不能不限制地分配下去,而且一个程序要尽量节省资源,所以当所分配的内存区域不用时,就要释放它,以便其它的变量或者程序使用。
光用malloc()函数申请内存,而不释放,称作“内存泄漏”,是计算机安全的大敌。
这时我们就要用free函数来释放内存(向操作系统返还)。
其函数原型是:
voidfree(void*p)无返回值,形参为通用型指针(指针类型不限)
作用是释放指针p所指向的内存区(返还操作系统)。
其参数p必须是先前调用malloc函数(动态分配存储区域的函数)时返回的指针。
给free函数传递其它的值很可能造成死机或其它灾难性的后果。
注意:
这里重要的是指针变量的值(即起始地址),而不是用来申请动态内存的指针变量本身。
例:
int*p1,*p2;
p1=malloc(10*sizeof(int));
p2=p1;
……
free(p2)/*或者free(p2)*/
这里:
malloc返回值赋给p1,又把p1的值赋给p2,所以此时p1,p2都可作为free函数的参数。
malloc函数是对存储区域进行分配的。
free函数是释放已经不用的内存区域的。
所以由这两个函数就可以实现对内存区域进行动态分配并进行简单的管理了。
这一对动态内存管理函数,有被称作“堆内存管理”函数或者“堆管理(heap)函数”。
堆是操作系统管理计算机内存的概念,这里就免谈了。
要用动态内存管理函数,就必须在程序中加入头函数:
#include
3、二维数组空间的动态分配
思路:
把每一行当作一个一维数组,分别动态分配内存。
//动态分配二维数组空间
//动态分配二维数组空间
#include
#include
voidmain()
{
intm=4;//二维数组的行数
intn=5;//二维数组的列数
inti,j;
//动态分配一个二维数组内存空间
//其类型为int,a指向该数组
int**a;//指向二维数组的指针为二级指针
a=(int**)malloc(m*sizeof(int*));
/*m个行指针构成一维数组,二级指针a
指向该数组的首地址*/
for(i=0;i a[i]=(int*)malloc(n*sizeof(int)); /*以上所作的工作,效果等价于静态内存分配a[4][5], 但动态内存分配的特点是静态数组a[4][5]代替不了的。 */ for(i=0;i for(j=0;j a[i][j]=i*j; for(i=0;i for(j=0;j printf("%d",a[i][j]); //由此分配的二维数组空间中,行与行之间不一定紧密相邻(连续)。 //但每一行内部的元素肯定是紧密相邻的。 //释放所分配的内存空间 for(i=0;i free(a[i]);//以行为单位释放数值空间 free(a);//二级指针a也要释放 } 如果把二维数组的动态内存分配用函数的方式单独写出,就要考虑二级指针变量的双向传递,形参要用三级指针。 上题改写如下: #include #include #include /*二维数组的动态内存分配函数*/ intdynamic_momory(double***x,intr,intc) { inti; /*double**x二级指针变量x是指向二维数组的指针 为每行的首元素申请r个地址,这些地址构成一个长度为r的一维数组, 这个数组的首地址存入二级指针变量**x*/ (*x)=(double**)malloc(r*sizeof(double*)); if((*x)==NULL)return0; /*申请存放每一行c个元素的一维数组空间,得到行首地址, 装入该行的首地址*/ for(i=0;i { *(*x+i)=(double*)malloc(c*sizeof(double)); if((*(*x+i))==NULL)return0; } return1; } /*主函数*/ voidmain() { double**a;/*二级指针二维数组*/ inti,j; constintn=10,m=5;/*n行m列*/ /*a双向传递,返回动态分配的二维数组首地址*/ dynamic_momory(&a,n,m); for(i=0;i for(j=0;j a[i][j]=100*rand()/65535.0; for(i=0;i { for(j=0;j printf("%4.1f",*(*(a+i)+j)); printf("\n"); } } 枚举 在实际问题中,有些变量的取值被限定在一个有限的范围内。 例如,一个星期内只有七天,一年只有十二个月,一个班每周有六门课程等等。 如果把这些量说明为整型,字符型或其它类型显然是不妥当的。 为此,C语言提供了一种称为“枚举”的类型。 在“枚举”类型的定义中列举出所有可能的取值,被说明为该“枚举”类型的变量取值不能超过定义的范围。 应该说明的是,枚举类型是一种基本数据类型,而不是一种构造类型,因为它不能再分解为任何基本类型。 枚举类型的定义和枚举变量的说明 一、枚举的定义枚举类型定义的一般形式为: enum枚举名{枚举值表}; 在枚举值表中应罗列出所有可用值。 这些值也称为枚举元素。 例如: enumweekday{sun,mou,tue,wed,thu,fri,sat}; 该枚举名为weekday,枚举值共有7个,即一周中的七天。 凡被说明为weekday类型变量的取值只能是七天中的某一天。 二、枚举变量的说明如同结构和联合一样,枚举变量也可用不同的方式说明,即先定义后说明,同时定义说明或直接说明。 设有变量a,b,c被说明为上述的weekday,可采用下述任一种方式: 方式一: enumweekday { ...... }; enumweekdaya,b,c; 方式二: enumweekday { ...... }a,b,c; 方式三: enum { ...... }a,b,c; 枚举类型变量的赋值和使用 枚举类型在使用中有以下规定: 1.枚举值是常量,不是变量。 不能在程序中用赋值语句再对它赋值。 例如对枚举weekday的元素再作以下赋值: sun=5;mon=2;sun=mon;都是错误的。 2.枚举元素本身由系统定义了一个表示序号的数值,从0开始顺序定义为0,1,2…。 如在weekday中,sun值为0,mon值为1,…,sat值为6。 voidmain() { enumweekday { sun,mon,tue,wed,thu,fri,sat }a,b,c; a=sun; b=mon; c=tue; printf("%d,%d,%d",a,b,c); } 3.只能把枚举值赋予枚举变量,不能把元素的数值直接赋予枚举变量。 如: a=sum;b=mon;是正确的。 而: a=0;b=1;c=a+b都是错误的。 如一定要把数值赋予枚举变量,则必须用强制类型转换,如: a=(enumweekday)2;其意义是将顺序号为2的枚举元素赋予枚举变量a,相当于: a=tue;还应该说明的是枚举元素不是字符常量也不是字符串常量,使用时不要加单、双引号。 #include voidmain() { enum { not_pass,pass,good,better,best }everyone[4]; inti; intscore; for(i=0;i<=3;i++){ scanf("%d",&score); if(score<60)everyone[i]=not_pass; elseif(score<70)everyone[i]=pass; elseif(score<80)everyone[i]=good; elseif(score<90)everyone[i]=better; elseif(score<=100)everyone[i]=best; /*赋值在0到4之间,但不能赋整数,以防书写混乱*/ } for(i=0;i<=3;i++)printf("%d\n",everyone[i]); /*输出结果为整数,在0到4之间*/ } 位运算 前面介绍的各种运算都是以字节作为最基本位进行的。 但在很多系统程序中常要求在位(bit)一级进行运算或处理。 C语言提供了位运算的功能,这使得C语言也能像汇编语言一样用来编写系统程序。 一、位运算符C语言提供了六种位运算符: &按位与 |按位或 ^按位异或 ~取反 <<左移 >>右移 1.按位与运算按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。 只有对应的两个二进位均为1时,结果位才为1,否则为0。 参与运算的数以补码方式出现。 例如: 9&5可写算式如下: 00001001(9的二进制补码)&00000101(5的二进制补码) 00000001(1的二进制补码)可见9&5=1。 00001001(9的二进制补码) &00000101(5的二进制补码) 00000001(1的二进制补码) 按位与运算通常用来对某些位清0或保留某些位。 例如把a的高八位清0,保留低八位,可作a&255运算(255的二进制数为0000000011111111)。 main() { inta=9,b=5,c; c=a&b; printf("a=%d\nb=%d\nc=%d\n",a,b,c); } 2.按位或运算按位或运算符“|”是双目运算符。 其功能是参与运算的两数各对应的二进位相或。 只要对应的二个二进位有一个为1时,结果位就为1。 参与运算的两个数均以补码出现。 例如: 9|5可写算式如下: 00001001|00000101 00001101(十进制为13)可见9|5=13 00001001(9的二进制补码) |00000101(5的二进制补码) 00001101(1的二进制补码) main() { inta=9,b=5,c; c=a|b; printf("a=%d\nb=%d\nc=%d\n",a,b,c); } 3.按位异或运算按位异或运算符“^”是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 参与运算数仍以补码出现,例如9^5可写成算式如下: 00001001^0000010100001100(十进制为12) main() { inta=9; a=a^15; printf("a=%d\n",a); } 4.求反运算求反运算符~为单目运算符,具有右结合性。 其功能是对参与运算的数的各二进位按位求反。 例如~9的运算为: ~(0000000000001001)结果为: 111111*********0 5.左移运算左移运算符“<<”是双目运算符。 其功能把“<<”左边的运算数的各二进位全部左移若干位,由“<<”右边的数指定移动的位数, 高位丢弃,低位补0。 例如: a<<4指把a的各二进位向左移动4位。 如a=00000011(十进制3),左移4位后为00110000(十进制48)。 6.右移运算右移运算符“>>”是双目运算符。 其功能是把“>>”左边的运算数的各二进位全部右移若干位,“>>”右边的数指定移动的位数。 例如: 设a=15,a>>2 表示把00001111右移为00000011(十进制3)。 应该说明的是,对于有符号数,在右移时,符号位将随同移动。 当为正数时,最高位补0,而为负数时,符号位为1,最高位是补0或是补1取决于编译系统的规定。 很多系统规定为补1。 例如: 设a=-15,a>>2,结果为-4。 (a=15,a>>2,结果为3。 补码存放,符号移动,高位补1) -15的原码10001111 (-15的补码)11110001(右移2位)11111100 (减1)11111011(求反,-4的原码)100001000 设a=-15,a<<2,结果为-60。 (a=15,a<<2,结果为60。 左移时符号位不动) main() { unsigneda,b; printf("inputanumber: "); scanf("%d",&a); b=a>>5; b=b&15; printf("a=%d\tb=%d\n",a,b); } 请再看一例! main() { chara='a',b='b'; intp,c,d; p=a; p=(p<<8)|b; d=p&0xff; c=(p&0xff00)>>8; printf("a=%d\nb=%d\nc=%d\nd=%d\n",a,b,c,d); } /*ab9798*/ 本章小结 1.枚举是一种基本数据类型。 枚举变量的取值是有限的,枚举元素是常量,不是变量。 2.枚举变量通常由赋值语句赋值,而不由动态输入赋值。 枚举元素虽可由系统或用户定义一个顺序值,但枚举元素和整数并不相同,它们属于不同的类型。 因此,也不能用printf语句来输出元素值(可输出顺序值)。 3.位运算是C语言的一种特殊运算功能,它是以二进制位为单位进行运算的。 位运算符只有逻辑运算和移位运算两类。 位运算符可以与赋值符一起组成复合赋值符。 如&=,|=,^=,>>=,<<=等。 4.利用位运算可以完成汇编语言的某些功能,如置位,位清零,移位等。 还可进行数据的压缩存储和并行运算。 *************************************************************************
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 补充 动态 内存 分配 运算