版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第8章指针8.1指针概述8.3指针与数组8.4指针数组与多级指针8.6指针程序设计案例本章主要内容8.2指针与函数8.5动态内存分配8.1指针概述8.1.1地址和指针1.变量的地址在C语言中,定义的变量(包括数组)会根据数据类型被分配一个或者多个连续的内存单元,变量的值存放在这些内存单元中。如果某个变量本分配多个连续的内存单元,那么第一个内存单元的地址,被称为给变量的地址。2.直接访问:定义变量后,对该变量的访问有两种,一种是“直接访问”,即按照变量名访问分配给该变量的内存单元。如:inta;scanf("%d",&a);8.1指针概述3.间接访问:是将变量a的地址存放在另一个变量中,然后通过后面的变量来找到变量a的地址,从而访问变量a。能够存储变量的地址的变量是哪种类型呢?在C语言中,可以定义这样一种特殊类型的变量用来存放地址,这就是指针变量。比如:inta,*p;//定义一个指针变量p,它指向整型变量p=&a;//将a的地址赋值给pscanf("%d",p);//访问p相当于访问a的地址8.1指针概述8.1.2指针变量1.指针变量的定义指针变量是存放另一个变量地址的变量,它是专门用来存放内存地址的,也叫做地址变量。。指针变量和普通变量一样,也要先定义后使用。定义指针变量的一般格式为:
类型名*指针变量名;注意:“类型名”要与指针变量所指向数据的类型(也称基类型)一致,指针变量名前的“*”在这是的作用是类型说明符,表示该变量为指针变量。8.1指针概述以下是指针变量的定义:其中,p1和p2为指向整型变量的指针变量,c1和c2为指向字符型变量的指针变量。任何一个指针只能指向一种数据类型,即只能保存这一种类型变量的地址。例如下面程序段是错误的:int*p1,*p2;char*c2,*c2;inta;float*p;p=&a;//p只能指向float类型的变量8.1指针概述2.指针变量的赋值指针变量中只能存放某个存储单元的地址,不能将一个整型数据或任何其他非地址类型的数据赋值给一个指针变量。指针变量一经赋值,它就“指向”了某个变量。指针变量一定要有明确的指向,才可以使用。指针变量赋值的一般格式为:
指针变量=&变量;
例如:
inta=3;
int*p;
p=&a;8.1指针概述(1)指针变量的初始化。像一般变量的定义一样,指针变量在定义的同时也可以指定初值,例如:(2)指针变量还可以为空值(0或NULL)。即该指针变量不指向任何变量,表示方法为:
p=NULL;或p=0;inta=3;int*p=&a;8.1指针概述(3)同类型指针变量间可以赋值,即用已赋过值的指针变量给相同类型的指针变量赋值。比如:p和q指向了同一个变量a。(4)不能给指针赋值一个非地址型数据。下面有关指针变量的赋值是不合法的。inta,*p,*q;p=&a;q=p; inta,*p;p=a;p=0x1000;a=p;8.1指针概述【例8-1】输入两个整数a和b,按由大到小顺序输出a和b。要求用指针变量实现数据的输入、比较和输出。#include<stdio.h>intmain(){
inta,
b,
t,
*p1,
*p2;
p1
=&a;p2
=&b;
printf("请输入两个整数:");
scanf("%d,%d",p1,p2);
if(*p1
<*p2)
{
t
=*p1;*p1
=*p2;*p2
=t;
}
printf("a=%d,b=%d\n",*p1,*p2);
printf("a=%d,b=%d\n",a,b);
return0;}8.1指针概述8.1.3指针运算1.取值运算符*通过指针变量得到它所指向变量的值的一般格式为:*指针变量名“*”是指针取值运算符,是一个单目运算符,它在不同的位置含义也不同。在定义指针时是类型说明符,在其他语句中是取值运算符。如在下列代码中:inta,*p;//此时的*是指针类型定义符p=&a;*p=3;//此时的*是指针取值运算符,*p代表p指向的变量a8.1指针概述【例8-2】阅读下列程序,熟悉指针变量的定义与使用。#include<stdio.h>intmain(){inta,*p; /*定义了指针变量p*/a=3;p=&a; /*p指向了变量a*/printf("a=%d\n",a);printf("a=%d\n",*p); /*通过指针变量p处理变量a*/return0;}8.1指针概述2.指针的算术运算(1)指针变量值加或减一个整数指针可以与一个整数进行加减运算,指针与整数的加减运算结果与该指针所指向的数据类型有关。p+n表示指针p当前所指向位置后面第n个同类型数据的地址,p-n表示指针p当前所指向位置前面第n个同类型数据的地址。例如:inta[5]={1,2,3,4,5},*p;p=a;/*指针变量p指向a[0]*/p++;/*指针变量p指向a[1]*/p+=2;/*指针变量p指向a[3]*/p--;/*指针变量p指向a[2]*/8.1指针概述(2)两个相同类型的指针可以相减如果有两个指针分别指向同一个数组的不同元素,则二者相减的结果表示这两个数组元素之间相差元素的个数,即数组元素下标之差。如有下面程序段:其中,q-p的结果为数字3,q-p的结果为数字-3。inta[5]={1,2,3,4,5},*p,*q;p=&a[1];q=&a[4];8.1指针概述3.指针的关系运算指向同一种数据类型的指针可以进行关系运算。如果两个相同类型的指针相等,表示这两个指针指向同一个地址。例如下面程序段:结果输出yesinta[5]={1,2,3,4,5},*p,*q;p=a;/*指针变量p指向a[0]*/q=a+2;/*指针变量p指向a[2]*/if(p<q)/*q指向的数组元素在p指向的数组元素之后,p<q为真*/
printf("yes\n");else
printf("no\n");8.1指针概述3.指针的关系运算说明:(1)对两个毫无相关的指针比较没有意义,因为指针只表示内存单元的“位置”信息;(2)指针也可以与0进行比较运算(常量0也可以赋值给指针变量),如果p==0成立,我们称p是一个空指针,即指针p还没有具体指向。8.2指针与函数8.2.1指针作为函数的参数若函数的形参为指针类型,则调用该函数时,对应的实参必须是与形参类型相同的地址值。指针变量作为函数参数传递数据,实际上是传送地址值,可以在被调用函数中引用调用函数中的变量。【例8-3】编写函数myadd(int*a,int*b),把a和b所指向存储单元中的两个值相加,然后将结果作为函数值返回。分析:在主函数中输入两个数给变量,把变量的地址作为实参,传递给相应的形参。程序如下:#include<stdio.h>myadd(int*a,int*b)/*指针作函数参数*/{
intsum;
sum
=*a+*b;
returnsum;}intmain(){
intx,
y,
z;
printf("输入两个整数:");
scanf("%d%d",&x,&y);
z=myadd(&x,&y);/*传送地址*/
printf("%d+%d=%d\n",x,y,z);
return0;}程序运行结果:输入两个整数:69↙6+9=158.2指针与函数在用一般变量作函数参数时,属于单向值传递,形参值的改变并不能改变对应的实参的值。把数据从被调用函数返回到调用函数的唯一途径是通过return语句返回函数值,这就限定了只能返回一个数据。但是,通过传送地址值,可以在被调用函数中对调用函数中的变量进行引用,这也就使得通过形参改变对应实参的值有了可能。利用此形式就可以把两个或两个以上的数据从被调用函数返回到调用函数。下面用这个例子来说明其用法:【例8-4】通过调用swap()函数,交换主函数中变量x和y中的数据。程序如下:#include<stdio.h>swap(int*a,int*b){
intt;
printf("(2)a=%db=%d\n",*a,*b);
t
=*a;*a=*b;*b
=t;
printf("(3)a=%db=%d\n",*a,*b);}intmain(){
intx
=30,
y
=20;
printf("(1)x=%dy=%d\n",x,y);
swap(&x,&y);
printf("(4)x=%dy=%d\n",x,y);
return0;}程序运行结果为:(1)x=30y=20(2)a=30b=20(3)a=20b=30(4)x=20y=308.2指针与函数8.2.2返回指针值的函数函数的返回类型可以是指针类型,返回值是指针类型的函数称为返回指针值的函数,也叫指针函数。指针函数的一般定义格式为:下面用一个例子来说明指针函数的用法:【例8-5】求一维数组中的最大值,要求用返回指针值的函数实现。数据类型名*函数名(参数列表){函数体}#include<stdio.h>#defineN10inta[N];/*定义一个全局数组变量*/int*max(intn);/*声明指针函数*/intmain(){
inti,*p;
for(i=0;i<N;i++)/*输入数组元素*/
scanf("%d",&a[i]);
p=max(N);/*max()函数返回最大值的地址*/
printf("最大值:%d\n",*p);/*输出最大值*/
return0;}int*max(intn)/*定义指针函数*/{
inti,m=0;
for(i=0;i<n;i++)/*查找最大值*/
if(a[m]<a[i])
m=i;/*m为最大值元素的下标*/
return&a[m];/*返回最大值元素的地址*/
}程序运行结果:输入/p>
输出:最大值:108.2指针与函数8.2.3指向函数的指针指针变量不仅可以指向整型变量、实行变量、字符串、数组,也可以指向一个函数。指向函数的指针变量定义的一般格式为:其中,数据类型名是所指向函数返回值的类型,参数列表是函数指针所指向函数的所有形参。例如:
int(*p)(int,int);/*p为指向函数的指针,所指函数有两个整型参数,返回值为整型*/数据类型名
(*指针变量名)(形参列表);8.2指针与函数函数指针的赋值:定义了指向函数的指针变量后,必须将一个函数名(函数的入口地址)赋值给函数指针,然后才能用函数指针间接调用该函数。赋值的形式如下:
指针变量=函数名;例如:int(*p)(int,int);
/*定义一个指向函数的指针变量p*/intadd(inta,intb);/*声明有两个整型参数的函数*/p=add;
/*指针p指向函数add*/【例8-6】利用指向函数的指针求两个整数的最大值。#include<stdio.h>intmax(intx,inty){
if(x>=y)
returnx;
else
returny;}intmain(){
inta,b,c,(*p)(int,int);
scanf("%d%d",&a,&b);
p=max;
c=(*p)(a,b);
printf("最大值:%d\n",c);
return0;}8.3指针与数组数组在内存中存放在一个连续的存储区域,数组名就是这个存储区域的首地址。因此,可以通过指向数组的指针变量来调用数组。8.3.1指针和一维数组1.一维数组元素的地址在C语言中,数组名可以认为是数组的首地址,但是数组一旦定义,首地址不能改变,也叫静态地址。由于数组在内存中占用一个连续的存储空间,所以,只要知道了一维数组的首地址,在首地址上加一个整数i(i>=0),就可以表示其后的第i个元素的地址。8.3指针与数组2.通过指针引用一维数组元素可以定义一个指针,让它指向一个一维数组,就可以通过这个指针引用一维数组的每一个元素。通过指针引用数组元素一般有两种方法:指针加偏移量法和指针移动法。下面用两个例子来说明这两种用法的区别。【例8-7】编写程序,利用指针加偏移量法输入输出五个整数#include<stdio.h>intmain(){
inta[5],*p,i;p=a;
/*获取数组的首地址*/for(i=0;i<5;i++)
/*通过指针p输入5个数*/
scanf("%d",p+i);for(i=0;i<5;i++)
/*获取指针p输出5个数*/
printf("%5d",*(p+i));
return0;}程序运行结果:输入:12345输出:12345【例8-8】编写程序,利用指针移动法输入输出五个整数。#include<stdio.h>intmain(){
inta[5],*p,i; for(p=a,i=0;i<5;i++)
/*通过移动指针p输入5个数*/
scanf("%d",p++);
for(p=a,i=0;i<5;i++)
/*通过移动指针p输出5个数*/
printf("%5d",*(p++));
return0;}程序运行结果:输入:12345输出:12345【例8-9】编写程序,通过指针正入倒出10个整数,即先输入的数后输出#include<stdio.h>intmain(){inta[10],i,*p;p=a;printf("输入10个整数:");for(i=0;i<10;i++) scanf("%d",p+i);printf("输出:");for(i=9;i>=0;i--) printf("%4d",*(p+i));return0;}程序运行结果:输入10个整数:12345678910↙输出:109876543218.3指针与数组3.向函数传递一维数组函数之间传递一维数组的时候,是要把一维数组的起始地址作为函数的参数传递,实际上是把一维数组名作为实参传递,在这里传递的就是地址,对应的形参定义为同类型的一维数组或同类型的指针变量。不管是指针变量作形参,还是数组做形参,被调函数并不知道指针所指向的数组共有多少个元素,所以,应该另外附加一个参数表示数组元素的个数或者数组长度。例如下面两个例子:【例8-10】将一个整型数组的数组元素乘以10后输出(用数组名做形参和实参)#include<stdio.h>voidreadd(inta[],intn)//用数组名作为形参{
inti;
for(i=0;i<n;i++)
a[i]=a[i]*10;
}intmain(){
inta[5],i;
for(i=0;i<5;i++)
scanf("%d",a+i);
readd(a,5);//用数组名做实参
for(i=0;i<5;i++)
printf("%4d",a[i]);
return0;}【例8-11】将一个整型数组的数组元素乘以10后输出(用指针做形参,数组名做实参)#include<stdio.h>voidreadd(int*q,intn){ inti; for(i=0;i<n;i++) *(q+i)=*(q+i)*10;}intmain(){inta[5],i;for(i=0;i<5;i++) scanf("%d",a+i);readd(a,5);for(i=0;i<5;i++) printf("%4d",a[i]);return0;}【例8-12】将一个整型数组的数组元素乘以10后输出(用指针做形参和实参)#include<stdio.h>voidreadd(int*q,intn){
inti;
for(i=0;i<n;i++)
*(q+i)=*(q+i)*10;
}intmain(){
inta[5],i,*p;
p=a;
for(i=0;i<5;i++)
scanf("%d",a+i);
readd(p,5);
for(i=0;i<5;i++)
printf("%4d",a[i]);
return0;}【例8-13】已知某书店M种图书的库存数量,统计库存量小于5的图书,并输出其相应的序号,要求编写一个函数求库存量小于5的图书共有几种及其序号。#include<stdio.h>#defineM10#defineN5intmain(){intsub(int*a,int*b)/*声明被调函数*/inta[M],i,b[M],k;/*b[M]用于存放小于5的序号,k存放小于5的种类*/
for(i=0;i<M;i++)
scanf("%d",&a[i]);
k=sub(a,b);
printf("小于5的图书种类:%d\n",k);
printf("小于5的图书序号:");for(i=0;i<k;i++)
printf("%d",b[i]);
return0;}intsub(int*a,int*b)/*定义sub函数*/{
inti,j;
for(i=0,j=0;i<M;i++)
if(a[i]<N)
{
b[j]=i;
j++;
}
returnj;
}8.3指针与数组8.3.2指针和二维数组指针不仅可以指向一维数组,也可以指向二维数组。1.二维数组的地址在C语言中,二维数组可以看作是一种特殊的一维数组,即数组的每个元素又是一个一维数组。例如:
inta[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};在这个二维数组中,a是数组名,a数组包含3行,可以看作是一个一维数组,包含3个元素:a[0]、a[1]、a[2]。其中每个元素又是一个一维数组,各包含4个元素。8.3指针与数组比如,a[0]所代表的一维数组包含了4个元素:a[0][0]、a[0][1]、a[0][2]、a[0][3]。如下图所示:由于数组名代表数组的首地址,a则代表整个二维数组的首地址,即第0行的首地址。a+1代表第1行的首地址,a+i代表第i行的首地址。a[0]、a[1]、a[2]被看作是一维数组名,所以它们又分别代表对应数组的第0号元素的地址a表示数组第0行的地址,a+i表示数组第i行的地址;a[i]或*(a+i)表示数组第i行的第0号元素a[i][0]的地址,即&a[i][0]。二维数组每行的首地址确定后,若要访问某行某列的元素,应先确定二维数组元素的地址。现以第0行元素为例,a[0]代表首地址,利用地址运算规则,加1使指针指向下一个数组元素,因此可以用a[0]+1表示a[0][1]的地址,即表达式a[0]+1等价于&a[0][1]。同理,若要访问第0行第2列的元素,它的地址可用a[0]+2表示。因此,二维数组中任意元素的地址&a[i][j]的表示形式为:a[i]+j、*(a+i)+j。通过二维数组元素的地址引用元素a[i][j],有以下几种等价的形式:
*(a[i]+j)、*(*(a+i)+j)、(*(a+i))[j]8.3指针与数组比如,a[0]所代表的一维数组包含了4个元素:a[0][0]、a[0][1]、a[0][2]、a[0][3]。如下图所示:由于数组名代表数组的首地址,a则代表整个二维数组的首地址,即第0行的首地址。a+1代表第1行的首地址,a+i代表第i行的首地址。a[0]、a[1]、a[2]被看作是一维数组名,所以它们又分别代表对应数组的第0号元素的地址8.3指针与数组2.通过指针引用二维数组元素像一维数组一样,也可以通过指针指向二维数组,从而对二维数组元素进行访问(1)指向二维数组元素的指针【例8-14】阅读下列程序,注意用指向数组元素的指针输出二维数组元素的方法。程序如下:8.3指针与数组2.通过指针引用二维数组元素像一维数组一样,也可以通过指针指向二维数组,从而对二维数组元素进行访问(1)指向二维数组元素的指针【例8-14】阅读下列程序,注意用指向数组元素的指针输出二维数组元素的方法。程序如下:8.3指针与数组#include<stdio.h>intmain(){
inta[3][4]
={1,2,3,4,5,6,7,8,9,10,11,12};
int*p;
for(p
=a[0];
p
<a[0]+12;
p++)
{
if((p
-a[0])%4
==0)//每输出四个整数就换行
printf("\n");
printf("%4d",*p);
}
return0;}8.3指针与数组(2)指向数组某一行的行指针如果让一个指针p指向二维数组某一行的起始地址,则称该指针为行指针。这时的p先指向a[0],则p+1不是指向a[0][1],而是指向a[1],p的增值是以一行中元素的个数(即一维数组的长度)为单位,如下图所示。8.3指针与数组定义二维数组行指针变量的一般格式为:
类型名(*指针变量名)[长度];其中“类型名”为所指数组的数据类型,“*”表示后跟的变量是指针类型,“长度”表示二维数组分解为多个一维数组时,一维数组的长度,也就是二维数组的列数。如:把二维数组a分解为一维数组a[0]、a[1]、a[2]之后,设p为指向二维数组的行指针变量,即指向由4个整型元素组成的一维数组。inta[3][4];int(*p)[4];p
=a;若p=a(指向二维数组的第0行,即a[0]),则p+i指向二维数组的第i行,与a+i相同。根据二维数组元素的地址计算规则,*(p+0)+j指向元素a[0][j]、*(p+i)+j指向元素a[i][j]。通过行指针引用元素a[i][j],有以下几种等价的形式:
*(*(p+i)+j)、*(p[i]+j)、(*(p+i))[j]、p[i][j]【例8-15】阅读下列程序,注意用行指针访问二维数组元素的方法。#include<stdio.h>intmain(){
inta[3][4]
={1,2,3,4,5,6,7,8,9,10,11,12};
int(*p)[4],i,j;
p
=a;
for(i
=0;
i
<3;
i++)
{
for(j
=0;
j
<4;
j++)
printf("%4d",*(*(p+i)+j));
printf("\n");
}}程序运行结果:123456789101112说明:此程序中p为行指针,它只能指向一个包含4个元素的一维数组,p的值就是该一维数组的首地址。p+1就指向下一个一维数组,这里的1代表一行元素的个数。8.3指针与数组8.3.3指针和字符串使用数组可以处理C语言中的字符串,使用指针变量可以更方便灵活地处理字符串。1.指向字符串的指针(1)字符串指针变量的定义用一个字符指针指向一个字符串,然后通过字符指针来访问字符串存储区域。如:等价于:char*cp="Thisisastring";char*cp;cp="Thisisastring";8.3指针与数组初始化后使cp指向了字符串中第一个字符(T),如下图所示。以后就可以通过cp访问字符串常量中的各字符。比如:*cp或cp[0]就是字符T,*(cp+i)或cp[i]就是字符串中的第i号字符元素。8.3指针与数组(2)字符串的输入和输出字符串的输入和输出都可以使用格式符“%s”。字符串输出时,使用“%s”格式是通过字符数组名或字符指针变量进行的。例如:
printf("%s",cp);给出字符指针变量名cp,则系统先输出它所指向的一个字符数据,然后自动使cp加1,使之指向下一个字符,然后再输出一个字符……如此直到遇到字符串结束标志'\0'为止。【例8-16】输出字符串中第n个字符后的所有字符。#include<stdio.h>intmain(){
char*cp
="Thisisastring";
intn
=10;
cp
=cp+n;
printf("%s\n",cp);
return0;}程序运行结果:
string8.3指针与数组定义一个字符指针变量,是给指针变量分配内存单元,但一定要给它赋予一个地址值,否则它将不具体指向一个确定的字符数据。如该赋值方式是正确的:而这种赋值是危险的:
charstr[5],*cp;cp
=str;scanf("%s",cp);char*cp;scanf("%s",cp);【例8-17】用指针移动法实现字符串的复制。#include<stdio.h>intmain(){
chara[]
="Goodmorning!",b[20],
*p1,
*p2;
p1
=a;p2
=b;
for(;
*p1
!='\0';
p1++,
p2++)
*p2
=*p1;
*p2='\0';/*复制字符串结束标记*/
printf("字符串a:%s\n",a);
printf("字符串b:%s\n",b);
return0;}8.3指针与数组2.字符串指针作函数参数将一个字符串从一个函数传递到另一个函数,可以采用传地址方式进行,即用字符数组名或指向字符串的指针变量作函数参数。在被调函数中可以改变字符串的内容并返回给主调函数。【例8-18】阅读下列程序,熟悉利用指针处理字符串的方法。#include<stdio.h>voidcopy(char*from,char*to){
for(;
*from
!='\0';
from++,
to++)
*to
=*from;
*to
='\0';}intmain(){
char*a
="Goodmorning!",
*b
="Goodafternoon!";
printf("字符串a:%s\n字符串b:%s\n",a,b);
copy(a,b);
printf("字符串a:%s\n字符串b:%s\n",a,b);
return0;}8.4指针数组与多级指针8.4.1指针数组指针数组是由若干基类型相同的指针变量所构成的数组,即数组元素为指向同一类型数据的指针变量的集合。1.指针数组的定义定义指针数组的一般格式如下:
类型名*指针数组名[元素个数];“类型名”表示指针元素指向的变量的数据类型,“*指针数组名”两边不能加“()”,否则,就会被理解成指向二维数组的指针。例如:int*p[3];8.4指针数组与多级指针2.指针数组的赋值对指针数组的赋值主要是对指针数组元素赋值,在赋值时应注意所有数组元素必须指向同一数据类型的数据。例如:char*a[5]={"a1","a2","a3","a4","a5"};说明:a是一维指针数组,它有5个元素,每个元素都是字符指针。3.指针数组的使用指针数组的元素可以指向一个一维数组,这时指针数组中的每个元素被赋予一维数组中相应元素的首地址。也指向一个二维数组,指针数组中的每个元素被赋予二维数组中每一行的首地址。同样,指针数组也常用来表示一组字符串,这时指针数组的每个元素被赋予一个字符串的首地址。【例8-19】将三个字符串按从大到小的顺序进行排列并输出。#include<stdio.h>#include<string.h>intmain(){
char*a[3]
={"I","am","Chinese"};
char*p;
inti;
if(strcmp(a[0],a[1])<0)/*比较数组元素a[0]和a[1]的大小*/
{
p
=a[0];a[0]
=a[1];a[1]
=p;
}
if(strcmp(a[0],a[2])<0)/*比较数组元素a[0]和a[2]的大小*/
{
p
=a[0];a[0]
=a[2];a[2]
=p;
}
if(strcmp(a[1],a[2])<0)/*比较数组元素a[1]和a[2]的大小*/
{
p
=a[1];a[1]
=a[2];a[2]
=p;
}
for(i
=0;
i
<=2;
i++)
printf("%s\n",a[i]);
return0;}8.4指针数组与多级指针8.4.2多级指针1.多级指针的概念如果一个指针变量存放的是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。如下图所示,若q是一个指针变量,而p就是一个指向指针的指针。8.4指针数组与多级指针2.二级指针的定义定义二级指针变量的一般格式为:类型名**指针变量名;“类型名”为指针变量最终访问到的变量的数据类型。“**指针变量名”表示指针变量是一个二级指针。例如:
int**p;定义了一个指向整型指针变量的二级指针p,其中,*p表示p所指向的另一个指针变量。8.4指针数组与多级指针3.二级指针的使用可以把任何一个指针变量的地址赋给指向指针的指针,例如:注意:二级指针必须与它所指向的指针变量数据类型相同。int**p,*q,a;//定义了一个二级指针变量pq=&a;//q则指向一个整型变量ap=&q;//p指向一个指针变量q【例8-20】阅读下列程序,注意二级指针的定义与使用。#include<stdio.h>intmain(){
char*q[]
={"This","is","a","C","Language"};
char**p;
inti;
for(i
=0;
i
<5;
i++)
{
p
=q+i;
printf("%s",*p);
}
return0;}8.5动态内存分配8.5.1什么是动态内存分配变量在使用前需要在内存中分配好存储空间,例如整型、实型、数组以及指针类型的变量均在编译时判断所需空间大小,并进行存储空间的分配,这种方式称为静态内存分配。这些内存区域由系统自动分配和释放,称为栈。动态内存分配就是指在程序执行的过程中动态地分配或回收存储空间的方法。动态分配不像栈式内存分配方法那样由编译器自动分配和释放,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。所有的动态分配都要在堆区中进行。8.5动态内存分配8.5.2动态内存的分配和释放C语言动态内存分配管理是通过标准库函数来实现的,其头文件为<stdlib.h>。1.动态内存分配函数1)malloc()函数实现动态分配最简单的标准库函数是malloc()。它的函数原型如下:
void*mallo(unsignedintsize)该函数的功能是在内存的动态存储区中分配长度为size的内存空间,若分配成功,函数返回一个指向所分配内存空间起始地址的void类型指针;若分配失败,函数返回0值指针NULL。参数size表示申请分配的字节数。8.5动态内存分配在实际编程中,malloc()函数返回值的void类型指针可以显式转换为其他指针类型。调用函数是,一般使用sizeof来计算内存空间的大小,因为不同系统重数据类型的空间大小可能不一样。需要注意,分配得到的内存空间是未初始化的,即内存中的数据是不确定的。例如,分配一个int型内存空间:若分配成功,p指向分配得到的内存单元,*p表示该内存单元。int*p;p=(int*)malloc(sizeof(int));8.5动态内存分配在实际编程中,malloc()函数返回值的void类型指针可以显式转换为其他指针类型。调用函数是,一般使用sizeof来计算内存空间的大小,因为不同系统重数据类型的空间大小可能不一样。需要注意,分配得到的内存空间是未初始化的,即内存中的数据是不确定的。例如,分配一个int型内存空间:若分配成功,p指向分配得到的内存单元,*p表示该内存单元。int*p;p=(int*)malloc(sizeof(int));8.5动态内存分配malloc()函数分配失败的主要原因是没有足够的内存空间可以分配,所以在内存分配后,要对它的返回值进行检查,确保指针是否有效,例如下面的代码形式:if(p!=NULL)
/*分配失败时p为NULL*/{......}8.5动态内存分配2)calloc()函数calloc()函数用于分配n个连续的指定大小的内存空间,函数原型为:
void*calloc(unsignedintn,unsignedintsize);calloc()函数的功能为n个元素的数组分配内存空间,其中,每个元素长度都是size个字节,即分配了n*size个字节的内存空间。calloc()函数与malloc()函数在功能上的区别是,calloc()函数会将分配得到的内存空间的所有数据初始化为0。8.5动态内存分配2.动态内存释放函数在动态内存分配时,应该在不需要该内存时将内存释放。如果频繁地申请内存而没有及时释放不再需要的内存,可能会将内存耗尽或引起内存泄漏。free()函数是用来释放之前分配的不需要的内存。free()函数原型为:voidfree(void*ptr)注意,在使用free()函数释放指针指向的空间后,应将指针的值置为NULL。例如:8.5动态内存分配【例8-21】动态申请一个一维数组,输入n个整数,然后逆序输出。分析:利用malloc()函数动态申请一个n*sizeof(int)的空间,如果申请成功,则输入n个整数,然后逆序输出,最后释放空间。p=(int*)malloc(sizeof(int));...free(p);p=NULL;#include<stdio.h>#include<stdlib.h>intmain(){
intn,i,*p;
printf("请输入元素个数:");
scanf("%d",&n);
p=(int*)malloc(n*sizeof(int));/*申请分配一个有n个int整型的内存空间*/
if(p!=NULL)/*内存分配成功才进行后面的操作*/
{
for(i=0;i<n;i++)
scanf("%d",p+i);
printf("逆序输出为:");
for(i=n-1;i>=0;i--)
printf("%d",p[i]);
free(p);
p=NULL;
}
else
printf("空间分配失败!\n");
return0;}8.5动态内存分配3.动态内存调整函数动态分配空间后可能会发现过大或过小,使用realloc()函数可以调整已申请内存的大小,realloc()函数原型如下:
void*realloc(void*ptr,unsignedintnewsize);函数功能为:将指针ptr所指向的动态内存空间扩大或缩小为newsize大小。ptr也可以是空指针,此时realloc()函数的作用等同于malloc()函数。8.5动态内存分配无论扩大或者缩小,原有内存中的内容将保持不变,缩小空间会丢失缩小的那部分内容。如果调整成功,函数返回一个指向调整后的内存空间起始地址的void类型指针。例如:int*p;p=(int*)malloc(50*sizeof(int));/*分配一个有50个int整型的内存空间*/p=(int*)realloc(p,10*sizeof(int));/*调整为有10个int整型的内存空间*/p=(int*)realloc(p,100*sizeof(int));/*再次调整为有100个int整型的内存空间*/8.6指针程序设计案例【例8-22】使用函数循环移动数组中的数据。有10个整数,使前面各数顺序向后移动m个位置,最后m个数变成最前面的m个数,写一个函数实现以上功能,在主函数中输入10个整数,输出循环移动后的10个整数。分析:编写一个函数,实现循环向后移动一个数组元素的功能,然后在主调函数中,循环操作m次,即可实现循环移动m个数的功能。#include<stdio.h>voidmoveone(int*array,intn);/*声明循环移动数字函数*/intmain(){
inti,a[10],m,*p;
printf("请输入10个整数:");
for(i=0;i<10;i++)
scanf("%d",a+i);
printf("请输入要移动的位置数:");
scanf("%d",&m);/*循环移动m个位置*/
for(i=1;i<=m;i++)
moveone(a,10);/*调用循环移动函数*/
printf("移动后的10个整数为:\n");
for(i=0;i<10;i++)/*输出循环移动后的10个整数*/
printf("%d",a[i]);
return0;}voidmoveone(int*array,intn){
inttemp,*p,*q;
p=array+n-1;/*让指针p指向数组最后一个数*/
temp=*p;/*将最后一个数临时存放在temp中*/
for(;p>array;p--)/*将前面的数依次向后移动一个位置*/
*p=*(p-1);
*array=temp;/*将最后一个数移到数组最前面*/}程序运行结果:请输入10个整数:12345678910↙请输入要移动的位置数:3↙移动后的10个整数
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 哺乳动物DNA的快速分离与PCR扩增
- 国际焊接工程师IWE培训课程中焊接标准体系
- 临床PCR检测的标准化
- 变压器保护主设备保护
- 区县灌区水费收缴情况调研岗公开招聘题库
- 《高频微波板材》课件
- 2026年秋季小学生家校协同体重健康管理课件
- 2022~2023乡村医生考试题库及答案第608期
- 2026年新中国成立77周年庆祝课件
- 真性红细胞增多症血栓栓塞综合管理中国专家共识解读
- 2024年10月高等教育自学考试13013高级语言程序设计试题及答案
- GB/T 44848-2024工业通风机通风机振动测量方法
- 《工程概论》课件
- 游乐场项目策划方案
- 电子商务课件教学课件
- DL∕T 1252-2013 输电杆塔命名规则
- 2025届云南师大附中高一下数学期末检测试题含解析
- DL-T5054-2016火力发电厂汽水管道设计规范
- 银行消保服务培训课件
- 罐式集装箱教学课件
- 发电机总调试报告
评论
0/150
提交评论