C语言程序设计_第1页
C语言程序设计_第2页
C语言程序设计_第3页
C语言程序设计_第4页
C语言程序设计_第5页
已阅读5页,还剩129页未读 继续免费阅读

下载本文档

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

文档简介

1、指针与指针变量的概念 一、指针的概念:“指针”是一个地址概念,在程序中定义了一个变量,系统编译时会给该变量分配一定长度的内存空间,该变量在内存中占用空间的地址,就称为变量指针。 计算机中存储的数据是放在存储器中,存储器由存储单元构成,存储单元中存放数据信息,对存储单元编号就是“地址”。 按变量地址存取变量值的方式称“直接访问”方式。二、指针变量的概念:指针是一个静态的地址的概念,指针变量是一种新类型的变量 指针数据类型变量,专门用来存放另外一些变量占用的存储单元的地址,即用来存放其它变量地址的变量称指针变量。一般习惯上把指针变量简称为指针,如将指向数组类型的指针变量称为数组指针等。指针变量的值

2、(即指针变量中存放的值)是指针(地址)。三、指针变量的定义:变量的指针就是变量的地址。可定义一个指向一个变量的指针变量,为表示指针变量和它所指向的变量之间的联系,用“*”符号表示“指向”。指针变量定义的一般形式为:存储类型 数据类型 指针变量名;如:auto int *pointer_1;其中:“存储类型”是任选项,可以是自动、静态、外部等存储类型;“数据类型”指明指针变量的数据类型,即说明该指针变量所指向变量的数据类型,此数据类型可以是整型、实型、字符型、数组类型等。如:auto int *pointer_1;其中是定义pointer_1为指针类型变量的说明符。说明:1.指针变量定义时存储类

3、型是可选项,常省略。2.指针变量名前面的*起标识作用,表示该变量是指针变量,而非变量名本身。3.同一类型的多个指针变量可在一条语句中书写,而且可以和同一数据的其它非指针类型变量在一条语句中定义。如:int a,b,*a_pointer,*b_pointer; 4.一个指针类型的变量只能指向同一类型的变量,而指向不同类型变量的指针不能混用。如果要把定义为一种类型的指针变量指向另外一种类型的变量,则必须通过强制类型转换,把被指向的变量的类型强制转换成该指针变量所能够指向的类型。只有同一类型变量的地址才能放到指向该类型变量的指针变量中。 5.凡定义为void *的指针指向对象的类型不定,只是在赋值时

4、才转换成对象类型。 6.指针变量只能存放地址,不要将一个整型量赋给一个指针变量。四、指针变量的引用:指针变量是存放另一个变量地址的变量,通过指针变量可间接地引用变量,实现对变量的存取操作。1.单目运算符:为取地址运算符。如:int i,*pi;则&i即为整型变量i的地址,可赋给指针pi,即pi=&i。说明:适用于整型、字符型、实型等简单变量和数组元素以及结构体成员,不能用于常量和表达式,因为常量和表达式没有地址的概念。对于寄存器变量,也不能用取地址运算符,因为寄存器变量存放于硬件寄存器中,并不存放在内存中,没有地址的概念。2100Hpii(2100H)2.单目运算符:作用与相反,是取地址运算符

5、的逆运算,它只能作用于变量的地址,并对其相应的存储区域进行访问。如*&i先计算&i的地址,然后*作用于该地址,求出存放在该地址的变量的内容。指针运算符常和指针变量相结合,从而实现对该指针变量所指向变量的访问,所以运算符又称“间接访问”的运算符。注:使用指针变量时不要将一整型变量赋给一个指针,指针变量所赋的值只能是地址。 五、指针的初始化:有两种形式: 1.是给出指针的值,如#define NULL 0 int *pointer=NULL 先宏定义NULL为0,即ASCII码为零的字符,第二行将指针初始化0,即该指针不指向任何变量,习惯上称为空指针。 2.是初始化时将指针指向某一变量。如:int

6、 n;int *pn=&n;即在定义指针变量pn的同时又将它指向了变量n。 注:对于静态和外部存储类型的指针变量,如定义时不初始化,则编译系统自动将指针变量初始化为NULL;而自动存储类型的指针变量,如没有进行初始化,编译不会将其置NULL初值。#include main()int a,*p1=&a; float b,*p2=&b; a=3;b=5; printf(“a=%d,b=%fn”,*p1,*p2);注意程序中定义和输出语句中,*p1与*p2的区别。 指针的值是某种类型变量地址所允许的整数,但指针不是整数,不能将任意整数向指针变量赋值,因为指针的值是某特定类型变量的地址,而变量的地址是

