指针

来源:互联网 发布:福利彩票数据库 编辑:程序博客网 时间:2024/05/20 19:16
 

§9.1 指针的概念

指针(pointer):是一个变量的地址。

指针变量:是一个变量,其值是另一个变量的地址。

任何变量都在计算机内存中占有一块内存区域, 变量的值就存放在这块内存区域之中,(寄存器变量不在内存中,而是在CPU的寄存器中)。

们通过下面的图来了解一下这两个概念:

其中:2000为变量i的地址(指针) ,i_pointer用来存放变量i的地址(2000),所以i_pointer是一个指针变量.

一个变量的访问(访问是指取出其值或向它赋值)方式有两种:

(1)直接访问,通过变量名访问,如通过变量名i直接访问。

(2)间接访问,通过该变量的指针来访问,如通过i_pointer访问变量i。

  请看下图:

 

 

 

§9.2 变量的指针

9.2.1 指针变量的定义

 指针变量有三个属性:

(1)该指针变量指向的变量的类型。如i_pointer指向的变量i是整型。

(2)该指针变量在内存中占多少内存单元。如i_pointer占两个内存单元,称为“近指针”,用     near表示。如果该变量在内存中占4个内存单元,称为“远指针”,用far表示。如果未指定     near或far,缺省是near。(指针变量在内存中要么占2个内存单元,要么占4个内存单元)。

(3)该指针变量指向哪一个变量,即该指针变量的值是多少。如i_pointer的值是2000。

     指针变量定义的一般形式:

类型标识符 * 标识符

“*”      表示定义指针变量“标识符”   是指针变量名“类型标识符” 表示该指针变量所指向的变量类型。
   例:
int i,j; /* 定义两个整型变量 */int *pointer_1, *pointer_2; float *pointer_3; char *pointer_4; void *pointer_5; char far *pointer_6;
     指针变量的赋值.  例:
pointer_1 = &i; pointer_2 = &j;
    注意:指针变量中只能存放地址,不能将一个非地址类型的数据(如常数等)赋给一个指针变          量,  如:         pointer_1 = 100;
         也可以在定义指针变量的同时指定其初值,  如:
int a;int *p = &a;

 

 

 

 

 

 

 

9.2.2 指针变量的引用

 有两个运算符可以引用指针变量

(1)&:取地址运算符。如 pointer_1 = &i;

(2)*:指针运算符。用于访问指针变量所指向的变量。

如果定义:

int i,j;int *pointer_1;pointer_1 = &i;

指针变量pointer_1指向变量i,现在,对变量i有两种访问方式:

(1)直接访问。如 i = 100; j = i。

(2)通过指针变量间接访问。

      如:
*pointer_1 = 100; j = *pointer_1;

到这里为止,用指针变量间接访问另一个变量,似乎显得多余,但是,在复杂程序设计中

, 这种间接访问可以大大提高程序的效率并使程序非常简洁。因为,只要改变指针变量

的值, 就可以访问其他变量。 例:

