程序设计基础(C语言)中ppt.ppt_第1页
程序设计基础(C语言)中ppt.ppt_第2页
程序设计基础(C语言)中ppt.ppt_第3页
程序设计基础(C语言)中ppt.ppt_第4页
程序设计基础(C语言)中ppt.ppt_第5页
已阅读5页,还剩157页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

程序设计基础(C语言)中,东北大学高级语言程序设计课程组,程序设计基础(C语言),指针,3,指针是语言的重要概念之一 它使语言比之其它程序设计语言更具特色 深入理解语言特性 指针是学习使用语言的难点 指针是C语言的一个重要概念 正确而灵活地使用指针 有效地描述各种复杂的数据结构 动态地分配内存空间 自由地在函数之间传递各种类型的数据 使程序简洁、紧凑,执行效率高 指针是C提供的一种数据结构 属于简单数据类型 它的意义和作用明显又不同于一般的数据类型,概述,4,1.预备知识,直接和间接的含义 内存 即内部存储器,是由内存单元组成 特点 存储单元是线性连续的 存储单元的最小单位为字节,5,1.预备知识,内存 计算机的内存中的每一个字节都有一个编号 所有的内存单元都要统一进行“编号” 这个编号就是该字节在整个内存中的“地址” 所有的内存单元都要有地址 每一内存单元具有唯一的内存地址 系统为每一个已定义的变量分配一定存储空间 使变量名与内存的一个地址相对应 通过地址我们就能够访问该地址所标识的存储单元 程序处理过程 需要根据地址取出变量所对应的内存单元中存放的值 参与计算/操作 处理结果存入相应的变量名所对应的内存单元,6,1.预备知识,计算机的寻址方式 直接寻址 间接寻址,数据 数据,内存,地址 1003 1004 1008 100C,B单元,内存,地址 1050 1051 3020 3021 ,地 址,数据 数据,A单元,7,1.预备知识,C语言如何处理? 高级语言中的变量具有三个属性 变量的名、变量的值、变量的地址 编程者使用c程序的一个变量 定义变量的数据类型 数据类型决定了一个变量在内存中所占用的存储空间的大小 变量在内存中总是占用几个连续的字节 定义变量名 语言的编译系统会根据变量的数据类型为该变量分配内存单元 确定变量的地址 变量的地址就是为变量分配的内存单元的起始编号 就可以通过变量名对内存中变量对应的地址进行操作,8,1.预备知识,9,1.预备知识,C语言如何处理? 直接访问 通过变量名访问数据的方式称为“直接访问“ 例如:printf(“%d“, n); /* 通过变量名访问变量n */ 间接访问 如果将变量n的地址存放在另一个变量pn中,通过访问变量pn,间接达到访问变量i的目的,这种方式称为变量的“间接访问“ 在C语言中,间接寻址方式访问内存是通过指针变量实现的 指针变量通过直接寻址方式访问获得的是一个地址值 在该地址起始的一个存储单元中存放程序需要的数据,10,2.指针变量,定义 用于指向其他变量的变量 指针变量是一个变量 和普通变量一样占用一定的存储空间 该存储空间存放的是一个地址值而不是普通的数据值 指针变量是一个地址变量 指针变量的值是一个内存单元的地址 它指向另一个变量的值 指针就是地址 当将变量的地址存入指针变量后,就说这个指针指向了该变量 指针变量只能存放某一数据类型的变量 问题? 变量的类型和指向变量的类型之间的区别?,11,2.指针变量,指针变量的声明 指针变量与一般的变量的区别 格式 *指针变量名; 说明 数据类型表示指针所指向的数据的数据类型 提示:不是指针变量的数据类型 *是指针的标志,表示它后面的变量名是指针类型 例 int *i_p;/*定义了指针变量i_p ,指向的对象类型为整型*/ char *cp;/*定义了指针变量cp, 指向的对象类型为字符型*/,12,2.指针变量,指针变量的声明 提示 声明语句的*只能用于指针变量 这种表示方式说明被声明的变量是一个指针 *不针对声明语句中的所有变量 每一个指针都必须在其名字前面有一个前缀*声明 例如:int *pt1,pt2; pt2为整型变量而不是整型指针变量 指针变量名是pt1而不是*pt1 指针变量只能指向定义时规定的类型变量 只有同一数据类型的地 址才能存放在指向该类型变量的指针变量中 例如: int *ip;/*ip指针变量只能存放整型变量的地址*/,13,2.指针变量,指针变量的声明 提示 在定义指针时可以用void将指针定义为空类型 不指定指针指向一个确定的类型 区分“值”与“内容”的含义 例如 整型变量是内容为整型数值的变量 整型指针变量是存放整型变量地址的变量 在引用指针变量前必须首先让指针指向一个变量 这一点非常重要 定义指针变量后,并未确定该变量指向何处,该变量的值是不确定的,14,2.指针变量,指针变量初始化 在使用指针变量时,要首先对指针变量进行初始化,让指针变量指向一个具体的变量 初始化的方式 使用赋值语句 例 int i, *pi; /* 说明指针变量pa和变量a */ pi=会出现什么问题?,15,2.指针变量,指针变量初始化 在使用指针变量时,要首先对指针变量进行初始化,让指针变量指向一个具体的变量 初始化的方式 给指针变量赋空值(赋值为符号常量NULL ) 例 p=NULL;/*表示指针p为空,没有指向任何对象*/ 一般情况下给没有初始值的指针变量赋值为NULL 实际上,NULL是整数0,系统保证0单元不存储任何数据的值(地址) NULL是头文件以及其它几个头文件中定义的符号常量,16,2.指针变量,指针变量初始化 提示 在使用指针之前一定要对其进行初始化 否则程序不知道指向何处 系统在执行程序时会给出如下的信息 run-time error R6001null pointer assigment 或者引起更严重的错误系统崩溃 对未使用的指针应该赋以空值NULL 表明它未指向任何地方 这并不是对指针的初始化 只是 表示该指针未被使用而已,17,2.指针变量,指针变量初始化 提示 一个指针变量赋空值与不赋值是完全不同的概念 前者是有不指向任何变量的地址 后者是一个不确定的随机值 这时指针可能指向一个事先未指定的数据 利用空指针可以简化程序代码、提高效率 指针变量应具有无符号整型类型的值 存放的是地址 由于地址本身的特征,不能将一个整型值赋给一个指针变量。同样也不能将指针变量的值赋给一个整型变量 例如:int *p;p=2000;(非法),18,2.指针变量,指针变量初始化 提示 用void将指针定义为空类型 即不指定指针指向一个确定的类型 例如:void *p;/*表示指针变量P不指向一个确定的类型*/ 它的作用仅用来存放一个地址 作用 任何类型的指针都可以赋给指向void类型的指针 指向void类型的指针也可以赋给任何类型的指针 但是:不能复引用void*类型的指针 编译器不能根据类型确定它引用的字节数 常见错误 将一种类型的指针赋给另一种类型的指针,而这两种指针都不是void*类型,【例】定义指针变量 void *p2; int *p1,i=5; p1=&i;p2=p1; 演示91。C,19,3.指针变量,指针变量的运算 语言提供的指针运算 取地址运算( p=&x /* &表示取的地址,将变量地址赋给指针变量p */ 提示 &是一个单目运算符,它返回变量的地址 &的操作数必须是一个变量 取地址运算符“&”是取操作对象的地址而不是其值,20,3.指针变量,指针变量的运算 语言提供的指针运算 取内容运算(*) 运算对象必须是指针(该指针所指向的对象可以是任意类型) 作用是取指针所指向变量的内容。*也称为“间接引用运算符”或“指针的复引用” 例如 a=*prt; /*表示指针 prt所指向变量的内容赋a*/ int a=3, *p, x; pa= /* *pa表示取指针变量pa所指单元的内容,即变量a的值,则x=3 */,21,3.指针变量,指针变量的引用 当指针变量定义和赋值之后,引用变量的方式 用变量名直接引用 例如 scanf(“%d”, /* 通过 * 运算符实现间接访问 */,22,3.指针变量,指针变量的引用 例:用指针变量进行输入输出,main() int *p,m; scanf(“%d”,&m); p=&m; printf(“%d”,*p); ,main() int *p,m; p=&m; scanf(“%d”, p ); printf(“%d”, m ); ,运行结果完全一样。请思考一下若将程序修改为如下形式: main() int *p,m; scanf(“%d”, p ); p=&m; printf(“%d”, m ); 会产生什么结果?,23,#include main() int a, *aptr; a=7; aptr= ,3.指针变量,指针变量的引用 例:本程序演示了指针运算符的用法,24,【例】 main() int *p1,*p2; int i=10,j=20; p1=&i; p2=&j; p2=p1; printf(“*p1=%d;*p2=%dn” ,*p1,*p2); printf(“i=%d;j=%dn” ,i,j); printf(“p1=%p,p2=%pn”,p1,p2); ,本题中若*p2=*p1? 将p1指向变量的值传递给* p2的内存单元。即j的内存单元中,P2和p1中存放的地址并不发生变化。,结果为: *p1=10;*p2=10 i=10;j=20,结果: *p1=10;*p2=10 i=10;j=10,3.指针变量,25,3.指针变量,指针变量的提示 取内容运算符*p与定义指针变量时使用的*p含义不同 定义时的*p表示的是一个指针变量,该变量指向一个整型变量的地址 取内容运算符*p则表示取指针变量的内容,即另一个变量值 对指针的引用需要*和& &用于求任一类型变量的指针,求出的指针具有对应的类型 *和指针一起使用时被认为是同类型的一个变量 可以参与该类型变量的一切运算 *和&是一对功能恰好相反的运算操作符 在实际应用中, 应特别注意&和*的操作对象 &的操作对象是一个“变量” *的操作对象是一个“地址”,26,4.指针与数组,指针和数组紧密联系 对数组元素的访问可以通过指针和借助数组元素下标的方法实现 凡是由数组下标完成的操作皆可用指针来实现 “下标方式“访问数组元素 通过数组的下标唯一确定了某个数组元素在数组中的顺序和存储地址 例如:int a5 = 1, 2, 3, 4, 5, x x=a0; /* 通过下标将第1个元素的值赋给x=1 */,27,4.指针与数组,指针和数组紧密联系 对数组元素的访问可以通过指针和借助数组元素下标的方法实现 凡是由数组下标完成的操作皆可用指针来实现 指针最常见的一种用途就是指向数组元素 当一个指针指向数组后,就可以灵活快速地处理数组元素,从而体现出指针的优点 “指针方式”访问数组中的元素 一个数组(/数组元素)有相应的地址,可以将数组的起始地址或某一个数组元素的地址放到一个指针变量中 数组的指针就是数组的起始地址(数组名),既数组第一个元素的地址(&数组名0) 数组元素的指针就是该数组元素的地址,28,4.指针与数组,指针和数组紧密联系 分析程序 语言中规定 数组第1个(下标为0)元素的地址就是数组的首地址 数组名代表的就是数组的首地址 数组名代表的一个地址常量,是数组的首地址 它不同于指针变量,#include main ( ) int a = 1, 2, 3, 4, 5 ; int x, y, *pt,*p2; pt = ,29,4.指针与数组,指针和数组紧密联系 语言中规定说明 当指针指向数组首地址时(pt=ary或pt=&ary0时) 指针可以向数组一样使用 提示 对数组元素的访问,下标方式和指针方式是等价的 从语言系统内部处理机制上讲,指针方式效率高 指针方式不如下标方式直观 下标方式可以直截了当地看出要访问的是数组中的哪个元素 指向数组的指针变量,进行运算以后,指针变量的值改变了,其当前指向的是哪一个数组元素不再是一目了然,30,4.指针与数组,指针和数组的可替代性 C语言中的指针和数组几乎可以互换 指针可以用来完成涉及数组下标的操作 例如:int a10;int *p; 若p=&a0;或 p=a;则p就指向了数组a的首址 当p指向了一维数组a,指针对数组元素可以表示: p+n与a+n( n (偏移量)的取值为09 )表示数组元素 an的地址,既&an *(p+n)和*(a+n) 表示数组元素an ( n的取值为09 ) 指向数组的指针变量也可以用数组的下标形式pn,其效果相当于*(p+n),等价于an,31,4.指针与数组,指针和数组的可替代性 例,#include void strcopy1(char str,const char *); void strcopy2(char str,const char *); main( ) char str110,*str2=“teacher”; char str310,*str4=“boys and girls”; strcopy1 (str1,str2); printf(“str1=%sn”,str1); strcopy2(str3,str4); printf(“str3=%sn”,str3); ,这两个函数都将一 个字符串(可能是 一个字符数组)拷 贝到一个字符数组 中,比较这两个函 数原型可以发现其 形式是相同的,虽 然这两个函数完成 同样的功能,但是 他们的实现过程是 不同的。,32,/* 采用数组表示法将s2拷贝到s1.*/ void strcopy1(char *s1,const char *s2) int i; for(i=0;s1i=s2i;i+) ; /*函数体中没有任何动作*/ ,/* 采用指针表示法将s2拷贝到s1.*/ void strcopy2(char *s1,const char *s2) for( ;*s1=*s2;s1+,s2+) ; /*函数体中没有任何动作*/ ,4.指针与数组,33,4.指针与数组,指针和数组的可替代性 数组的表示法 数组/下标 指针/偏移量 利用*p、*(p+1)、来代表数组a0、a1 数组/偏移量 对数组的引用以*a、*(a+ 1)、代替a0、a9 指针/下标 表达式p1引用了数组元素a1,34,【例1 】采用指针/偏移量表示的地址法输入输出数组元素。 main() int n,a10,*pa=a; /*定义时对指针变量初始化*/ for (n=0;n=9;n+) scanf(“%d”,pa+n); for(n=0;n=9;n+) printf(“%d”,*(pa+n); printf(“n”); ,【例2】采用表示的数组/偏移量表示的地址法输入输出数组元素。 main() int n,a10,*pa=a; /*定义时对指针变量初始化*/ for (n=0;n=9;n+) scanf(“%d”,a+n); for(n=0;n=9;n+) printf(“%d”,*(a+n); printf(“n”); ,4.指针与数组,35,【例3】采用指针/下标法输入输出数组元素。 main() int n,a10,*pa=a; for (n=0;n=9;n+) scanf(“%d”, ,【例4】采用数组/下标法输入输出数组元素。 main() int n,a10; for (n=0;n=9;n+) scanf(“%d”, ,4.指针与数组,36,main() int ary4=2,4,8,16; int *iprt,i; iprt=ary; /*按正序输出数组元素*/ for (i=0;i=0;i- -) printf(“%d” ,*( iprt+i ); prntf(“n”); ,4.指针与数组,指针和数组的可替代性 例:将一个数组按正向、逆向输出,37,4.指针与数组,指针和数组的关系 提示 指针变量与数组的区别 指针变量是地址变量,数组名是地址常量 指针变量的内容可以在程序运行过程中被修改,数组名一旦被声明,它的值在整个程序运行中是不能改变 (p+1)和 *(a+ 1)都可以表示数组元素a1, 两者并不能完全等价 对 数组a只能引用,不能修改 实际上就是指针与指针变量的区别 对于数组名,虽然可以使用它得到其元素的值,但它仅表示数组的首地址,不能将其改变 试图用指针算术运算修改数组名是一种语法错误,38,4.指针与数组,指针和数组的关系 提示 指针变量与数组的区别 指针变量可以改变其自身的值,应该特别注意其的当前值 例如:对上例程序中的a和pa,若有程序段: pa=a;printf(”%dn”,*pa); pa+;printf(“%dn”,*pa); 第一个printf输出的是a0的值 第二个printf则输出的是a1的值 指向数组的指针在数组处理时不要越界 由于C语言不检查数组越界,对一个仅有10个元素的数组a引用第11个元素a10时系统仍按 *(a+10)处理,发生错误,39,4.指针与数组,指针的运算 指针的运算旨在针对数组进行 指针的运算 指针与正整数的加减运算 两个指针的减法运算 两个指针的关系运算 禁止的运算 指针做乘除法运算 指针进行位操作运算 指针与浮点型数据运算 两个指针相加没有意义 指针运算的结果依赖于指针所指向对象的大小 指向对象的数据类型,40,4.指针与数组,指针的运算 指针与正整数的加减运算 该指针下移或上移整型值所指定个数的存储单元的内存地址 存储单元的大小就是该指针的数据类型所需要的内存的大小 p+(-)i*sizeof(type) 对指针的加/减(整数)操作运算在用指针访问数组元素是经常用到 表达式:p+n表示指针p所指向当前元素之后的第n个元素 而表达式:p-n表示指针p所指向当前元素之前的第n个元素,41,4.指针与数组,指针的运算 指针与正整数的加减运算 例如: int a10 ,*p; p=a;(或p=&a0 ;),4.指针与数组,指针的运算 指针与正整数的加减运算 自增/自减 指针的自增(+)/自减(-)运算分别表示将指针移动到下一个/上一个存储单元并指向新的数据 p+的含义 指针加,指向数组中的下一个元素 p-的含义 指针减,指向数组中的前一个元素,main() int *prt; int arrary=2,4,6,8,10; prt=array; prt+=3; prt-; prt-=2; prt+; 96。C,43,4.指针与数组,指针的运算 提示 例如:int a5,*pa:pa=a; pa+; /*使pa指向下一个元素a1;*/ *pa; /*指针所指向的数组名(或变量或数组元素)*/ *pa+; /*先取出pa所指向元素的值 (*pa),后pa+1送 与pa。由于+与*的优先级相同,自右向左的结 合方向,所以它等价于*(pa+);*/ *(+pa);/*先令pa加1,再取pa指向的值*/ (*pa)+; /*pa所指向的元素的值加1,即(a0)+*/ 对于-运算同上,44,4.指针与数组,指针的运算 两个指针的减法运算 减法运算的含义 当两个指针指向同一数组中的元素时 p-q表示计算两个指针相差的存储单元个数 计算公式为 提示 只有当两个指针指向同一数组中的元素时才可进行两个指针的减法运算,prt1-prt2= (prt1的值-prt2的值)/指针的数据类型的所占字节数,45,4.指针与数组,指针的运算 两个指针的减法运算 例:计算一个字符串的长度(不用strlen函数),#include main() int len; char a80 ; gets(a); puts(a); len=lenth(a); printf(“%dn” ,len); ,lenth(char *p) char *q=p; while (*q!=0) q+; return(q-p); ,46,4.指针与数组,指针的运算 两个指针的关系运算 比较两个指针的含义 比较两个存储单元的地址(相对位置) 除非两个指针指向同一数组,否则这种比较是没有意义的 比较两个指向同一数组的指针反映的是指针所指向元素的先后顺序 任何指针p与NULL进行“p=NULL”或“p!=NULL”运算均有意义 “p=NULL”是当指针p为空时成立 “p!=NULL”的含义是当p不为空时成立 例如:有两个指针ptr1和ptr2 if (ptr1ptr2) printf(“ptr1 points to lower memory than ptr2n”);,47,例1: 以下程序的输出结果是: #include main( ) int x =10,20,30,40,50,60; int *p=x; *(p+4)+=6; printf(“%d,%d”,8,*(p+4); ,A) 10,46 B) 0,56 C) 10,56 D) 50,46,例2: 已知: int a10=1,2,3,4,5,6,7,8,9,10; int *p=a; 则不能表示数组a中元素的表达 式是:,A)*p B)a10 C)*a D)ap-a,4.指针与数组,48,4.指针与数组,指针的运算 提示 数组名代表数组的首地址 固定的,不允许修改的 不能对数组名采用 +/-操作 指针的算术运算除了应用于数组外没有什么意义 除数组外,不能认为两个数据类型相同的变量在内存中是连续存储的 指针的运算与其基类型有关 通常情况下允许一个指针或多个指针指向同一个目标 常见错误 对不指向数组的指针进行算术运算 指针的运算结果超出的数组的范围,49,5.指针与字符串,可以通过字符数组和字符型指针处理字符串 例如: 字符数组实现的方式 char ch =“this is a book!” ; printf (“%sn” ,ch); 对字符数组初始化 ch是字符数组 它存放了一个字符串 字符指针实现的方式 char *ch=“this is a book!” ; printf (“%sn” ,ch); 是对字符指针初始化 *ch是字符指针 它指向的是一个字符串常量的首地址,即指向字符串的首地址,50,5.指针与字符串,可以通过字符数组和字符型指针处理字符串 字符指针与字符数组区别 char *ch字符指针是一个变量 指向字符串的第一个字符 可以改变字符指针使它指向不同的字符串 char ch 是一个字符数组 一个足以存放字符串和空字符0的一维数组 可以改变数组中保存的内容 数组名 ch是一个不可改变的常量 C语言规定 一个指针类型的变量如果指向对象为字符型 可以对其赋值(或初始化)字符串常量 此时字符指针指向的就是用来保存字符串的字符数组的首地址,51,5.指针与字符串,可以通过字符数组和字符型指针处理字符串 C语言规定 一个指针类型的变量如果指向对象为字符型 可以对其赋值(或初始化)字符串常量 此时字符指针指向的就是用来保存字符串的字符数组的首地址 定义 应用 可以使用字符指针指向一个字符类型的数据 可以使用字符指针访问字符串常量(另一种方法为字符数组) 将“good”赋予指针p并不是将字符串的字符赋予p, 是将“good”在内存中的存储区的起始地址赋给p,char *p;,main( ) char *pc;char ch; pc=&ch; scanf(“%c” ,pc); printf(“%c” ,ch); ,char *p; p=“good” ;,52,5.指针与字符串,字符指针 应用 例如 p+1作首地址;输出结果为bc 提示 赋值时,字符串的结束标志0是系统自动增加的 在输出或处理字符串指针变量和由字符数组组成的字符串时只要指出指针变量或字符数组的名字 对于其它类型的数组,不能只使用数组名,输出或处理全部元素 使用指针编写的字符串处理程序比使用数组方式处理字符串的程序更简洁、更方便 在C语言中,大量使用指针对字符串进行各种处理 在处理字符串的函数中,一般都使用字符指针作为形参,53,5.指针与字符串,指针与字符串的应用 例:字符串连接的一个程序 说明 在处理字符串时(包括它作为函数参数使用时),只需要指定其变量名,无须指出字符串的长度 对字符串str,若写成*str则变成了该字符串的第一个字符,表达式*(str+i)则是表示该字符串的第i+1个字符,#include main() char *str1=“People” ; char *str2=“teacher”; char *str3=“n”; strcat(str1,str2); strcat (str1,str3); printf(“%sn”,str1); ,main() char *str=“A string!n”; printf(“%s”,str); /*整个字符串操作*/ for(;*str!=0;) printf(“%c”,*(str+); /*单个字符操作*/ ,54,5.指针与字符串,指针与字符串的应用 例:设计程序,将两个字符串连接起来 不要用strcat函数,#include main() char s180,s280 ; printf(“n input string1:”); gets(s1); printf(“n input string2:”); gets(s2); stringcat(s1,s2); printf(“n new string:%s”,s1); ,stringcat(char *s1,char *s2) while (*s1!=0)s1+; while (*s2!=0) *s1=*s2; s1+; s2+; *s1=0; ,55,5.指针与字符串,指针与字符串的应用 例:编写程序判定一个字符在一个字符串中出现的次数,如果该字符不出现则返回0值 算法 从头开始扫描字符串的每一个字符 若当前字符等于字符串中的字符时,次数计数器加1 直到字符串中的所有字符比较完成,56,#include main() char *str,c; printf(“enter a string:n”); gets(str); printf(“enter a char:n”); c=getchar(); printf(“counts:%dn” ,char_count(str,c); int char_count(char *string,char letter) int count=0; while (*string) if (*string+= =letter) count+; return(count); ,5.指针与字符串,57,6.指针与函数,函数与指针之间有着密切的关系 包含三种含义 指针作为函数的参数 函数的返回值为指针 指向函数的指针,58,6.指针与函数,指针作为函数的参数 调用函数并希望改变参数的值时,指针做函数参数非常实用 按址传递 当函数要求能够修改调用函数中的一个或多个值,或能够传递指向大型数据对象的地址以避免开销很大的传值调用 通过变量的地址就可以通过地址间接访问变量的数值 指针是变量的地址 指针作函数的参数就是在函数间传递变量的地址 函数间传递的不再是变量中的数据,而是变量的地址 变量的地址在调用函数时作为实参,被调用函数使用指针变量作为形参接收传递的地址,59,6.指针与函数,指针作为函数的参数 要求 在调用函数时以地址做参数传递 当将变量的地址传递给被调函数后,在调用函数的函数体中采用间接引用运算符*修改内存单元中的该变量的值 实参的数据类型要与作为形参的指针所指的对象的数据类型一致 如果是变量的值需要修改,则在变量前面使用&来实现变量的地址 如果是数组的内容需要修改,则直接使用数组名 常见的错误 在需要复引用(*)一个指针来获取指针所指向的值时没有复引用该指针,60,6.指针与函数,指针作为函数的参数 典型例题:比较变量与指针变量的传递方式 变量传递,#include void swap(int p1,int p2); main() int x1=100,x2=200; printf(“before swap:x1=%d,x2=%d” ,x1,x2); swap(x1,x2); printf(“after swap:x1=%d,x2=%d” ,x1,x2); void swap (int p1,int p2) int temp; temp=p1; p1=p2; p2=temp; ,运行结果:x1=100,x2=200 x1=100,x2=200 形参p1与p2的单元值发生交换与实参x1和x2无关。,61,6.指针与函数,指针作为函数的参数 典型例题:比较变量与指针变量的传递方式 指针传递,#include int swap(int* p1,int* p2); main() int x1=100,x2=200; printf(“before swap:x1=%d,x2=%d” ,x1,x2); swap(&x1,&x2); printf(“after swap:x1=%d,x2=%d” ,x1,x2); void swap (int *p1,int *p2) int temp; temp=*p1; *p1=*p2; *p2=temp; ,指针作为参数时并未改变实参的值 而是改变了所指向单元中的内容,62,6.指针与函数,指针作为函数的参数 在主调函数中,传递的是两个变量的地址 并没有试图改变调用函数时的参数,而只是利用了指针变量的特性,改变了指针变量所指向单元的内容 swap根据得到的两个变量的地址将指针指向的变量中的数据进行调换,swap就是在要调换数据的两个实际变量的存储单元上进行的数据交换 交换的结果自然带回到主调函数main中 问题? 如果将例题中的swap()函数写出如下的形式,那么主调函数中的实参x1和x2的值能否改变呢?,swap(p1,p2) int *p1,*p2; int *temp; temp=p1; p1=p2; p2=temp; ,63,6.指针与函数,指针作为函数的参数 提示 地址作为函数的参数必须定义一个接收地址的指针参数(形参为指针类型) 例如: void max(int *mptr,int *nptr) 在使用函数之前先检查一下函数原型,确定该函数是否能够修改传递给它的值 函数原型中没有必要指出指针名,但是要指出指针类型 例如:函数原型void max(int *,int *); 用指针作为函数参数并在调用时传递地址作为实参这样可以改变主调函数中变量的值 不能通过改变指针形参的值而使指针实参改变 因为C语言中参数传递具有“单向性”,64,6.指针与函数,指针作为函数的参数 提示 除非主调函数明确地要求修被调函数环境中的参数变量,否则用传值调用给函数传递参数 传值调用只能在被调函数中修改一个值 要在被调函数中修改多个值必须使用传址调用 如果传递给函数的值没有(或不应该)在函数体内被修改,应该用 const限定符声明该值以防止被意外地修改 如果试图在被调函数中修改用 const限定符声明的值,编译器回给出警告信息或给出错误报文 常见错误 没有意识到传址调用需要指针做参数,而是把值传递给参数,65,6.指针与函数,指针作为函数的参数 例:输入三个数,按大小顺序排序,main() int a,b,c; scanf(“%d%d%d”, ,void swap (int *p1,int *p2) int temp; temp=*p1; *p1=*p2; *p2=temp; ,66,6.指针与函数,指针作为函数的参数 例:用传址方法将字符串中的小写字母转换为大写字母。,#include void convtoupper(char *); main( ) char string=“characters CHARacters”; printf(“The string before conversion is: %sn”,string); convtoupper(string); printf(“The string after conversion is :%sn”,string); void convtoupper(char *s) while (*s !=0) if(*s=a /*将指针s指向下一个字母*/ ,67,6.指针与函数,指针作为函数的参数 一维数组作为参数 函数的头部和函数原型的参数列表中可以采用一个指针表示一维数组 编译器对接收一个指针还是接收一个一维数组是不加区分的 当遇到如一维数组int a 的参数时,编译器将其转换为指针形式int *a; 这两种形式是可以互换的 例 能够将任意一个整型数组的若干个连接的元素输出,68,out_ary(int a ,int start,int count) int *p=&astart ,i; printf(“n”); for (i=0;icount;i+) printf(“%d” ,pi); main() int b10 =0,1,2,3,4,5,6,7,8,9; out_ary(b,0,3); out_ary(b,2,3); ,本例中形参实参均为数组名,并没有指针类型,但在过程中引用了指针变量。,6.指针与函数,69,例:指出下面程序的输出结果。 #include main ( ) int a=1,b=2,c=3,d; d=f( ,A)1,2,3,0 B)2,3,3,2 C)2,2,3,2 D)有语法错误,无输出结果,6.指针与函数,70,内存管理,C语言的内存分配有三种方式,即静态分配、动态分配以及在栈上创建。 动态分配是程序在运行期间根据实际需要动态的申请或释放内存的方式。 需要内存数量不确定 变量占据较大存储空间(结构体),71,内存管理,malloc() 函数原型 void *malloc(unsigned size); 例如:利用malloc()函数分别分配2个连续的存储空间,将其起始地址分别赋给字符指针pc和整型指针pi: char *pc; int *pi; pc = malloc (100); pi = malloc (50 * sizeof(int);,72,内存管理,calloc() 函数原型 void *calloc(unsigned n, unsigned size); 例如: int *pi; if(pi=calloc(50, sizeof(int)=NULL) printf (“内存分配失败!“); exit (1); ,73,内存管理,realloc() 函数原型 void *realloc(void *ptr, unsigned size); 例如: int* p = (int *) malloc (sizeof(int) * 10); p = (int *) realloc (p, sizeof(int) *15); p = (int *) realloc (p, sizeof(int) * 5);,74,内存管理,free() 函数原型 void free(void *ptr); 例如: int * p = (int *) malloc(4); *p = 100; free(p);,75,动态内存分配应用,利用动态内存分配函数实现字符串的动态分配。,#include #include #include main() char *s; register int t; if(s=malloc(50)=NULL) /*使用malloc分配50个字节动态空间*/ printf (“内存分配失败“); exit (1); gets(s); for (t=strlen(s)-1; t = 0; t-) putchar (st); /*反向打印 */ free (s); /* 释放 */ ,程序设计基础(C语言),结构体、共用体与枚举类型,78,1.结构概念,C语言允许将一组逻辑上联系的不同类型的数据组织起来作为一个整体使用 保证了数据之间的内在联系 用同一个名字引用的相关变量的集合 提供了将相关信息组合在一起的一种手段 语言提供了一种新的称为结构的构造型数据类型 概念 结构是一组相关的不同类型的数据的集合 结构类型为处理复杂的数据提供了便利的手段 结构体类型的变量可以拥有不同数据类型的成员 是不同数据类型成员的集合,79,成绩表: struct score char grade20;/*班级*/ long number ; /*学号*/ char name20;/*姓名*/ float os; /*操作系统*/ float datastru; /*数据结构*/ float cprog; /*C语言程序设计*/ float compnet; /*网络工程*/ ;,通讯录表: struct addr char name20; /*姓名*/ char department30;/*部门*/ char f_address50;/*家庭住址*/ long box; /*邮编*/ long phone; /*电话*/ char email20;/*Email*/ ;,1.结构概念,80,1.结构概念,结构体与数组 组成方式 都是由若干分量组成的 数组是由相同类型的数组元素组成 结构的分量可以是不同类型的 结构中的分量称为结构的成员 访问方式 数组中的分量(元素)是通过数组的下标 访问结构中的成员是通过成员的名字 结构体的成员可以分别引用 利用结构体可以组织复杂的紧凑的数据结构 如:链表、队列、堆栈和数等,81,2.结构的定义,在程序中使用结构之前,必须做的工作 定义结构体类型 建立一个可用于定义结构类型变量的模型 其组成的各个要素称为结构体的成员 结构的定义说明了该结构的组成成员,以及每个成员的数据类型 变量在计算机中的存在格式 定义结构体变量 要使用该结构就必须说明结构类型的变量 根据结构体类型为所定义的变量分配内存空间 提示 不同的问题有不同数据成员,即不同描述的结构体类型 可以理解为结构体类型根据所针对的问题不同而使得其结构体的成员是不同的 可以有任意多的结构体类型描述 使得C语言可以解决的问题范围扩大,82,2.结构的定义,结构定义的形式 说明 struct为关键字 结构的标识符 结构类型名称是所定义的结构的类型标识 由用户自己定义 中包围的是组成该结构的成员项 每个成员的数据类型既可以是简单的数据类型,也可以是复杂的数据类型 整个定义作为一个完整的语句用分号结束 提示 结构体类型的说明只是列出了该结构的组成情况 标志这种类型的结构模式已存在 编译系统并没有因此而分配存储空间,struct 结构类型名称 数据类型 成员名1; 数据类型 成员名2; 数据类型 成员名n; ;,83,2.结构的定义,结构定义的形式 例 当一个成员项是一个结构体时,就形成了结构体的嵌套 在数据处理时有时要用到结构体嵌套处理组织复杂的数据集合 常见的错误是忘记终止结构定义的“;”,例:定义一个日期结构体类型 struct date int year; int mouth; int day;,例:定义一个teacher 类型的结构体 struct date int year; int mouth; int day; ; strcut teacher char name20 ; struct date birthday; char depart20 ; ;,84,2.结构的定义,结构定义的形式 提示 结构体必须有struct开始 结构体的成员可以是基本数据类型,也可以是数组和其它结构类型的变量 结构体不能递归定义 在结构体类型说明中不能有该结构体变量 允许有指向该结构体的指针成员(自引用结构) 结构的定义可以在一个函数的内部,也可以在所有函数的外部 在函数内部定义的结构,仅在该函数内部有效 定义在外部的结构,在所有函数中都可以使用 定义结构体类型后可以定义该结构体类型的变量 通过变量使用该结构,以对不同变量的成员进行引用,85,2.结构的定义,结构体变量 定义的形式 struct 结构类型名称 结构变量名; 说明 说明结构变量的作用类似于说明一个类型变量一样 系统为所说明的结构变量按照结构定义时组成分配存储数据的实际内存单元 结构变量的成员在内存中占用连续存储区域,所占内存大小为结构中每个成员的长度之和 例:struct date int year; int mouth; int day; ; struct date today,days20,*day;,struct 结构类型名 成员1类型标识 成员1名; 成员2类型标识 成员2名; 成员n类型标识 成员n名; ; struct 结构体类型名 结构体变量列表;,86,2.结构的定义,结构体变量 提示 在程序中,结构的定义要先于结构变量的说明 不能用尚未定义的结构类型对变量进行说明 结构的定义和说明可以同时进行 被说明的结构变量可直接在结构定义的“ ”后给出 例如说明结构变量today可以使用下面的语句 struct date int year, month, day; today; 使用sizeof计算一个结构变量占用内存的实际大小 使用的一般形式为:sizeof(变量或类型说明符),87,2.结构的定义,结构体变量 提示 结构体类型与结构体变量是两个不同的概念 编译系统不为结构体类型分配内存空间,只为结构体变量分配内存空间 内存的大小依据结构体类型的定义(结构体类型的变量占内存长度不定长) 结构体中的成员名可以和程序中的其他变量同名,互不影响,也可以与结构变量名相同 但应尽量避免在不同的结构中使用相同的结构名 虽然允许,但会造成混淆 结构体变量的成员(域)可以单独使用(相当于基本变量) 成员也可以是指向本结构体类型的指针,struct teacher char name20 ; int age; char sex; char depart20 ; struct teacher *next; ;,88,3.使用结构体,结构成员的引用 结构作为若干成员的集合是一个整体 可对结构整体进行操作 可访问结构

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论