7、由编译系统决定的。 一、指针的算术运算:是一种地址的计算,是指针指向地址的变化,通过指针变量值加或减一整数来实现。指针不能参加所有算术运算,指针能参加的算术运算符只能是、+、-,即只能让指针加减一个整数、或两个指针相减,不能让指针加或减一个float或double类型的数据,也不能让两个指针相加或让指针参加乘法、除法、取余及字位运算等操作。main()static int a5=0,1,2,3,4; int *p; p=&a0; printf(“P points to a%d.n”,*p); p+=4; printf(“P points to a%d.n”,*p); p-; printf(“P

8、 points to a%d.n”,*p); p-=2; printf(“P points to a%d.n”,*p); p+; printf(“P points to a%d.n”,*p);P points to a0P points to a4P points to a3P points to a1P points to a2main()int i; float *p; static float a5=1.0,2.0,3.0,4.0,5.0; p=&a0; for(i=0;i5;i+) printf(“%lxn”,p+); p-=5; printf(“%lxn”,p); for(i=0;i

9、5;i+) printf(“%fn”,*p+);ff8aff8eff92ff96ff9aff8a1.0000002.0000003.0000004.0000005.000000 指针可定义成指向不同数据类型的变量,不同的数据类型其长度不同,其所占的内存单元个数不同,因此在指针加或减一整数时,地址的实际变化也有所不同。如整型变量占字节,则指向它的指针加时,地址值加。 两上指针相减时要求两个指针指向同一数组或指向同一结构,可用来求两个指针间的元素个数。假设p,q为指针,n为整数,则: p+n,p-n,p+,p-,+p,-p,p-q均是合法的; p*n,p/n,p%n,p+q,p/q,p%q均无意义

10、。 二、指针的赋值运算:指针是一个存放其它变量地址的变量可把含有地址的量赋给指针,即指针的赋值运算,语言允许指针接收任一变量的地址。赋值方式有: 1.将变量i的地址赋给p:p=&i; 2.将数组某一元素赋给p:p=&a3;3.将数组的首地址赋给指针p,有两种方式:把a0当作普通变量处理:p=&a0;把数组的名直接写在表达式右部:p=a;(因为C规定数组的名字代表数组的首地址)。4.对一个已定义的函数,可将指针指向它,即把该函数入口地址赋给指针:p=fun; (fun为函数名)。5.也可把一指针的值赋给另一类型相同的指针: p=q;此外也可将指针变量赋空值,使它不指向任何变量,常用如下形式:#d

11、efine NULL 0p=NULL; 即把指针p的存储单元中所有二进位置0,也就是使p指向地址为0的单元,系统保证任何有效数据不存放在地址为0的单元。三、指针的关系运算:关系运算符有、=、=、!=,均适合于指针的关系运算,对于关系运算的两指针必须满足指向同一数据集合,这里的同一数据集合可以是数组或结构等。注:指针变量的变量与一非指针类型的变量进行关系运算是没有意义的。 语言中不仅整型等简单类型可作函数的参数,指针也可作为函数的参数。中函数参数的传递是传参,函数调用时把实参值传递给形参,形参的改变对实参无影响。使用指针参数,将实参地址传给形参,被调函数可利用该地址间接访问函数之外的实参变量,可

12、引用其值,也可对它进行修改。 要使在函数中改变了的变量值被main函数所用,应该用指针变量作函数参数,在函数执行过程中使指针变量所指向的变量值发生变化,函数调用结束后,这些变量值的变化仍能保留下来。即调用函数不能改变实参指针变量的值,但可改变实参指针变量所指变量的值。不能企图通过改变指针形参的值而使指针实参的值也改变。如想通过函数调用得到n个要改变的值,可以:在主调函数中设n个变量,用n个指针变量指向它们;然后将指针变量作实参,将这n个变量地址传给所调用函数的形参;通过形参指针变量,改变这n个变量值;主调函数中就可使用这些改变了值的变量。函数调用可以(而且只可以)得到一个返回值(即函数值),而

13、运用指针变量作参数,可得到多个变化了的值。编写实现交换两变量数据的函数swap():swap(px,py)int *px,*py;int temp; temp=*px;*px=*py;*py=temp;main()int a,b,*p1,*p2; a=10;b=20;p1=&a;p2=&b; printf(“Before swapping:a=%d,b=%dn”,a,b); swap(p1,p2); printf(“After swapping:a=%d,b=%dn”,a,b);运行结果:Before swapping:a=10,b=20 After swapping:a=20.b=10指针可

14、指向数组,数组的定义和引用也可用指向它的指针表示。数组是同一类型变量组成的有序集合,每个元素在内存中占用存储单元,这些存储单元的地址是连续的。每个数组元素的地址称为整个数组的指针。数组的指针是指数组的起始地址,数组元素的指针是指数组元素的地址。 一、数组的指针表示 语言中规定数组名代表数组的首地址,要让一个指针指向一个数组,可直接把该数组名赋给一个指针变量,也可把每个数组元素地址赋给指针变量,即pa=%a0和pa=a是等价的,都代表数组的首地址赋给指针变量pa。也可在定义指针时将它初始化为指向某一数组,初始化形式如下:int *pa=a; 由于数组元素在内存中存放是连续的,因此数组元素存放的地

15、址也是连续的。C规定数组名代表数组的首地址,即a代表a0的地址,a+1代表元素a1的地址,a+i代表ai的地址。假设p为指针变量,并指向某一数组元素,C规定p+1指向数组的下一个元素(而不是将p值简单加1)。如数组元素是实数,每个元素占4个字节,则p+1使p的原值(地址)加4个字节,以使它指向下一元素。p+1所代表的地址实际上是p+1d,d是一个数组元崐素所占的字节数。对于数组a的指针pa,如pa的值为数组a的首地址,则表达式p1+i与表达式a+i含义一致,即ai的地址,即pa+i指向ai。通过指针可间接访问它所指向的对象,即*(pa+i)表示pai所指向的数组元素的值。同理a+i表示ai的指