int i,j;int *p;p = &i; (p指向i )*p = 100; (*p访问i)p = &j; (p指向j)*p = 200;(*p访问j
   [例9.1]
main(){int a,b; int *pointer_1, *pointer_2; /* 定义指针变量 */ a = 100; b = 10; pointer_1 = &a;  pointer_2 = &b; printf("%d,%d\n",a,b); printf("%d,%d\n",*pointer_1,*pointer_2);} 程序运行结果:  100,10 100,10
  说明:
  1、在定义指针变量时,还未规定它指向哪一个变量,此时不能用*运算符访问指针。只有在程序     中用赋值语句具体规定后,才能用*运算符访问所指向的变量。
int a;int *p; (未规定指向哪个变量)*p = 100;int a;int *p; (未规定指向哪个变量)p = &a; (规定指向a)*p = 100;这种错误称为访问悬挂指针(suspeded pointer)。 
  2、区分:*运算符在不同场合的作用,编译器能够根据上下文环境判别*的作用。
int a,b,c;int * p; (*表示定义指针)p = &a;*p = 100; (*表示指针运算符)c = a * b; (*表示乘法运算符)
  3、区分*运算符的以下用法:
int a ;int *p = &a; /* 定义指针变量时指定初值,是为p指定初值 */

*p = 100; /* 给指针p所指向的变量赋值,这里是给变量a赋值 */

关于*和&运算符的其它细节,自学教材pp160~161。

   [例9.2]  输入a和b两个整数,按先大后小的顺序输出a和b。

   运行结果:a=5,b=9

        max=9,min=5

该例不交换变量a、b的值,而是交换指针p1、p1的值。

 

 

 

 

9.2.3 指针变量作为函数参数

[例9.3] 题目要求同[例9.2],输入a和b两个整数,按先大后小的顺序输出a和b。

intswap(int *p1, int *p2) /*交换指针p1、p2所指向的变量的值 */{int p; p = *p1; *p1 = *p2; *p2 = p;}   main(){  int a, b; int *pointer_1, *pointer_2; scanf("%d,%d",&a,&b); pointer_1 = &a; pointer_2 = &b; if (a<b) swap(pointer_1, pointer_2); /* 另一种调用swap()的形式是: if (a<b) swap(&a,&b); */  printf("\n%d,%d\n",a,b);} 

1、程序执行过程的说明。

执行pointer_1 = &a; pointer_2 = &b后,pointer_1和pointer_2分别指向a和b。调用函数swap(pointer_1,pointer_2),生成两个形参p1和p2。实参pointer_1的值传送给形参p1,因此p1也指向a。同理,p2指向b。在swap()函数内,把*p1和*p2的值进行交换,*p1是变量a,*p2是变量b,即把a和b的值进行交换。函数swap()调用结束后,形参p1、p2被释放,main中得到的a和b是已经被交换的值。

2、使用指针变量,应注意避免指针悬挂。

intswap(int *p1, int *p2){int *p; *p = *p1; *p1 = *p2; *p2 = *p;} 

3、函数swap()的形参是指针变量,两种调用方式:

swap(pointer_1, pointer_2);/* 传值,这里的值是一个地址 */swap(&a, &b); /* 传地址 */
   均把变量a和b的地址传送给形参,均能实现交换a和b的值。
   只有函数swap()知道变量a和b的地址,才能改变其值(交换)。如果把swap()设计为下面的形式,不能实现   a和b的值交换(函数不知道a、b的地址)。
int swap(int x, int y) /* 该函数交换形参的值 */{int t; t = x; x = y; y = t;} 

该函数交换形参的值,不能实现实参值的交换, 因为在C语言中,实参和形参之间使用“传值法”,数据只能单向由实参传到形参。形参值的变化不影响实参。

4、以指针变量作函数的参数,实参和形参之间仍然使用“传值法”,数据只能单向由实参传到形参。形参值    的变化不影响实参,即,不能改变指针变量本身的值。但可以改变指针变量所指向的变量的值,例、
swap(pointer_1,pointer_2); swap(&a,&b);
   因此,下面的程序也不能达到交换的目的。
intswap(int *p1, int *p2) /* 交换形参的值 */{int *p; p = p1; p1 = p2; p2 = p;} main(){  int a,b; int *pointer_1, *pointer_2; scanf("%d,%d",&a, &b); pointer_1 = &a; pointer_2 = &b; if (a<b) swap(pointer_1, pointer_2); printf("\n%d,%d\n", *pointer_1, *pointer_2);} 
   不能达到交换的原因:swap()函数交换形参(指针变量)本身,而不是交换指向的变量。

[例9.4] 输入a、b、c三个整数,按大小顺序输出。

intswap(int *pt1, int *pt2){int p; p = *pt1; *pt1 = *pt2; *pt2 = p;} intexchange(int *q1, int *q2, int *q3){if (*q1 < *q2) swap(q1,q2); if (*q1 < *q3) swap(q1,q3); if (*q2 < *q3) swap(q2,q3);} main(){  int a,b,c, *p1, *p2, *p3; scanf("%d,%d,%d", &a, &b, &c); p1 = &a; p2 = &b; p3 = &c; exchange(p1,p2,p3); printf("\n%d,%d,%d\n", a,b,c);} 

 

 

 

§9.3 数组的指针和指向数组的指针变量

9.3.1 指向数组元素的指针变量的定义与赋值

      指针可以指向数组和数组元素,当一个指针指向数组后,对数组元素的访问,既可以使用数组下标,也可以使用指针。并且,用指针访问数组元素,程序的效率更高(用下标访问数组元素程序更清晰)。   所谓数组的指针是指数组的起始地址,数组元素的指针是数组元素的地址。
   指向数组元素的指针变量,其类型应与数组元素相同,例:
int a[10]; /* 元素为整型 */float b[10] ; /* 元素为实型 */int *p; /* 可以指向数组a的元素 */float *pf; /* 可以指向数组b的元素 */
   为了让指针p指向数组a,应把数组a的地址赋给指针变量p。

在§7.7中,已学过:数组名a表示该数组在内存的起始地址。可以用地址运算符&获得某个元素的地址。如&a[2]获得元素a[2]的地址。第一个元素a[0]的地址&a[0]即为数组a的起始地址。

   因此,以下语句均使指针p指向数组a:
p = &a[0];p = a; /* 把数组a的起始地址赋给p,不是把数组的全部元素赋给p */

 

 

 

 

 

 

9.3.2 通过指针引用数组元素

 当使指针p指向数组a后,可以用指针p访问数组的各个元素。

如果指针p指向数组a(指向数组的第一个元素a[0]),则:

p+1指向下一个元素a[1],注意不是将p值简单加1。如果数组元素是整型,p+1表示p的地址加2;如果数组元素是实型,p+1表示p的地址加4;如果数组元素是字符型,p+1表示p的地址加1。

p+i指向元素a[i]。可以使用*(p+i)访问元素a[i]。

另外:

1、p+i也可以记作a+i。指向元素a[i]。

2、指向数组的指针变量也可以带下标,如,p[i]与*(p+i)等价,表示元素a[i]。

[例9.5] 输出数组的全部元素。(设10个元素,整型)。

访问各元素有三种方法:

1、下标法(常用,很直观)

main(){  int a[10]; int i; for(i=0;i<10;i++)    scanf("%d", &a[i]); printf("\n"); for(i=0;i<10;i++)   printf("%d ",a[i]);} 

2、用数组名计算数组元素的地址。(效率与下标法相同,不常用)

main(){  int a[10]; int i; for(i=0;i<10;i++)    scanf("%d", &a[i]); printf("\n"); for(i=0;i<10;i++)   printf("%d ",*(a+i));} 

3、用指针访问各元素。(常用,效率高)

main(){  int a[10]; int *p, i; for(i=0;i<10;i++)    scanf("%d", &a[i]); printf("\n"); for(p=a;p<(a+10);p++)/* p++使p指向下一个元素 */    printf("%d ",*p);} 

使用指针指向数组,应注意以下问题:

1、若指针p指向数组a,虽然p+i与a+i、*(p+i)与*(a+i)意义相同,但仍应注意p与a的区别(a代表数组的首地址,是不变的;p是一个指针变量,可以指向数组中的任何元素),例、

for(p=a; a<(p+10); a++)a代表数组的首地址,是不变的,a++不合法printf("%d", *a)

2、指针变量可以指向数组中的任何元素,注意指针变量的当前值。

[例9.6] 输出数组a的10个元素。

程序:

main(){  int *p, i, a[10]; p = a; for(i=0;i<10;i++)   scanf("%d", p++); printf("\n"); for(i=0;i<10; i++,p++)   printf("%d", *p);} main(){  int *p,i,a[10]; p = a; for(i=0;i<10;i++) scanf("%d", p++); printf("\n"); p = a; for(i=0;i<10; i++,p++) printf("%d",*p);} 

3、 使用指针时,应特别注意避免指针访问越界。在上例中,第二次for循环,p已经越过数组的范围,但编译器不能发现该问题。避免指针访问越界是程序员自己的责任。

4、指针使用的几个细节。

设指针p指向数组a(p=a),则:

① p++(或 p += 1),p指向下一个元素。

② *p++,相当于*(p++)。因为,*和++同优先级,++是右结合运算符。

③ *(p++)与*(++p)的作用不同。

*(p++):先取*p,再使p加1。*(++p):先使p加1,再取*p。

④ (*p)++表示,p指向的元素值加1。

⑤ 如果p当前指向数组a的第i个元素,则:

*(p--)相当于a[i--],先取*p,再使p减1。*(++p)相当于a[++i],先使p加1,再取*p。*(--p)相当于a[--i],先使p减1,再取*p。

 

 

9.3.3 数组名作函数参数

    数组名代表数组首地址,因此,它作实参在函数调用时,是把数组首地址传送给形参。这样,实参数组和形参数组共占同一段内存区域。从而在函数调用后,实参数组的元素值可能会发生变化

[例9.7] 将数组a中n个元素按相反顺序存放。

算法:a[0]与a[n-1]交换,a[1]与a[n-2]交换,.....,a[(n-1)/2]与a[n-int((n-1)/2)]交换。

实现:用i,j作元素位置变量,开始i=0,j=n-1。将a[i]与a[j]交换,然后i加1,j减1,直到i=(n-1)/2。

程序:

voidinv(int x[], int n) /* 形参是数组 */{  int t,i,j,m=(n-1)/2; for(i=0; i<=m; i++) { j = n - 1 - i;   t = a[i]; a[i] = a[j]; a[j] = t; } return; /* 函数的返回值类型是void,不返回任何值 */} main(){static int i, a[10] = {3,7,9,11,0,6,7,5,4,2}; printf("the original array:\n"); for(i=0; i<10; i++)    printf("%d ", a[i]); printf("\n"); inv(a,10); printf("the array hans been inverted:\n"); for(i=0; i<10; i++)    printf("%d ", a[i]); printf("\n");} 

函数inv()可以用指针作形参,运行情况与用数组作形参相同。

void inv(int *x, int n){  int *p, t, *i, *j, m=(n-1)/2; i = x; /* 指针i指向数组第一个元素 */ j = x + n - 1;/* 指针j指向数组最后一个元素 */ p = x + m; /* 指针p指向数组中间一个元素 */ for(;i<=p;i++,j--)  { t = *i; *i = *j; *j = t; } return;} 
[例9.8] 从10个数中找出其中最大值和最小值。只找出其中最大值和最小值,不能改变元素的排         列顺序)。

