1、,指针与引用,本章主要知识点(1) 关于指针的C程序实例 (2) 指针 (3)指针与数组 (4) 指针与函数,2. C程序实例(2),1. C程序实例(1),1.1 C程序实例,1.1 C程序实例(1),1.C程序实例1,【例】指针变量的定义示例。 /*程序功能:验证指针变量的定义*/main() int num_int=12, *p_int; /*定义一个指向int型数据的指针变量p_int */float num_f=3.14, *p_f; /*定义一个指向float型数据的指针变量p_f */char num_ch=p, *p_ch; /*定义一个指向char型数据的指针变量p_ch *
2、/p_int= 程序运行结果: num_int=12, *p_int=12 num_f=3.14, *p_f=3.14 num_ch=p, *p_ch=p,1.1 C程序实例(2),2.C程序实例2,【例】输入2个整数,按升序(从小到大排序)输出。 /*程序名为l5_2.cpp */ /*程序功能:使用指针变量求解2个整数的升序输出*/ main() int num1,num2;int *num1_p= 程序运行情况: Input the first number:9 Input the second number:6 num1=9, num2=6 min=6, max=9,2.2 指向变量的
3、指针变量,2.1 地址和指针的概念,2 指针,2.1 地址和指针的概念,1.内存地址内存中存储单元的编号2.变量地址系统分配给变量的内存单元的起始地址,(1)计算机硬件系统的内存储器中,拥有大量的存储单元(每个存储单元容量为字节)。 为了方便管理,必须为每一个存储单元编号,这个编号就是存储单元的“地址”。每个存储单元都有一个惟一的地址。 (2)在地址所标识的存储单元中存放数据。 注意:内存单元的地址与内存单元中的数据是两个完全不同的概念。,假设有这样一个程序: main() int num;scanf(“%d“, C编译程序编译到该变量定义语句时,将变量num 登录到“符号表”中。符号表的关键
4、属性有两个:一是“标识符名(id)” ,二是该标识符在内存空间中的“地址(addr)” 。 为描述方便,假设系统分配给变量num的2字节存储单元为 3000 和3001,则起始地址3000就是变量num在内存中的地址。,2.1 地址和指针的概念,3.变量值的存取通过变量在内存中的地址进行,系统执行“scanf(”%d“,”时,存取变量num值的方式可以有两种: (1)直接访问直接利用变量的地址进行存取上例中scanf(“%d”,&num)的执行过程是这样的:用变量名num作为索引值,检索符号表,找到变量num的起始地址3000;然后将键盘输入的值(假设为)送到内存单元3000和3001中。pr
5、intf(“num=%dn“,num)的执行过程,与scanf( )很相似:首先找到变量num的起始地址3000,然后从3000和3001中取出其值,最后将它输出。,2.1 地址和指针的概念,(2)间接访问通过另一变量访问该变量的值语言规定:在程序中可以定义一种特殊的变量(称为指针变量),用来存放其它变量的地址。例如,假设定义了这样一个指针变量num_pointer,它被分配到4000、4001单元,其值可通过赋值语句“num_pointer=num;”得到。此时,指针变量num_pointer的值就是变量num在内存中的起始地址3000。,6.2.2 指向变量的指针变量,1.指针与指针变量,
6、(1)指针即地址一个变量的地址称为该变量的指针。通过变量的指针能够找到该变量。 (2)指针变量专门用于存储其它变量地址的变量 指针变量num_pointer的值就是变量num的地址。指针与指针变量的区别,就是变量值与变量的区别。 (3)为表示指针变量和它指向的变量之间的关系,用指针运算符“*”表示。 例如,指针变量num_pointer与它所指向的变量num的关系,表示为: *num_pointer,即*num_pointer等价于变量num。 因此,下面两个语句的作用相同: num=3; /*将3直接赋给变量num*/ num_pointer= /*将3赋给指针变量num_pointer所指
7、向的变量*/,2.2 指向变量的指针变量,2.指针变量的定义与应用 程序中头三行的变量定义语句指针变量的定义与一般变量的定义相比,除变量名前多了一个星号“*” (指针变量的定义标识符)外,其余一样: 数据类型 *指针变量, *指针变量2;注意:此时的指针变量p_int、p_f、p_ch,并未指向某个具体的变量(称指针是悬空的)。使用悬空指针很容易破坏系统,导致系统瘫痪。 中间三行的赋值语句取地址运算() 取地址运算的格式: 变量 例如, 则表示指针p为空,没有指向任何对象。,3.2 通过指针引用数组元素,3.1 指向数组元素的指针,3 指针与数组,3.3 数组名作函数参数,3.4 指针与字符数
8、组,3.1 指向数组元素的指针,指向数组的元素指针变量的定义,与指向普通变量的指针变量的定义方法一样 例如,int array10; ( 定义array为包含10个整型变量的数组)int *pointer (定义pointer为指向整型变量的指针变量) 应当注意,如果数组为int 型,则指针变量亦应指向int 型。下面是对该指针赋值: pointer= ”的作用是“把array数组的第一个元素的地址赋给指针变量pointer”而不是“把array数组各元素的值赋给pointer”。,3.2 通过指针引用数组元素,1 通过指针引用一维数组中的元素,如果有“int array10,*pointer
9、=array;” ,则: (1)pointer+i和array+i都是数组元素arrayi的地址。 (2)*(pointer+i)和*(array+i)就是数组元素arrayi。 (3)指向数组的指针变量,也可将其看作是数组名,因而可按下标法来使用。例如,pointeri等价于*(pointer+i)。 注意:pointer+1指向数组的下一个元素,而不是简单地使指针变量pointer的值+1。其实际变化为pointer+1*size(size为一个元素占用的字节数)。 例如,假设指针变量pointer的当前值为3000,则pointer+1为3000+1*2=3002,而不是3001。,3.
10、2 通过指针引用数组元素,【例】 使用指向数组的指针变量来引用数组元素。 main() int array10, *pointer=array, i; printf(“Input 10 numbers: ”);for(i=0; i10; i+)scanf(“%d”, pointer+i); /*使用指针变量来输入数组元素的值*/printf(“array10: ”);for(i=0; i10; i+)printf(“%d ”, *(pointer+i); /*使用指向数组的指针变量输出数组*/printf(“n”); ,3.2 通过指针引用数组元素,程序运行情况: Input 10 numbe
11、rs: 0 1 2 3 4 5 6 7 8 9 array10: 0 1 2 3 4 5 6 7 8 9 程序说明: 程序中第3行和第6行的2个for语句,等价于下面的程序段: for(i=0; i10; i+,pointer+)scanf(“%d”,pointer); printf(“array10: ”); pointer=array; /*使pointer重新指向数组的第一个元素*/ for(i=0; i10; i+,pointer+)printf(“%d”,*pointer);,3.2 通过指针引用数组元素,2 通过指针引用二维数组中的元素在C语言中,二维数组是按行优先的规律转换为一维
12、线性存放在内存中的,因此,可以通过指针访问二维数组中的元素。如果有:int aMN;则将二维数组中的元素aij转换为一维线性地址的一般公式是:线性地址aiMj其中:a为数组的首地址, M和N分别为二维数组行和列的元素个数。,3.2 通过指针引用数组元素,【例】:给定某年某月某日,将其转换成这一年的第几天并输出。此题的算法很简单,若给定的月是i,则将1、2、3、i-1月的各月天数累加,再加上指定的日。但对于闰年,二月的天数29天,因此还要判定给定的年是否为闰年。为实现这一算法,需设置一张每月天数列表,给出每个月的天数,考虑闰年非闰年的情况,此表可设置成一个2行13列的二维数组,其中第1行对应的每
13、列(设112列有效)元素是平年各月的天数,第2行对应的是闰年每月的天数。程序中使用指针作为函数day_of_year的形式参数。 #include main( ) static int day_tab213= 0,31,28,31,30,31,30,31,31,30,31,30,31, 0,31,29,31,30,31,30,31,31,30,31,30,31 ; int y, m, d; scanf(“%d%d%d“, ,3.2 通过指针引用数组元素,printf(“%dn“, day_of_year(day_tab,y,m,d) ); /* 实参为二维数组名 */ day_of_year(
14、day_tab,year,month,day) int *day_tab; /* 形式参数为指针 */ int year, month, day; int i, j; i = (year%4=0 由于语言对于二维数组中的元素在内存中是按行存放的,所以在函数day_of_year 中要使用公式“day_tab+i*13+j“计算main函数的day_tab中元素对应的地址。,3.3 数组名作函数参数,数组名可以用作函数的形参和实参。如: main ( ) int array10 ; f ( array , 10 ) f ( arr , n ) int arr , n ; array 为实参数组名
15、,arr 为形参数组名。,3.3 数组名作函数参数,【例】将数组a 中n 个整数按相反顺序存放解此题的算法为:将a 0与an-1 对换,再将a1与an-2对换, ,直到将a(n-1)/2 与a n-int(n-1)/2) 对换。今用循环处理此问题,设两个“位置指示变量”i 和j , i 的初值为0 , j 的初值为n-1。将ai与aj 交换,然后使i 的值加1 , j 的值减1 ,再将ai与aj对换,直到i =(n-1)/2 为止。 程序如下: void inv(int x,int n) /*形参x是数组名*/ int temp,i,j,m=(n-1)/2;for(i=0;i=m;i+) j=
16、n-1-i; temp=xi;xiI=xj;xj=temp; return; main() int i,a10=3,7,9,11,0,6,7,5,4,2; for(i=0;i10;i+),3.3 数组名作函数参数,printf(“%d,”,ai); printf(“n”); inv(a,10); for(i=0;i 2,10,7,4,9,5,0,11,8,3,,3.3 数组名作函数参数,小结:引入指向数组的指针变量后,数组及指向数组的指针变量作函数参数时,可有种等价形式(本质上是一种,即指针数据作函数参数): (1)形参、实参都用数组名 (2)形参、实参都用指针变量 (3)形参用指针变量、实参
17、用数组名 (4)形参用数组名、实参用指针变量,3.4 指针与字符数组,1. 字符串的表示与引用 在语言中,既可以用字符数组表示字符串,也可用字符指针变量来表示;引用时,既可以逐个字符引用,也可以整体引用。 (1)逐个引用 【例】 使用字符指针变量表示和引用字符串。 main() char *string=”I love Beijing.”; for(; *string!=0; string+) printf(“%c”, *string);printf(“n”); 程序运行结果:I love Beijing.,3.4 指针与字符数组,(2)整体引用 【例】采取整体引用的办法,改写例5.9。 ma
18、in() char *string=”I love Beijing.”; printf(“%sn”,string); 注意:其它类型的数组,是不能用数组名来一次性输出它的全部元素的,只能逐个元素输出。,3.4 指针与字符数组,2.字符指针变量与字符数组之比较 虽然用字符指针变量和字符数组都能实现字符串的存储和处理,但二者是有区别的,不能混为一谈。 (1)存储内容不同。 字符指针变量中存储的是字符串的首地址,而字符数组中存储的是字符串本身(数组的每个元素存放一个字符)。 (2)赋值方式不同。 对字符指针变量,可采用下面的赋值语句赋值:char *pointer;pointer=“This is
19、a example.“; 而字符数组,虽然可以在定义时初始化,但不能用赋值语句整体赋值。下面的用法是非法的:char char_array20;char_array=“This is a example.“; /*非法用法*/ (3)指针变量的值是可以改变的,字符指针变量也不例外;,3.4 指针与字符数组,3. 字符串指针作函数参数 【例】 用函数调用方式,实现字符串的复制。 /*/ /*string_copy()函数:复制一个字符串*/ /*形参:字符指针str_from接收源串,字符指针 str_to存储目标串 */ /*返回值:无*/ /*/ void string_copy(char *str_from, char *str_to) int i=0;for(; (*(str_to+i)=*(str_from+i)!=0; i+) ; /*循环体为空语句*/ main() char array_str120=”I am a teacher.”;char array_str220;,3.4 指针与字符数组,string_copy(array_str1, array_str2) ; /*数组名作实参*/ printf(“array_str2=%sn”, array_str2); 程序运行结果:I am a teacher.,