16、针,*(ai)表示ai的值。任何指向数组元素的指针变量在引用数组时,也可像数组一样通过下标引用数组元素,即pai与*(pa+i)等价。可以看出,实际上是变址运算符,即将ai按a+i计算地址,然后找出此地址单元中的值。元素ai编译时就是处理成*(a+i)的。所以引用数组元素如有以下种形式:以数组元素的下标引用数组,如ai。利用数组名作为数组首地址,以数组元素的地址引用数组元素,如*(a+i)。利用指向数组元素的指针变量,以指向数组元素的指针表达式引用数组元素,如*(pa+i)或pai。注意数组a和指针pa的区别:pa是指针变量,它的值可以改变,如pa+;是正确的;而a是常量,它的值在系统为其定义

17、时已确定,是数组a的起始地址,a+是错误的。另外当指向数组a的指针变量pa的值为数组的首地址时,*(pa+i)或pai才和ai等价。当pa不指向数组的首地址时,它们就不完全等价了,因此在指针引用数组时要注意指针所指向数组元素的位置。在用指针引用数组元素时要注意指针的运算问题,特别是+和-使用的前后次序。如:*p+;表示先取*p的值,再使p指向下一元素。而*+p;先使p加1,再取*p的值。 同时要注意*p+的含义相当于*p(+)而不相当于(*p)+,因为(*p)+是使p指向的元素自加,而指针p的值并没有改变。注:如p指向a数组中第i个元素,则: *(p-)相当于ai-,先取p值作“*”运算,再使

18、p自减; *(+p)相当于a+i,先使p自加,再作*运算; *(-p)相当于a-i,先使p自减,再作*运算。 因ai相当于*(a+i),则&ai相当于&*(a+i)即&ai相当于a+i。如p指向数组a的第i个元素,即p的值是元素ai的地址,则有如下对应关系:ai+相当于*(p+),ai-相当于*(p-),a+i相当于*(+p),a-i相当于*(-p)。由于指针运算符和自加、自减运算符优先级别相同,则上面的圆括号可去掉。main()static int a=0,1,2,3; int i,*pa; for(pa=&a0,i=0;pa=&a3;i+,pa+) printf(“a%d=%dt*pa=%

19、dn”,i,ai,*pa);程序输出结果为: a0=0 *pa=0 a1=1 *pa=1 a2=2 *pa=2 a3=3 *pa=3 二、数组名或指向数组指针作函数参数 语言参数传递是传值的,而且在需要返回多个值时return语句又做不到,这就要使用数组名为函数参数,其主调函数形式为:main() int a5; fun(a,5); 其中fun(a,5);为函数调用语句,以数组名作实参,被调函数形式为:fun(x,n) int x,n; 其中fun为函数名,x为形参,被说明成数组形式。当以数组名作实参时,调用函数时实际是把数组的首地址传给函数形参,而不是把所有数组元素作为实参传递给形参,这里的