方法1、实参和形参均用数组变量。

int max, min; /* 全局变量,最大值和最小值*/  voidmax_min_value(int array[], int n){int *p, *array_end; /* p是数组元素指针 */ array_end = array + n; /* 指向数组尾*/ max = min = *array; /* 第一个元素array[0] */ for(p=array+1; p<array_end; p++) /* p++指向下一个元素 */   if (*p > max) max = *p;   else if (*p < min) min = *p; return;} main(){int i, number[10]; printf("enter 10 data\n"); for(i=0;i<10;i++)   scanf("%d",&number[i]); max_min_value(number,10); printf("\nmax=%d,min=%d\n",max,min);} 

方法2、形参和实参均使用指针变量。

int max, min; /* 全局变量,最大值和最小值*/  voidmax_min_value(int *array, int n){int *p, *array_end; /* p是数组元素指针 */ array_end = array + n; /* 指向数组尾 */ max = min = *array; /* 第一个元素array[0] */ for(p=array+1; p<array_end; p++) /* p++指向下一个元素 */   if (*p > max) max = *p;   else if (*p < min) min = *p; return;} main(){int i, number[10],*p; p = number; /* 指针p指向数组number首地址 */ printf("enter 10 data\n"); for(i=0;i<10;i++)   scanf("%d",&number[i]); printf("the 10 data:\n"); for(p=number,i=0; i<10; i++,p++) printf("%d ", *p); p = number;/* for循环后,p指向数组尾,因此应为p重新赋值 */ max_min_value(p,10); printf("\nmax=%d,min=%d\n",max,min);} 

