




已阅读5页,还剩95页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第6章指针,指针基础指针与数组指针与函数,6.1指针基础,6.1.1指针就是地址1.程序实体的内存地址一个程序一经编译,在其执行过程中,就会为变量、数组以及函数分配存储空间。这些变量、数组、函数都称为程序实体,也具有某一种数据类型。这些被分配了内存空间的程序实体,都具有自己的内存地址。,#includeintmain(void)inti1,i2;floatf1,f2;doubled1,d2;printf(数据大小:int,%d;float,%d;double,%dn,sizeof(i1),sizeof(f1),sizeof(d1);/*输出类型宽度*/printf(%ld,%ldn,解释:(1)变量存储空间的分类顺序:先声明的后分配;撤销的顺序与之相反:先建立的后撤销。这种机制就称为栈机制,好像往一个只能允许进出一个盘子的桶里放盘子,先放进的后拿出。在C语言程序中,局部变量就是被分配在栈区的,并且是以高端为栈底建立的。所以后建的变量的地址小。(2)每个变量只有一个地址,但占用的空间不同。空间的大小因类型而异。同时,数据的存储方式也不同,如实型数据采用浮点存储,而整型数采用定点存储。,2.指针的概念从根本上说,目标程序是按照地址访问这些程序实体的。C语言不仅提供了用变量名访问内存数据的能力,还提供了直接使用内存地址访问内存数据的能力。这个内存地址就称为指针。它好像一个指路标指向要访问的内存数据。,6.1.2指针变量及其定义,1.指针变量的概念变量可以用来存放数值(如整数、实数等),也可以用来存放地址(另一个变量的地址),这种专门用于存储指针(地址)的变量就称为指针变量。2.指针变量的定义在定义指针变量时,需要用指针声明符*表示此变量不是一般的变量,而是用来存放其他变量地址的指针变量。由于每一个变量都是属于一个特定类型的,因此在定义指针变量时,需要声明该变量的类型,以便能通过指针能正确访问特定类型的数据。定义一个指针的语法格式为:基类型标识符*指针变量名;,6.1.2指针变量及其定义,int*pi1,*pi2;double*pd1,*pd2;说明:(1)“基类型”,就是指针要指向的数据的类型。(2)定义指针变量时,在指针变量名前加符号“*”。“*”称为指针声明符,用于说明它后面的名字是一个指针变量名。例如语句:inti1,i2,*pi1,*pi2;定义了两个数据变量i1和i2,还定义了两个指向int类型的指针pi1和pi2。,3.指针变量的初始化和赋值,和其他变量一样,指针变量也可以初始化。可以用变量的地址对指针变量进行初始化,但必须注意:该变量的类型必须和指针变量的基类型相同。也可以用一个指针变量的值给另一个指针变量赋值,但它们应该具有相同的基类型。例如:inti1,i2,i3;int*pi1=注意,不要将一个变量的值赋给指向它的指针变量。例如pi1=i1;或pi2=i1;都是错误的。应该是将变量的地址赋给指向它的指针变量。如:pi1=pi=此处的“*”是“指针运算符”。又称为“间接访问运算符”,它作用于指针变量。*pi表示指针变量pi所指向的存储空间,即变量i。*pi相当于变量i。,2.使用指针变量需要注意的问题(1)使用指针,首先应当区分指针变量与它所指向的存储单元之间的不同。#includeintmain(void)inti1=10,i2=20,*p,*p1,*p2;p1=,#includeintmain(void)int*p1,*p2,i1=10,i2=20,i;p1=,(2)可以引用指针所指向的单元的值,但应注意,指针必须经过初始化或赋值,使它有确定的值,指向有效的程序实体,才能正确地引用其指向的单元的内容。如果指针变量未经赋值,它并不是没有值,而是一个未知或不确定的值,它所指向的存储单元也是未知的或不确定的。在未知和不确定的存储单元中存储的可能是无用数据,也可能是系统的重要数据。读取无用数据的操作是无意义的;有些非法入侵者也可以利用这种方法获取系统的重要数据;而向存储有重要数据的位置写入新的数据可能会造成系统的潜在危险,甚至可能造成系统瘫痪。所以,通常把没有指向有效程序实体的指针称为无效指针。,3.关于运算符“*”和“则通过“*”和“与小整数相加、减.(1)同类型指针间赋值同类型指针间赋值,就是使一个指针指向另外一个指针所指向的位置。,#includeintmain(void)inti1,i2;int*pi1=运行结果:指针间赋值前:pi1=215052,pi2=215048指针间赋值后:pi1=215048,pi2=215048,(2)指针与小整数相加、减#includeintmain(void)inti,*pi;doubled,*pd;pi=运行结果:pi=215052,pi+1=215056pd=215040,pd+1=215048通过指针与小整数的相加减去引用它所指向的数据单元是很危险的。,2.同类型指针间的比较和相减运算指针间的的比较和相减运算,主要用于指向同一数组的两个元素的两个指针之间,例如有一整型数组a,如已定义:inta10;int*p1=如p1的值为2000,p2的值为2016,则p2-p1的值不等于16,而等于两个数组元素下标之差,即两个元素之间的元素个数。指向同一数组不同元素的两个指针之间的关系运算,是比较它们之间的地址大小。如果p1p2,表示p1指向下标值小的元素,p2.指向下标值大的元素。如果两个指针相等,表明它们指向同一数组元素。,6.1.5指向指针变量的指针与多级指针,一个指针变量可以指向一个整型数据,或一个实型数据,或一个字符型数据,也可以指向一个指针型数据。这就是指向指针的指针。指向指针的指针形成二级指针。,#includeintmain(void)inti=8,*pi,*ppi;pi=运行结果:,6.1.6指向void类型的指针,ANSIC标准允许使用空基类型(void)指针,即不指定指针指向一个固定的类型,定义形式为:void*p;这表示指针变量p不指向一个确定的类型数据。它的作用仅仅是用来存放一个地址,而不能指向非void类型的变量。例如下面的写法是不对的:int*p1;void*p2;inti;p2=printf(%d,*p2);,如果确实需要将,6.2指针与数组,6.2.1数组元素的指针引用1.一维数组元素的指针引用通过对数组的讨论,已经知道数组元素在内存是连续存储的,并通过下标引用数元素。下标增1,数据改变一个数据单元位置。在学习了指针之后,已经看到定义了一个指针后,可以通过指针的移动来引用连续的存储单元。前已说明,数组名代表数组首元素的地址,即是一个指针。那么,数组元素和指针之间有什么关系呢?C语言的数组元素有两种引用方式:下标引用方式和指针引用方式。请看下面的例子。,#includeintmain(void)inta5=1,3,5,7,9,i,*p;printf(下标法:);for(i=0;i5;i+)printf(%d,ai);printf(n数组名法:);for(i=0;i5;i+)printf(%d,*(a+i);printf(n指针变量法:);for(p=a;pa+5;p+)printf(%d,*p);printf(n);return0;,运行结果如下:下标法:1,3,5,7,9,数组名法:1,3,5,7,9,指针变量法:1,3,5,7,9,2.多维数组元素的多级指针引用C语言用一维数组来解释多维数组:例如把二维数组解释为以一维数组为元素的一维数组;把三维数组解释为以二维数组为元素的一维数组;。对一个二维数组a来说,可以把它看成是由下列元素组成的一维数组:a0,a1,a2,aI,这里,ai既是是广义一维数组a的一个元素,又是一个一维数组ai的名字,是指向ai的起始元素的指针常量。,由图中可以得到如下结论:(1)二维数组a,可以看成由两个元素组成的向量,这两个元素一个称为a0,一个称为a1。按照上节的讨论,数组名a指向a0,a+1指向a1。而a0和a1本身又都是一维数组,它们分别由a00,a01,a02和a10,a11,a12组成。由于数组名是指针,所以a0和a1都是一级指针,它们的基类型(所向的存储单元中的数据的类型)是int类型。而数组a是由两个一级指针组成的数组,这两个指针具有相同的类型(指向基类型为int的指针变量),并且a指向a数组首元素(即a0的首地址-for(i=0;iN;i+)for(j=0;jM;j+)printf(a%d%d=%d,*(a%d+%d)=%d,*(*(a+%d)+%d)=%dn,i,j,aij,i,j,*(ai+j),i,j,*(*(a+i)+j);,#include#defineN2#defineM3intmain(void)staticintaNM=1,2,3,4,5,6;int*arrN=a0,a1;inti,j,*p=arr;for(i=0;iM;i+)printf(%d,*(*p+i);printf(n);,for(j=0;j0)p=string0;string0=string2;string2=p;if(strcmp(string1,string2)0)p=string1;string1=string2;string2=p;printf(排序后:n);for(i=0;i%sn,i,(2)本程序实际上说明了冒泡排序的过程,并用strcmp(stringI,stringj)进行两个字符串的比较。有了本例的基础,读者可以编写出顺序输出n个字符串的程序,也可以采用循环结构实现字符串的排序算法,例如用选择法,起泡法等。上例中,指针数组string的每一个元素都是一个地址(指向一个字符串),如果另外设一个指针变量p,用来指向string数组中的元素,那么这个p就是一个二级指针(又称“双重指针”)。,采用二级指针的字符串冒泡排序程序。,#include#include#defineN3intmain()char*stringN=Java,VisualBASIC,C;char*p=i+),printf(p+%d-%sn,i,*(p+i);printf(n);for(j=0;j0)ptemp=*(p+i);*(p+i)=*(p+i+1);*(p+i+1)=ptemp;printf(排序后:n);for(i=0;i%sn,i,*(p+i);printf(n);return0;,6.2.3内存的动态分配与动态数组的建立,1.动态分配的概念通过前面的讨论已经知道,使用指针方式进行内存空间的访问是非常危险的。于是可以设想,如果能在程序运行时能为指针分配一个连续的存储空间,将会使得指针的使用变得安全。C语言提供了这一功能。这一功能称为内存的动态分配。前面讨论过,全局变量是在编译时在内存静态存储区分配的,非静态的局部变量是程序运行时在栈区自动分配的,而为指针进行内存空间的动态分配是在程序运行过程中在自由内存区堆(heap)区分配的。堆可以形成比较大的存储空间,供动态分配使用。动态分配的特点是,可以由程序员控制,在需要时分配,在不需要时释放,还可以根据需要改变所分配存储空间的大小。这些功能重要通过stdlib.h库中的4个函数实现。,说明:(1)viod*p是说明p是void*类型指针,声明其基类型是未确定的类型,可以用强制转换的方法将其转换为任何别的类型。例如double*pd=NULL;pd=(double*)calloc(10,sizeof(double);表示将向系统申请10个连续的double类型的存储空间,并用指针pd指向这个连续的空间的首地址。并且用(double)对calloc()的返回类型进行转换,以便把double类型数据的地址赋值给指针pd。(2)使用sizeof的目的是用来计算一种类型的占有的字节数,以便适合不同的编译器。(3)由于动态分配不一定成功,为此要附加一段异常处理程序,不致程序运行停止,使用户不知所措。通常采用这样的异常处理程序段:if(p=NULL)/*或者if(!p)*/printf(“Noenoughmemry!n”);exit(1);,2.动态数组的建立通过前面的学习,可以建立这样的概念:数组就是用于存储同类型数据的连续空间。在C语言中,这个空间可以用下标形式表示,也可以用指针形式表示。动态数组指的是不在程序开始时定义固定大小的数组,而是在需要时建立数组,不需要时释放。在下面的例子中的动态数组用指针形式引用。,#include#include#defineSTUDENT_NUM3intmain()double*p=NULL,sum=0.0;inti;p=(double*)calloc(STUDENT_NUM*sizeof(double);if(!p)printf(“Memoryrequestfailed!n”);exit(1);,printf(“请输入学生的成绩:”);for(i=0;i1)+argv;printf(%sn,*argv);-argc;return0;,如果从键盘输入的命令行为:cfilehardwaresoftware则输出为:hardwaresoftware因为argc初值为3,每次循环减1,故其循环两次。第一次时,先使argv指向argv1,然后输出argv1指向的字符串“hardware”,第二次开始时,又使argv指向argv2,然后输出“software”。,用带参的main函数可以直接从命令行得到参数值(这些值是字符串),在程序运行时可以根据输入的命令行中的不同情况进行相应的处理。例如,在使用数据文件时,可以根据不同的需要输入不同的命令行;以打开不同的文件(有关文件的概念和使用将在第8章介绍)。利用main函数中的参数可以使程序从系统得到所需的数据,或者说,增加了一条系统向程序传递数据的渠道,增加了处理问题的灵活性。其实main的形参名并不一定非用argc和argv不可,只是习惯上一般用这两个名字。如果改用别的名字,其数据类型不能改变,即第一个形参为int型,第二个形参为指针数组。,顺便说明一个问题:在本例中用到argv+的运算,是使argv的值自加。而argv是数组名。以前说过数组名代表一个常量,它是数组起始地址,它是不能进行自加运算的,是不能改变其本身的值的。例如下面程序是不能通过编译的:main()inta5;inti;for(i=0;i5;i+,a+)printf(“%d”,*a);,错误在于a不能进行自加运算,a+不合法。这是由于在编译时给a数组分配一段内存单元,a代表数组起始地址,是一常量。注意a是main函数中的数组名,不是形参。如果将a设为形参数组,情况就不同了:voidfun(inta,intn)inti;for(i=0;in;i+,a+)printf(“%d”,*a);intmain()staticintarr5=1,3,5,7,9;fun(arr,5);,此时,a是形参。在编译时并未分配其固定的内存单元。只是在调用函数fun时才将arr的起始地址传给a,实际上a是一个指针变量,定义fun函数的第一行相当于:fun(int*a,intn)这里,a是指针变量,a+是合法的。所以例6.24中argv+是合法的。它的作用是使argv指针下移一个元素。,6.3.3返回指针值的函数,一个函数在被调用之后可以带回一个值返回到主调函数,这个值可以是整型、实型、字符型等类型,也可以带回一个指针类型的数据。例如前面介绍的内存动态分配函数malloc()和calloc()都是返回指针的函数。下面介绍如何编写返回指针的函数。例:编写一个函数,它的作用是在一个字符串中找一个指定的字符,返回该字符的地址(库函数中有标准函数strchr(),要求自己编写具有同样功能的stringchr()。,函数如下:char*stringchr(char*str,charch)while(*str+!=0)if(*str=ch)return(str);return(0);可以用下面的main函数调用它。#include,intmain(void)char*stringchr(char*str,charch);char*pt,ch,line=”IloveChina”;ch=C;pt=stringchr(line,ch);printf(“n字符串的起始地址:%o。n”,line);printf(“最先出现字符%c的地址是:%o。n”,ch,pt);printf(“这是该字符串中的第%d(从0开始)个字符。n”,pt-line);,这里4577550和457757是八进制的地址,十进制地址则分别是1245032和1245039。下图表明了这个程序的执行过程:str的初值是数组line的起始地址,即for(p=str1;*p!=0;p+);do*p+=*str2+;while(*str2!=0);*p=0;return(str1);,可以用main函数来调用它:#includeintmain(void)char*stringcat(char*str1,char*str2);charstring120=”Clanguage”,string2=”isfun.”,*pt;pt=stringcat(string1,string2);printf(“Thenewstringis:%sn”,pt);return0;,运行结果如下:Thenewstringis:Clanguageisfun.说明:stringcat()函数中的for语句作用是使p指向string1最后的“0”。dowhile循环的作用是将字符串string2中的字符按照p的指示位置逐个传到string1中去。开始,string2中第一个字符*str2的值赋给string1字符串中原来存放“0”的单元。然后str2和p都同步下移一个位置,再使*str2赋给*p,直到遇到string2中的“0”为止。注意应该再赋一个“0”给*p,即加到新串的末尾。这个过程如图6.21所示。函数stringcat()返回string1的首地址给主函数中的字符指针pt,并由printf()函数输出连接后的新字符串。,6.3.4指向函数的指针,一个函数包括一系列的指令,在内存中占据一片存储单元,它有一个起始地址,即函数的入口地址,通过这个地址可以找到该函数,这个地址就称为函数的指针。也可以定义一个指针变量,使它的值等于函数的入口地址,那么通过这个指针变量也能调用此函数,这个指针变量称为指向函数的指针变量。定义一个指向函数的指针变量的一般形式如下:类型标识符(*指针变量名)();例如:int(*p)();它表示p指向一个“返回整型值的函数”。注意*p两侧的括弧不能省略,如果写成:“int*p();”就成了“返回指针值的函数”了。,由于函数可以返回一个值。所以在C语言中,可以认为函数也具有数据类型。定义函数时必须定义函数返回值的类型(void也是一种类型)。同理,在定义一个指向函数的指针变量
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 中国银行包头市青山区2025秋招英文面试20问及高分答案
- 工商银行保定市望都县2025秋招笔试经济学专练及答案
- 邮储银行咸阳市旬邑县2025秋招笔试会计学专练及答案
- 工商银行滨州市惠民县2025秋招英文结构化面试题库含答案
- 中国银行哈尔滨市香坊区2025秋招英文面试20问及高分答案
- 邮储银行益阳市桃江县2025秋招笔试英语阅读理解题专练30题及答案
- 工商银行蚌埠市禹会区2025秋招英文结构化面试题库含答案
- 邮储银行营口市大石桥市2025秋招英文群面案例角色分析
- 基因检测代理培训
- 工商银行滨州市无棣县2025秋招笔试法律专练及答案
- 公司品牌建设五年规划
- 第二单元 三国两晋南北朝的民族交融与隋唐统一多民族封建国家的发展 知识清单 高中历史统编版(2019)必修中外历史纲要上册
- 居室环境的清洁与消毒
- GB/T 39766-2021人类生物样本库管理规范
- GB/T 2900.50-2008电工术语发电、输电及配电通用术语
- GB/T 2518-2008连续热镀锌钢板及钢带
- GB/T 1689-2014硫化橡胶耐磨性能的测定(用阿克隆磨耗试验机)
- 第二讲国外教育评价的发展历程
- 中外管理思想史-课件
- 教育学原理课后答案主编项贤明
- 湖南人民出版社乘槎笔记(斌椿)
评论
0/150
提交评论