20、“值”传递的是数组变量的首地址。实参把地址传递给形参意味着形参和实参共同占用一段内存空间。不论形参还是实参,都可用指针或数组作参数,因此如果有一个实参数组,想在函数中改变此数组元素的值,实参与形参的对应关系有以下四种:1.形参和实参都用数组名:主函数的说明形式为:main() int array5; fun1(array,5);被调函数说明为:fun1(x,n) int x,n; 2.实参用数组名,形参用指针变量:主函数的说明形式为:main() int array5; fun1(array,5);被调函数说明为:fun1(x,n) int *x,n; /*调用时把数组array首地址赋给x*

21、/ 3.实参为指针变量,形参为数组名:主函数的说明形式为:main() int array5,*p; p=array;fun1(p,5);被调函数说明为:fun1(x,n) int x,n; 4.实参和形参都用指针变量:主函数的说明形式为:main() int array5,*p; p=array;fun1(array,5);被调函数说明为:fun1(x,n) int *x,n; 三、指向多维数组的指针和指针变量 1.多维数组的地址:以二维数组a34为例,可看成3个含有4个元素一维数组a0、a1、a2;二维数组代表第0行首地址,即a+1代表数组a1的首地址,即数组名加1实际上指针移动“一行”元

22、素,而不是“一个元素”。a0代表0行第0列元素的地址,a1代表1行0列的地址。ai+j代表元素aij的地址,也可写成*(a+i)+j或&aij。 所以二维数组元素aij可有以下等价的表达式:*(ai+j),*(*(a+i)+j),(*(a+i)j。 2.多维数组的指针:对多维数组可像处理一维数组一样用指向数组元素的指针变量,也可把多维数组看成由几个一维数组构成,然后用指针指向由多个元素构成的一维,即所谓用“行指针”处理多维数组。 如要找到元素aij的位置,设数组a共m行n列,公式为:i*m+j,这是因为数组元素在内存中是按顺序存放。 可定义一个指向由n个元素组成的数组的指针变量,语句为:int

23、(*p)4;定义一个指针变量,指向包含4个整型元素的一维数组,即“行指针”,这里的圆括号是必须的,如p增减1,表示指针向下或向上移动一行元素。 指向多维数组的指针可作为函数参数,用来间接引用数组元素,在函数中实现对数组的输入、输出、赋值等操作。 一、字符串的表现形式 字符串就其存储形式来说,就是元素的字符的一维数组。对字符串常量来说,它是由双引号括起来的字符序列。在程序运行过程中如出现字符串常量,编译程序就给字符串安排一存储区域,此区域是静态的,在整个程序运行过程中始终占用,编译系统自动在字符序列末尾添加一个字符串结束标志符0,此结束符不是数字0,而不ASCII码为0的字节。即字符串常量实际所

24、占存储区域字节数比它的字符个数多一个。 对于字符串常量的操作,可采用字符数组和指针两种形式实现。 1.用字符数组实现,如:char s=Good morning!; 则定义一数组名为s的字符数组,并初始化把字符串赋给字符数组,数组s有14个元素,最后元素s13值为结束符0。 2.用字符指针实现,如按递增或递减顺序访问数组,可用指针操作。先定义一指向字符数据的指针变量,然后用字符指针指向字符串常量,通过字符指针访问字符串的存储区域。 如:char *p=“Good morning!”;字符指针变量p存放的是该字符串常量在内存中的首地址。 在输出时采用指针来操作应先把指针指向该字符串,可采用pri

25、ntf(%s,p);不能写成:printf(%s,*p);因为以%s的格式输出,后面参数应当跟代表字符串地址的量,可以是字符数组名,也可是指向字符串的指针。通过字符数组或字符指针变量可输出一个字符串。而对数值型数组,则不能用数组名输出它的全部元素。 把字符指针指向字符串常量时,就可通过字符指针访问在常量存储区的各个字符。但不能通过指针变量修改字符串中的某些字符,因为字符串常量存在于内存中的常量区,不允许被修改。 二、字符指针作函数参数 将一个字符串从一个函数传递到另一个函数时,可用传递地址的方式,即可用字符数组名或指向字符串的指针作参数,就可在不同函数中引用字符串。 三、字符指针与字符数组比较

26、 字符指针与字符数组都能实现对字符串的存储和运算,大部分情况下它们之间可相互代替,它们的区别有: 1.是性质不同的两个变量,字符数组由若干个元素组成,每个元素中存放一个字符,而字符指针变量中存放的是地址,而不是将字符串的每个字符放到字符指针变量中。 2.字符数组的各个元素在内存中都有相应地址,并且字符数组名就代表字符串的首地址,但这个起始地址只是一个指针常量,是个固定值;而指针变量中的地址值是可以根据指向不同而变化。 3.字符指针与字符数组在初始化和赋值方面不一致: 在定义一数组时,系统编译时即已分配内存单元,有确定的地址,此时就可为字符数组输入数据。对字符指针,若定义之后,它本身在内存中的地

27、址也就随之确定,但该指针变量中存放的指针值并未确定,即该指针在内存中存放的地址值未确定,此时不可对其进行引用。 可在定义字符数组时为其进行初始化,即可在定义字符数组时,将字符串常量的值赋给该字符数组,如char a=good;这种初始化字符数组的方法是可以的,但不可以直接为字符数组赋值,如:a=good;即不允许在赋值语句中直接把字符串常量的值赋给字符数组。 对字符指针既可用字符串常量对字符指针初始化,也可直接用字符串常量来为字符指针赋值。 如:char *p=good;或char *p;p=good;都是正确的,都是将字符串的首地址赋给指针变量p。 数组可在变量定义时整体赋初值,如:char

28、 s14=“I Love China!”;但不能在赋值语句中整体赋值,如下面写法是错误的: char s14;s=I Love China!; 如定义了一个指针变量,使它指向一个字符串后,可用下标形式引用指针变量所指的字符串中的字符。 用指针变量指向一个格式字符串,如用它代替printf函数中的格式字符串。如:char *format;format=a=%d,b=%fn;printf(format,a,b);它相当于:printf(a=%d,b=%fn,a,b);也可用字符数组,如: char format=a=%d,b=%fn); printf(format,a,b); 字符数组赋初值时,如

29、: char str10=s,t,r,i,n,g,!,0;string!000 人为地在最后一个有效字符后加入结束标志0。实际上只要所赋初值的字符个数少于数组的元素个数时,系统都将自动在其后加入0。因此上例中去掉最后的0,其效果是相同的。如有定义:char str= s,t,r,i,n,g,!;则所赋初值没有字符串结束标志,因此不能认为str中存放字符串。如有定义: char str7=“string!”;则7个单元空间不够用,0将占用下一个不属于str的存储单元,即str中存放的不是字符串。例:以下不能正确进行字符串赋初值的语句是: A)char str5=“good!”; B)char s

30、tr=“good!”; C)char *str=“good!”; D)char str5=g,o,o,d; 一、指针数组的概念及使用 如一数组的每个元素都是指针类型变量,则称此数组为指针数组。定义指针数组的形式为:存储类型 数据类型 *数组名常量表达式 在定义中要注意下标运算符和间接寻址运算符*的结合优先性,语言的下标运算符优先级高于间接寻址*,因此指针数组先与下标运算符结合,形成一个有若干元素的数组,然后与间接寻址运算符*结合,形成每个元素都是指针变量的指针数组。 如:char *p10;p0=Good;表示将字符串常量的地址赋给该指针数组的第一个元素。 注:在定义指针数组时不与要指向一维数

31、组的指针变量混淆。 使用指针数组目的是便于统一管理同类指针,指针数组常用来管理若干个字符串,使每个元素指向不同的字符串,使字符串处理更加灵活方便。 指针数组不是把每个字符串的值赋给相应的指针数组元素,而是把每个字符串的首地址赋给相应的指针数组元素,即指针数组元素都是指针,它们的值是相应字符串的首地址,可通过字符串的指针一一对应地访问字符串。 指针数组处理字符串具有一定优越性,一是因为指针数组每个元素都是指针,可指向长度不等的字符串,比字符数组存放字符串节省空间;二是在对字符串处理上比较简洁、方便。 二、多级指针:指针是一地址概念,是一个变量,是专门存放其它变量地址的变量,系统编译时将为其分配存

32、储单元,即指针变量本身也有地址。一指针变量的值是其它变量的地址,那么这个地址也可能是一个指针变量的地址,当一个指针变量的值是另一个指针变量的地址时就构成了多级指针。单级指针示意图地址变量值指针变量多级指针示意图地址指针地址指针变量值变量地址指针 在多级指针中,指针的值就是变量的地址,称为单级间接地址;在多级指针中一个指针中存放的是另一个指针的地址,而另一个指针中存放的可能是别的指针变量的地址,直到最后一级指针才指向其所指向变量的地址,这种通过多级指针访问变量的方式称多级间接寻直。实际编程中以二级指针,即指向指针的指针为最简单和最常用的形式。二级指针定义的方式是在单级指针变量名前再加一个间接寻址

33、运算符*,如:char *p; 多级指针在定义时也可为其赋初值,即进行初始化操作。#include main()int i,*p1,*p2; i=10; p1=&i; p2=&p1; printf(“i=%dn*p1=%dn*p2=%dn”,i,*p1,*p2);程序运行结果:i=10*p1=10*p2=10 指针和函数有密切的关系:一方面可用指针指向函数,来达到间接引用的目的;另一方面函数的返回值也可以是指针类型。 一、指向函数的指针 1、用指向函数的指针变量调用函数:指针变量可指向一个函数,当指针指向简单类型变量时是直接把地址赋给指针变量;当指针指向数组这种构造数据类型时,是把数组名赋给指

34、针变量;当指向函数时,函数名代表函数的入口地址,函数编译时系统为其分配一入口地址,此入口地址称为函数的指针。 把一指针指向函数,就是把函数名赋给该指针变量,因为函数名代表函数的入口地址或称为函数存储的首地址,这种类型的指针称为指向函数的指针变量或简称为函数的指针,可通过函数指针来间接引用函数。 调用一次函数,实质上是控制指针指向函数的入口地址,使程序转入执行函数的各条语句如用一指针变量指向函数,就可通过指向函数的指针变量来引用函数,指向函数的指针变量即函数指针。在使用前必须先定义,函数指针的定义形式为: 类型说明符 (*函数指针名)();类型说明符是指针指向的函数返回值的数据类型,函数指针名两

35、侧括号不能省略,表示指针名先与*号结合,是指针变量,再与后面的()结合,表示指针变量指向函数。函数的调用可通过函数名调用,也可通过函数指针调用。函数指针在使用前要进行定义,并为函数指针赋值,使它指向某一函数后才可使用函数指针调用函数,但函数不是一种数据类型,使用时应注意:对于函数指针的目标运算符*号,与指向一般数据类型指针的目标运算符含义不同,指向一般数据类型指针做*运算,目的是通过指针访问其内存中的值;而函数指针实行访问目标运算*时,其结果是使程序控制转移至指针指向的函数,并执行函数体,执行完函数体各项操作后,带回返回值。(*p)()表示定义了一个指向函数的指针变量,该指针变量不是专指向某一

36、个函数,而是专指向某一类函数,这一类函数返回值应相同,把不同函数入口地址赋给函数指针,就可指向不同的函数,但这些函数返回值类型应与函数指针所允许指向的类型标识符相一致。为函数指针赋值时,是把函数名赋给指针变量,不可加参数,如p=max(a,b);是错误的。因为函数参数表示对函数的调用。对函数指针的运算,如函数指针加或减一个整数,函数指针的自加、自减均无意义。即它只能指向函数的入口处而不能指向函数的某一条指令处。在一程序中,一个指针变量可先后指向不同的函数。2、用指向函数的指针变量作函数参数:函数的参数可以是简单变量、数组名及指向不同变量的指针,也可用指向函数的指针变量作函数参数。在中,指向函数

37、的指针也可作为指针数组的元素,这样的指针数组称为函数指针数组,即该指针数组的每个元素都是指向函数的指针变量。 二、返回指针的函数 函数可返回某种类型的指针值,把返回值为指针的函数称为指针型函数,其定义的一般形式为:类型说明符 *函数名(参数表) 注:函数名两侧分别是*号运算符和()运算符,()的优先级高于*,函数名先于()结束说明是函数,再与*结合,说明此函数返回指针类型的值,类型说明符表示指针函数返回的指针是指向何种类型的。 在指针型函数中,使用return语句返回的可以是变量的地址,数组的首地址或指向某种类型的指针变量,也可是结构体、共用体等构造数据类型的地址。 当main的参数为空,编译

38、成目标程序时,键入的命令为“可执行的目标文件名”,程序所需的数据在运行期间输入。main函数也可有参数,如:main(argc,argv),其参数不像一般函数参数被用来完成函数间的通信,它的参数由编程人员在执行该程序的命令行里输入,并由操作系统传给main函数,这种在命令行里把数据传给程序的参数称“命令行参数”,或称main函数参数。 输入命令行参数的形式为: 命令名 参数1 参数2 参数n 命令行的命令名及各参数间必须用空格或制表符分隔。逗号、分号或其它类似符号都不能作为分隔符使用。 如有一目标文件名为file1,要将两个字符串“China”、“Beijing”作为传送给main函数的参数,

39、可写成以下形式:file1 China Beijing 注:以上参数与main函数中形参的关系。main函数中形参argc是指命令行中参数的个数(文件名也作为一个参数)。如“file1”是一个参数,因此argc的值等于3;main函数的第二个形参是一个指向字符串的指针数组。即带参数的main函数形式应当是:main(argc,argv) int argc; char *argv; 命令行参数应当都是字符串,这些字符串的首地址构成了一个指针数组。一、有关指针的数据类型1.指针变量加(减)一个整数:一个指针变量加(减)一个整数并不是简单地加(减)一个整数,而是将该指针变量的地址和它指向的变量所占用

40、的内存字节数相加(减)。即p+i代表地址计算:p+c*i。c为系数(字节数),对整型数据c2,实型c4,字符型c1。这样才能保证*(p+i)指向p下面的第i个元素,它才有实际意义。 2.指针变量赋值:将一个变量地址赋给一个指针变量。如:p=&a; (将变量a的地址赋给p) p=array; (将数组array首地址赋给p) p=&arrayi; (将数组array第i个元素的地址赋给p) p=max; (max为已定义的函数,将max的入口地址赋给p) p1=p2; (p1和p2都是指针变量,将p2的值赋给p1)注意:不能把一个整数赋给指针变量。 如p=1000;是错误的。 只能将变量已分配的

41、地址赋给指针变量。 也不能把指针变量p的值(地址)赋给一个整型变量i:i=p; 3.指针变量可以有空值,即该指针变量不指向任何变量,可这样表示:p=NULL; NULL是整数0,它使p的存储单元中所有二进位均为0,也就是使p指向地址为0的单元。系统保证使该单元不作它用(不存放有效数据),即有效数据的指针不指向0单元。实际上是先定义NULL,即: #define NULL 0 p=NULL; 在stdio.h头文件中就有以上的NULL定义,它是一个符号常量。应注意,p的值为NULL与未对p赋值是两个不同的概念。前者是有值的(值为0),不指向任何变量,而未对p赋值并不等于p无值,只是它的值是一个不

42、确定的值,也就是p实际上可能指向一个事先未指定的单元。这种情况很危险,因此在引用指针变量前应对它赋值。 任何指针变量或地址都可与NULL作相等或不相等的比较,如: if(p=NULL)4.两个指针变量可以相减:如两个指针变量指向同一个数组的元素,则两个指针变量值之差是两个指针之间的元素个数。如p1指向a1,p2指向a4,则p2-p1=4-1=3。但p1+p2无实际意义。5.两个指针变量比较:如两个指针指向同一个数组的元素,则可进行比较。指向前面的元素的指针变量“小于”指向后面元崐素的指针变量。注意,如不指向同一数组则比较无意义。三、ANSI新标准增加了一种“void *”指针变量,即可定义一个

43、指针变量,但不指定它是指向哪一种类型数据的。ANSI C标准规定用动态存储分配函数时返回void指针,它可以用来指向一个抽象的类型的数据,在将它的值赋给另一指针变量时要进行强制类型转换使之适合于被赋值的变量的类型。如:char *p1; void *p2; p1=(char *)p2; 同样可用(void *)p1将p1给地址转换成void *类型。如:p2=(void *)p1; 也可将一个函数定义为void *类型。 如:void *fun(ch1,ch2) 表示函数fun返回的是一个地址,它指向“空类型”,如需要引用此地址,也需要根据情况对之进行类型转换,如函数调用得到的地址要进行以下转

44、换:p1=(char *)fun(c1,c2);结构的定义结构是若干相关数据项的集合,它们的类型可以不同,为了便于统一管理才把它们组织在一起。中的结构类型,类似于其它高级语言中的记录,它把同一事物的若干相关的数据组成一个整体,统一进行管理。“结构”是一种构造类型,它由若干个“成员”组成。每一个成员可以是一个基本数据类型或者又是一个构造类型,在说明或使用它之前必须先定义它。一、结构的定义定义结构的一般形式为:struct 结构类型名成员表列;成员表列由若干成员组成,每个成员都是该结构的一个组成部分,每个成员也必须作类型说明,形式为:类型说明符 成员名;成员名的命名应符合标识符的书写规定。姓 名专

45、 业性 别 年 龄 成 绩(字符数组) (字符数组) (字符) (整形) (实型)struct djbchar name20; char spec16; char sex; int age; float score; /*定义结构类型成员*/结构定义后即可进行变量说明。凡被说明为结构的变量都由相同的成员组成。由此可见,结构是一种复杂的数据类型,是数目固定、类型不同的若干有序变量的集合。还应注意结构类型和基本数据类型的区别。结构类型定义中的每一个项,表示该结构的分量或称“域”,它们并不是变量。因此在一个函数中,允许另外定义与结构类型成员相同名的变量,它们各自代表不同的对象。基本数据类型是一个具体