小结:数组作函数的参数,实参和形参之间传送数组的首地址,首地址可以用指针表示,也可以用数组名表示,因此,实参和形参有以下四种组合情况。

组合情况实参形参1数组名数组名2数组名指针3指针指针4指针数组名

请自行阅读教材pp175~179。

 

 

 

 

 

9.3.4 多维数组的指针

二维数组:

static int a[3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}};

理解为: 有三个元素a[0]、a[1]、a[2],每一个元素代表一行,每一个元素是一个包含4个元素的数组。

数组名a代表:整个二维数组的首地址,也是元素a[0][0]的地址,同时代表第一行元素的首地址。

a+1表示第二行元素的首地址,也是元素a[1][0]的地址。

a+2表示第三行元素的首地址,也是元素a[2][0]的地址。

设数组的首地址是2000,则有a等于2000。第一行4个元素,占8字节,因此第二行的首地址是2008,即a+1等于2008。第二行4个元素,占8字节,因此第三行的首地址是2016,即a+2等于2016。

由于把a[0]、a[1]、a[2]看成一维数组,它们代表各自数组的首地址,即:

a[0]~&a[0][0] (~表示“相当于”)a[1]~&a[1][0]a[2]~&a[2][0]

根据一维数组的表示方法,有:

a[0]+1:表示一维数组中第二个元素,~&a[0][1]a[0]+2: ~&a[0][2]a[1]+1: ~&a[1][1];