46、的数据类型,一旦定义后,其说明的变量就被分配固定的字节,按指定的形式存放。而“结构类型”只是一个抽象的数据类型,只表示了“由若干不同类型数据项组成的复合类型”,由那些项组成,占多少字节等信息。与基本数据类型不同,系统没有预先定义结构类型,凡需使用结构类型数据的,都必须在程序中先加以定义。定义一个结构类型后,系统并没有为所定义的各项分配相应的存储空间,这是因为定义的是类型而不是变量。定义一个类型只是说明该类型的结构,即告诉系统它由哪些类型的成员构成,各占多少字节,按什么形式存储,并把它们当成一个整体处理。只有在定义了变量后,系统才为所定义的变量分配相应的存储空间。二、结构变量的定义:可采用以下三

47、种方法:1.先进行结构类型的定义,然后再进行结构变量的定义。定义形式为:struct 结构类型名 结构变量名表;如:struct djb stud1,stud2;说明:一旦进行了结构变量的定义后,系统将根据组成该变量名成员的不同类型,分配相应的存储空间。结构变量名表的末尾必须带有一个分号,用以表示变量表结束。定义一个结构类型变量与定义一个标准类型变量不同,定义结构类型变量时不仅要求指定变量为结构类型,而且还要指定为某一特定的结构类型。为使用方便,通常用一个符号常量代表一个结构体类型,在程序开头,如用:#define STUDENT struct student这样在程序中,STUDENT与st