综上所述,二维数组a的地址用下图说明(教材p180,图9.25):

已知某元素的指针后,可以用*运算符访问该元素。例:

*(a[1]+2) = a[1][2] = 13

关于二维数组各种指针表示法,仅要求到此,教材p180第三行~p187[本节尾],除[例9.12]外,不要求。(讲述二维数组元素的另一种表述)。

 

[例9.12] 用指针变量输出数组元素的值。

main() {static int a[3][4] = {1,3,5,7,9,11,13,15,17,19,21,23}; int *p;   for(p=a[0]; p<a[0]+12; p++)  {   if ( (p-a[0])%4 == 0) printf("\n");   printf("%4d", *p); }} 
注意:本例用指针顺序访问二维数组的元素。若需访问二维数组a[n][m](n行m列)的某个元素      a[i][j],计算该元素的相对位置公式为:

i*m+j (i,j=0,1,2, ...)

这种方法相当于把二维数组转化为一维数组来使用。

比较直接用二维数组下标访问元素:

main(){static int a[3][4] = {1,3,5,7,9,11,13,15,17,19,21,23}; int i,j; for(i=0; i<3; i++) { for(j=0;j<4;j++) printf("%4d",a[i][j]); printf("\n"); }} 

这种方式虽然清晰,但需进行两层循环,且为了计算每一个元素a[i][j]的位置,均进行i*4+j的运算,执行效率非常低。

 

 

 

 

 

 

 

§9.4 字符串的指针

9.4.1 字符串的表现形式

C语言中,有两种方式可以实现字符串:1.用字符数组实现;2.用字符指针实现。

字符数组[例9.16]字符指针[例9.17]main(){ static char string[ ] =
        "I love China!";
 printf("%s\n",string);}main(){ char *string ="I love China!"; printf("%s\n",string);}

string是数组名,代表字符数组的首地址。数组可以用下标访问,也可以用指针访问。例:string[4]表示一个元素其值是字符v,也可以用*(string+4)来访问,string+4是指向字符v的指针。

string是一个指针变量,“I love China!"是一个字符串常量。语句:
char *string = "I love China!";等价于

 char * string; string = "I love China!";

它把字符串常量的首地址赋给指针string.不能理解为把字符串常量赋值给指针变量。

*string ="I love China!";

 

从以上两个例子中,可以看到:

1、字符数组和字符指针的概念不同。

2、字符指针指向字符串,而C语言中,字符串按数组方式处理,因此,字符数组和字符指针的访问方式相同。例如,均可以使用%s格式控制符进行整体输入输出。但应注意,如果不是字符数组,而是整型、实型等数字型数组,不能用%s,只能逐个元素处理。

[例9.18] 将字符串a复制到字符串b。

main(){char a[] = "Iam a boy."; char b[20]; int i; for(i=0; *(a+i) !='\0'; i++)     *(b+i) = *(a+i); *(b+i) = '\0'; printf("string a is: %s\n",a);  printf("string b is:"); for(i=0; b[i] !='\0'; i++)     printf("%c",b[i]); printf("\n");} 

[例9.19] 将字符串a复制到字符串b。(用指针处理)

main(){char a[]="Iam a boy.", b[20], *p1, *p2; int i; p1 = a; p2 = b; for(; *p1 != '\0'; p1++, p2++)      *p2 = *p1; *p2 = '\0'; printf("string a is: %s\n", a); printf("string b is:"); for(i=0; b[i] !='\0'; i++)     printf("%c",b[i]); printf("\n");} 

 

 

9.4.3 字符指针变量与字符数组的区别

虽然字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,区别如下表所示:

 字符数组字符指针变量组成由若干元素组成,每个元素中放一个字符。2或4字节,存放字符串的首地址。赋初值方式static char str[]={"I love China!"};char *a = "I love China!";赋值方式char str[14];str = "I love China!" char * a;a = "I love China!";占用内存字符数组一个元素占一字节内存,且在编译时分配。指针变量中只可以放一个地址值(近指针=2字节,远指针=4 字节)。且编译时未指定。 char str[10];scanf("%s",str);char *a;scanf("%s",a);a尚未指向任何变量

自行阅读pp195[例9.21]~196的细节

 

 

 

§9.5 函数指针

9.5.1 用函数指针变量调用函数

  C语言中的指针,既可以指向变量(整型、字符型、实型、数组等),也可以指向程序的代码(如函数)。

  一个函数在编译时被分配一个入口地址(第一条指令的地址),这个入口地址称为函数的指针。如果一个指针变量的值等于函数的入口地址,称为指向函数的指针变量,简称为函数指针

可以通过函数指针来调用函数。

[例9.23] 求a和b中的大者。

用函数名调用函数max()用函数指针调用函数max()int max(int x, int y); /*原型*/  main(){  int a,b,c; scanf("%d,%d", &a, &b); c = max(a, b); printf("a=%d,b=%d,max=%d",a,b,c);} int max(int x, int y); /*原型*/  main(){int (*p)(int, int); int a,b,c; p = max; scanf("%d,%d", &a, &b); c = (*p)(a,b); printf("a=%d,b=%d,max=%d",a,b,c);} int max(int x, int y){  int z;  if (x>y) z = x;  else     z = y;  return z;}

1、函数指针定义的一般形式

  函数返回值类型 (*指针变量名)(形参类型 )

即:除函数名用(*指针变量名)代替外,函数指针的定义形式与函数的原型  相同。(在函数指针定义中加入形参类型是现代程序设计风格)。例:
  int (*p) (int,int);
 仅当形参类型是int时,可以省略形参类型,一般不要省略。(见例9.25)
  int (*p) ();

2、语句p=max,把函数max的入口地址赋给函数指针p,因此,c=(*p)(a,b)中 ,*p就是调用函数max。

 注意:

   语句p=max中,函数名代表函数的入口地址,max后不跟函数参数。

   用函数指针调用函数时,应指定实参。

3、(*p)()表示一个指向函数的指针变量,它可以先后指向不同的函数。

4、指向函数的指针变量p,象p++、p--、p+n等运算是无意义的。

 

 

 

 

 

 

 

 

§9.5 函数指针

9.5.1 用函数指针变量调用函数

  C语言中的指针,既可以指向变量(整型、字符型、实型、数组等),也可以指向程序的代码(如函数)。

  一个函数在编译时被分配一个入口地址(第一条指令的地址),这个入口地址称为函数的指针。如果一个指针变量的值等于函数的入口地址,称为指向函数的指针变量,简称为函数指针

可以通过函数指针来调用函数。

[例9.23] 求a和b中的大者。

用函数名调用函数max()用函数指针调用函数max()int max(int x, int y); /*原型*/  main(){  int a,b,c; scanf("%d,%d", &a, &b); c = max(a, b); printf("a=%d,b=%d,max=%d",a,b,c);} int max(int x, int y); /*原型*/  main(){int (*p)(int, int); int a,b,c; p = max; scanf("%d,%d", &a, &b); c = (*p)(a,b); printf("a=%d,b=%d,max=%d",a,b,c);} int max(int x, int y){  int z;  if (x>y) z = x;  else     z = y;  return z;}

1、函数指针定义的一般形式

  函数返回值类型 (*指针变量名)(形参类型 )

即:除函数名用(*指针变量名)代替外,函数指针的定义形式与函数的原型  相同。(在函数指针定义中加入形参类型是现代程序设计风格)。例:
  int (*p) (int,int);
 仅当形参类型是int时,可以省略形参类型,一般不要省略。(见例9.25)
  int (*p) ();

2、语句p=max,把函数max的入口地址赋给函数指针p,因此,c=(*p)(a,b)中 ,*p就是调用函数max。

 注意:

   语句p=max中,函数名代表函数的入口地址,max后不跟函数参数。

   用函数指针调用函数时,应指定实参。

3、(*p)()表示一个指向函数的指针变量,它可以先后指向不同的函数。

4、指向函数的指针变量p,象p++、p--、p+n等运算是无意义的。

 

 

§9.6 返回指针的函数

 一般形式:类型标识符 * 函数名(参数表) 例: int * a (int x, int y)   声明一个函数,函数名为a,其返回值类型是“指向整型的指针”,函数形式参数为int x 和 int y。   [例9.26]有若干学生的成绩(每个学生四门课程),要求用户在输入学生序号(从0开始)后          ,能输出该学生的全部成绩。
 分析:     设计一个指针pointer指向一个学生的四门成绩 

      float (*pointer)[4]

pointer是一个指向一维数组的指针。数组元素个数为4(四门课程)

pointer+1指向下一个学生的成绩。

输入学生序号后,使pointer指向该学生的成绩,然后返回pointer指针.

程序:

float * search( float (*pointer)[4], int n) ;