48、ruct student完全等效。定义一个结构类型后,就可多次用来定义各种具体的结构类型变量。2.在定义结构类型的同时定义结构变量,形式为: struct 结构类型名 成员表列; 结构变量名表;该定义方式较为紧凑,既定义了类型,又定义了变量。如果还需要定义其它同类型的结构变量,还可用方法一继续定义变量。3.直接定义结构类型变量,形式为: struct 成员表列; 结构变量名表;与方法二相比,省略了结构类型名,因此不能用它再定义其他变量。注:类型和变量是不同的概念,不能混淆。在定义一个结构变量时,应先定义其类型,然后再定义变量为该类型。变量只有经过定义后,才能对其进行赋值、存取或运算操作;不能对

49、一个类型进行上述操作。系统只能给变量分配存储空间,不会给类型分配存储空间。对结构类型中的成员项(或称“域”、“结构分量”)可以单独使用,其作用相当于基本变量。允许结构类型中的成员仍然是结构变量。成员名可以与程序中的变量名相同,二者不代表同一对象。name spec sexbirthdayscoremnoth day yearstruct dateint month; int day; int year;struct djbchar name20; char spec16; char sex; struct date birthday; float score;stud1,stud2;一个结构体

50、变量的指针就是该变量所占据的内存段的起始地址。一、结构指针的定义:一指针变量用来指向一个结构变量时,称为结构指针变量。结构指针变量中的值是所指向的结构变量的首地址。通过结构指针变量可访问该结构变量。结构指针在程序中定义的形式为:struct 结构类型名 *结构指针名;其中结构类型名必须是已经定义过的结构。结构指针的定义了它的数据特性,并为结构指针本身分配了一定的存储空间。结构指针变量也必须先赋值给该指针变量,如:pdjb=&stud1 /*将stud1的首地址赋给pdjb*/不能把结构名赋值给该指针变量,因结构名只表示一种结构形式,编译系统不对它分配内存空间。只有当某变量被说明为这种类型时,才

51、能该变量分配存储空间。二、结构变量指针:用结构变量指针可方便地访问结构变量的各个成员,其访问的一般形式为:(*结构指针变量).成员名或为:结构指针变量-成员名或为:结构变量.成员名以上三种用于表示成员的形式完全等效。其中-称指向运算符。如:(*pdjb).score 或:pdjb-score 或 stud1.score注:(*pdjb)两侧括号不可少,因为成员符“.”的优先级高于“*”。分析以下几种运算:p-n 得到p指向的结构体变量中成员n的值p-n+ 得到p指向的结构体变量中成员n的值,用完该值后使它加1+p-n 得到p指向的结构体变量中成员n的值使之加1(先加)一、结构类型变量的初始化在

52、结构类型变量说明的同时,可给它的每个成员项赋初值,称为结构类型变量的初始化。但只有当结构变量为全局变量或静态变量时,才能进行初始化;对局部或自动结构变量不能初始化。1.对外部存储类型的结构变量进行初始化在初始化时,按照所定义的结构类型的数据结构,依次写出各成员的初始值,在编译时,将它们赋给变量中各成员。如:struct djb stud1=Wang Jian,computer,M,19,96.5;2.对静态存储类型的结构变量进行初始化注:初始化是指对结构变量赋初值,而不是对结构类型中的各成员项赋值。如一个结构类型内又嵌套另一个结构类型,则对该结构变量初始化时,仍按顺序写出各个初始值。过去许多版