void main()
{
 static float score[][4] =
   {{60,70,80,90},{56,89,67,88},{34,78,90,66} };
 float *p;
 int i, m;

 printf("enter the number of student:");
 scanf("%d",&m);

 printf("The scores of No.%d are:\n", m);
 p = search(score, m); /* 在score数组中查询m号学生的成绩 */
            /* 查询结果为指向成绩的指针 */
 for(i=0; i<4; i++)
   printf("%5.2f\t",*(p+i));
}

float * search( float (*pointer)[4], int n)
{
 float *pt; /* pt是指向实数的指针,pointer是指向数组的指针 */
 pt = *(pointer+n); /* pt = (float *)(pointer + n) */
 return pt;
}

[例9.27]对上例中的学生,找出有不及格成绩的学生及其学号。

    分析:上例中,search函数返回学生成绩的首地址。本例,用返    回地址区分学生成绩中有无不及格课程,若有不及格课程时,    仍返回学生成绩的首地址;若无不及格课程,则返回学生成绩    的末地址(等于下一个学生的首地址)。

float * search( float (*pointer)[4] );

void main()
{
 static float score[][4] =
   {{60,70,80,90},{56,89,67,88},{34,78,90,66} };
 float *p;
 int i, j;

 for(i=0; i<3; i++) /* 三个学生 */
 {
  p = search(score+i);
  if (p == *(score+i)) /* p指向i号学生成绩首地址,该生有不及格课程 */
   {
     printf("No.%d scores:\t", i); /* 显示该生的四门课程成绩 */
     for(j=0; j<4; j++)
      printf("%5.2\t", *(p+j));
     printf("\n");
    }
  }
}

float * search( float (*pointer)[4] )
{
 int i;
 float *pt;
 pt = *(pointer + 1); /*先设pt指向成绩末地址(等于下一个学生成绩首地址),即先假设

            无不及格成绩 */
 for(i=0; i<4; i++) /* 查四门课程中有无不及格成绩 */
  {
   if ( *(*pointer+i)<60 ) pt = *pointer; /*有不及格课程,pt指向成绩首地址*/
   return pt;
  }
}

 

 

 

 

 

§9.7 指针数组和指向指针的指针

9.7.1 指针数组

概念:指针数组是一个数组,该数组中的每一个元素是指针变量。

形式:类型标识符 *数组名[数组元素个数]

例子: int * p[4];
  定义一个指针数组,数组名p,有4个元素, 每一个元素是指向整型变量的指针。

区分:int (*p)[4] (指向数组的指针)
  定义一个指针变量,它指向有4个元素的一维数组。

用途:处理多个字符串。
   字符串本身是一维数组,多个字符串可以用二维数组来处理,但会浪费许多内存。用指针数组处理多个字符串,不会浪费内存。

二维数组
char name[][16],
浪费许多内存
指针数组
char *name[],
不浪费内存

[例9.28]、将若干字符串按字母顺序(由小到大)输出。

#include "stdio.h"
#include "string.h"

void sort(char *name[], int n); /* 排序函数原型 */
void print(char *name[], int n); /* 输出函数原型 */

void main()
{
  static char *name[] =
  {"Follow me", "BASIC", "Great Wall", "FORTRAN", "Computer Design"};
  int n = 5;

  sort(name, n); /* 排序 */
  print(name, n); /* 输出 */

}

void sort(char *name[], int n) /* 冒泡法排序 */
{
 char *temp;
 int i, j, k;
 for(i=0; i<n-1; i++) /* n个字符串,外循环n-1次 */
  {
    k = i;
    for(j=i+1; j<n; j++) /* 内循环 */
     if (strcmp(name[k], name[j]) > 0) k = j;
      /* 比较name[k]与name[j]的大小,较小字符串的序号保留在k中 */
    if (k != i)
     { /*交换name[i]与name[k]的指向 */
       temp = name[i];
       name[i] = name[k];
       name[k] = temp;
      }

   }
}

void print(char *name[], int n)
{
 int i;
 for (i=0; i<n; i++)
  printf("%s\n", name[i]);
}

 

9.7.2 指向指针的指针

定义举例:

   char * * p;

p是一个指向指针的指针。被p指向的指针指向字符变量。   

char **p;char * pch;char ch;

使用举例:

字符指针数组char *name[]指向字符串。