53、本规定,只有当结构体变量为全局变量或静态变量时,才能进行初始化,不能对动态局部变量进行初始化。过去许多版本规定,自动变量不能在定义时赋初值,只能在函数执行时用赋值语句对各成员分别赋值。新标准无此限制,允许对自动变量初始化。二、结构类型变量的引用结构由不同数据类型的若干个数据集合而成。在程序中一般不允许将所使用的结构体作为一个整体参加操作处理,而是通过对结构的各个成员项的引用来实现各种运算和操作。1.引用结构变量中的一个成员引用形式为:结构变量名.成员名其中,圆点符号称为成员(分量)运算符。如成员本身又是一个结构,则必须逐级找到最低级的成员才能使用。如:stud1.birthday.month注

54、:如定义stud1,stud2为同一结构类型的两个变量,当引用这两个变量的age成员时,应分别用stud1.age和stud2.age,它们表示内存中不同的存储单元并且有各自不同的值。如一个结构中又嵌套另一个结构,则访问一个成员时,应采取逐级访问的方法,要用若干成员运算符,一级一级地找到最低一级的成员,只能对最低级的成员进行赋值或存取以及运算。允许对结构变量的成员进行相应的运算,但运算的规则应遵循定义结构时各成员项类型的规则。2.结构类型变量的整体引用可将一个结构变量作为一个整体赋给另一个同类型的结构变量。如:stud2=stud1;注:不允许将一组常量直接赋给一个结构变量,如下列写法是错误的

55、:stud2=Li Ming,computer,F,19,91;3.结构变量的输入和输出C不允许把一个结构变量作为一个整体进行输入输出操作,在进行结构变量的输入输出时,必须指明结构变量所对应的各成员项。注:可引用成员的地址,也可引用结构体变量的地址。如: scanf(%d,&stud1.num) (输入stud1.num)的值 printf(%o,&stud1) (输出stud1的首地址)但不能用以下语句整体读入结构体变量,如: scanf(%d,%s,%c,%d,%f,%s,&stud1);结构体变量的地址主要用于作函数参数,传递结构体的地址。 数组元素也可是结构类型,因此可构成结构型数组。

56、结构数组的每个元素都是具有相同结构类型的下标结构变量。常用结构数组表示具有相同数组结构的一个群体。一、结构数组的定义有三种定义方法:1.先进行结构类型定义后再定义结构数组,如: struct djb ; struct djb stud40;以上定义了一结构数组,由40个元素组成,每个元素类型都是struct djb。该数组在内存中占有一段连续的存储空间。2.同时定义结构类型和结构数组,如: struct djb stud40;3.直接定义结构数组而不需定义结构类型名,如: struct stud40;数组中各元素在内存中连续存放。二、结构数组的初始化规定,对全局或静态存储类型的结构数组,可在引

57、用前进行初始化,即对各个变量中的各个元素赋初始值。如: struct djb stud2= Wang Jina,computer,M,19,96.5, Xu Dong,computer,M,20,89.5;在编译时,将每个内层大括号中的数组,依次赋给所定义数组的每一个元素。由于在编译时,系统会根据给出初值的结构常量的个数,自动确定数组元素的个数,因此在定义数组时,允许元素个数不指定,既可写成:stud=,;也可先定义结构类型,然后定义数组为该结构类型,并在定义数组的同时进行初始化。如: struct djb ; struct djb stud=,;从以上可看出,结构体数组初始化的一般形式是在定

58、义数组的后面加上:初值表列;三、结构数组的引用由于结构数组元素相当于结构变量,因此引用结构变量的方法同样适用于结构数组元素。1.结构数组元素中某一成员的引用,如:stud1.age2.结构数组元素的赋值:可以将一个结构数组元素赋值给具有同一类型的变量,或赋值给具有同一结构类型的数组中另一个元素。如:stud=stud0;stud4=stud2;3.结构数组元素的输入输出:规定只能将数组元素的单个成员项进行输入输出,不能把结构数组元素作为一个整体进行输入输出。注:不要用一个scanf函数输入不同类型数据。对于结构数组元素的输入操作通常采用gets字符串输入函数。此函数多键盘接收字符串,直到按回车

59、。然后使用atoi、atof、atol(包含在头文件stdlib.h中)函数转换成相应类型的数据。四、结构数组指针一个指针变量可指向一个结构数组,即将该数组的起始地址赋给指针变量。注:如p指向第一个元素,则p+1后指向下一个元素的起始地址。指针p已定义为指向结构体类型的数据,它只能指向一个结构体型数据,而不能指向一元素中的某一成员(即p的地址不能是成员的地址)。如下面写法是不对的:p=&stu.num不能认为:反正p是存放地址的,可将任何地址赋给它。如果地址类型不相同,可用强制类型转换。如:p=(struct student *)&stu.num);规定,不仅可使用指向结构变量的指针作为函数的

60、参数,而且可直接用结构变量作函数的参数。当使用结构变量作函数的参数时,必须将实参结构变量的全部成员逐个传递给形式的结构变量。当成员是数组时,会使传送的时间和空间开销增大,降低程序效率。因此最好使用指针,用指针变量作函数参数进行传送。一、结构变量作函数的参数用结构变量作函数的参数时,参数的传递属于“值传递方式”。此时系统为形参单独开辟了一个临时的存储空间用以存放从实参传递过去的各成员项的值。二、结构变量指针作函数的参数由于只传递一个地址而不是对应结构数组的所有成员项值,所以比直接使用结构变量或结构数组作参数节省内存和时间开销。要将一个结构变量的值传递给另一个函数,有三种方法:用结构变量的成员作参

温馨提示

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

评论

0/150

提交评论