定义指向指针的指针p:char **p, 使其指向name。

 p = name + 2;
 printf("%o\n", *p); /* 以八进制形式输出name[2]的值,
            它是字符串“Great Wall”的地址
*/
 printf("%s\n", *p); /* 输出name[2]指向的字符串 */

[例9.29]
void main()
{
  static char *name[] =
  {"Follow me", "BASIC", "Great Wall", "FORTRAN", "Computer Design"};
  char **p;
  int i;

  for(i=0; i<5; i++)
  {
   p = name + i;
   printf("%d %d %s\n", i, *p, *p);
   }
}

输出:
  0 414 Follow me
  1 424 BASIC
  2 430 Great Wall
  3 441 FORTRAN
  4 449 Computer Design

 

 

9.7.3 main()函数的参数

运行程序的命令行中,可以包含参数,例:

     命令名 参数1 参数2 ..... 参数n 

例如,“命令名”是可执行文件file1.exe,执行该命令时包含两个字符串参数:

     file1 China Beijing

在源程序file1.c中,用main()函数的参数来表示命令的参数,

   main(int argc, char argv[])

其中,argc表示命令行参数的个数(包括命令名),指针数组argv用于存放参数(包括命令 名),上例中:

   argc = 3;
   
argv[0] ="file1.exe", argv[1] ="China", argv[2] = "Beijing"

例:(设文件名是file1)
   main(int argc, char *argv[])
   {
    while(argc>1)
     {
      ++argv;
      printf("%s\n",*argv);
      --argc;
     }
   }

输入:file1 China Beijing
输出:China
   Beijing

该程序可改写为:

   main(int argc, char *argv[])
   {
     while( argc-- > 1)
      printf("%s\n", *++argv);
    }

例:echo(参数回送)命令的程序。

  main(int argc, char *argv[])    
  {
    while(--argc>0)
     printf("%s%c",*++argv, (argc>1)? ' ':'\n');
   }
或写为:

  main(int argc, char *argv[])
  {
    int i;
    for(i=1; i<argc; i++)
     printf("%s%c",argv[i], (i<argc-1)? ' ':'\n');

 

 

 

§9.8 指针使用小结

一、有关指针的数据类型

定义含义int i;定义整型变量iint *p;p是指向整型数据的指针变量int a[n];定义数组a,元素类型为int,元素个数是nint *p[n];p是指针数组,包含n个指针,每一个指针可以指向整型数据int (*p)[n];p是指向数组的指针,数组有n个整型数int f();f是函数,返回值是intint *p();p是函数,返回值是指针,该指针指向整型数据int (*p)();p是函数指针,所指向的函数返回整型数据int **p;p是指针,指向一个指向整型数据的指针

二、指针运算小结

1、指针变量加/减运算

  p++、p--、p+i、p-i、p+=i、p-=i

  加1表示指向下一个数据。

   

2、指针变量赋值

  p = &a; 变量a的地址赋给p,即指针p指向a

  p = array; 数组array首地址赋给p

  p = &array[i]; 数组元素array[i]的地址赋给p

  p = max; 函数max的入口地址赋给p

  p1 = p2; 指针p2的值赋给指针p1,即p1、p2所指的数据相同

   注意:

  p = 1000;
  i = p;

  int i;
  int *p = &i;
  *p = 100; 使p指向的数据(即变量i)等于100。

  char *p = "I love China!"; 使p指向字符串
   

3、空指针 p = NULL;

  空指针p=NULL表示p不指向任何数据。

  在stdio.h中,NULL被定义为0:#define NULL 0

  习惯上,不使用 p = 0
      
而使用 p = NULL

  指针变量p可以与NULL作比较,例、
       if (p == NULL) ...

    注意:空指针不指向任何数据,与p未赋值不同。当p未赋值时,其值是不确定的,而空指针的          值是确定数0。

4、指针变量相减。

  当p1、p2指向同一个数组的元素,指针相假p2-p1等于p1、p2间的元素个数。
   

    注意:指针相加无意义。

5、两个指针的比较

 当p1、p2指向同一个数组的元素时,可以比较,如、p1 > p2。若p1、p2不是指向同一个数组的元素,比

较无意义。

三、空类型指针 void *

  void *p,表示p是空类型指针,它可以指向任何数据类型。

  空类型指针与其他类型指针之间赋值时,应进行强制类型转换,例:

    char *p1;
    void *p2;

    p1 = (char *)p2;
    p2 = (void *)p1;

 

 

 

 

 

 

原创粉丝点击