Presentation is loading. Please wait.

Presentation is loading. Please wait.

第八章 指 针 8.1 指针的概念与定义 8.2 指针作函数参数 8.3 指针与数组 8.4 指针与函数 8.5 复杂指针.

Similar presentations


Presentation on theme: "第八章 指 针 8.1 指针的概念与定义 8.2 指针作函数参数 8.3 指针与数组 8.4 指针与函数 8.5 复杂指针."— Presentation transcript:

1 第八章 指 针 8.1 指针的概念与定义 8.2 指针作函数参数 8.3 指针与数组 8.4 指针与函数 8.5 复杂指针

2 8.1 指针的概念与定义 指针的概念 图 8.1 内存分配表

3 指针的定义及使用 1. 指针的定义 指针是一种存放地址值的变量,像其它变量一样,必须在使用前定义。指针变量的命名遵守与其它变量相同的规则, 即必须是唯一的标识符。指针定义的格式如下:  类型名 *指针名;

4 例 8.1 指针与非指针的定义 例 8.2 指针的指向 int *point;  scanf("%d", point);
例 8.1 指针与非指针的定义 char *pcl,*pc2; /* pcl和pc2均为指向char型的指针*/ float *pf, percent;/* pf是float型的指针,而percent为普通的float型变量*/ 例 8.2 指针的指向 int *point;  scanf("%d", point);

5 2. 指针的有关运算符 两个有关的运算符:  &: 取地址运算符。  *: 指针运算符(或称“间接访问”运算符)。  例如:&a为变量a的地址, *p为指针p所指向的存贮单元的内容。  &运算符只能作用于变量,包括基本类型变量和数组的元素、 结构体类型变量或结构体的成员(第九章), 不能作用于数组名、 常量或寄存器变量。 例如:

6 表达式&r、 &a[0]、 &a[i]是正确的, 而&(2*r)、 &a、&k是非法表示。
double r, a[20];  int i;  register int k; 表达式&r、 &a[0]、 &a[i]是正确的, 而&(2*r)、 &a、&k是非法表示。 单目运算符*是&的逆运算,它的操作数是对象的地址, *运算的结果是对象本身。 单目*称为间访运算符,“间访”就是通过变量的地址而不是变量名存取(或引用)变量。例如, 如果pc是指向字符变量c的指针,则*(&c)和*pc表示同一字符对象c。因而赋值语句 *(&c)=′a′;  *pc=′a′;  c=′a′; 效果相同,都是将′a′存入变量c。

7 例 8.3 取地址运算符。 int variable, *point;  point=&variable;

8 3. 指针的使用 例 8.4 指针的使用。 main( ) {int a, b, *p1, *p2;  a=10; b=20; 
例 8.4 指针的使用。 main( ) {int a, b, *p1, *p2;  a=10; b=20;  p1=&a; p2=&b;  printf("%d\t%d\n", *p1, *p2);  p1=&b; p2=&a;  } 程序运行结果:  10 20 20 10

9 程序说明:  (1) 在两个printf函数调用语句中的*是指针运算符,这一单目运算符的运算对象应该是指针或地址, 它的作用是得到指针指向变量的值。  (2) 在第一个printf函数调用时, 可以假设内存的分配如图8.2所示。 图 8.2 内存分配表

10 (3) 在第二个printf函数调用时,内存的分配将如图8.3所示。
图 8.3 内存分配表

11 例 8.5 指针的使用。 程序运行结果:  10 20.5 10 20.5 main( ) {int a, *pi; 
例 8.5 指针的使用。 main( ) {int a, *pi;  float f, *pf;  a=10; f=20.5;  pi=&a; pf=&f;  printf("%d\t%4.1f\n", a, f);  printf("%d\t%4.1f\n", *pi, *pf);  } 程序运行结果: 

12 程序说明: 图 8.4 内存分配表

13 8.2 指针作函数参数 例 8.6 函数参数的传递。 void swap(int x, int y);  main() { 
8.2 指针作函数参数 例 8.6 函数参数的传递。 void swap(int x, int y);  main() {  int a, b;  a=10; b=20;  swap(a, b);  printf("a=%d, b=%d\n", a, b);  }

14 void swap(int x, int y) 运行结果:  a=10, b=20 { int temp;  temp=x; 
x=y;  y=temp;  } 运行结果:  a=10, b=20

15 图 8.5 swap函数被调用时的内存分配图 图 8.6 swap函数调用结束时的内存分配图

16 例 8.7 指针作函数参数。 void swap(int * x, int * y);  main() {
例 8.7 指针作函数参数。 void swap(int * x, int * y);  main() { int a, b, *p1, *p2;  a=10; b=20; p1=&a; p2=&b;  swap(p1, p2);  printf("a=%d, b=%d\n", a, b); /* 或printf(″%d %d″, *p1, *p2) */ } void swap(int *pa, int *pb) int temp;  temp=*pa;  *pa=*pb;  *pb=temp;  } 运行结果:  a=20, b=10

17 图 8.7 swap函数被调用时的内存分配图 图 8.8 swap函数调用结束时的内存分配图

18 例 8.8 指针作函数参数。 void swap(int *, int *);  main() { 
例 8.8 指针作函数参数。 void swap(int *, int *);  main() {  int a, b, *p1, *p2;  a=10; b=20; p1=&a; p2=&b;  swap(p1, p2);  printf("a=%d, b=%d\n", a, b);  } void swap(int *pa, int *pb) int *temp;  temp=pa;  pa=pb;  pb=temp;  运行结果:  a=10, b=20

19 图 8.9 swap函数被调用时的内存分配图 图 8.10 swap函数调用结束时的内存分配图

20 例 8.9 指针作函数参数。 void swap(int *, int *); main() { 
例 8.9 指针作函数参数。 void swap(int *, int *); main() {  int a, b, *p1, *p2;  a=10; b=20; p1=&a; p2=&b;  swap(p1, p2);  printf("a=%d, b=%d\n", a, b);  } void swap(int *pa, int *pb) int *temp;  *temp=*pa;  *pa=*pb;  *pb=*temp;  }

21 可以将swap函数改变成这样: void swap(int *pa, int *pb) { int *temp, t; 
*temp=*pa;  *pa=*pb;  *pb=*temp;  } 此时可以得到运行结果:  a=20, b=10

22 8.3 指 针 与 数 组 8.3.1 指向一维数组的指针 图 8.11 指向数组元素的指针

23 实际上,C语言允许这样的表达方式: pa[i]和. (a+i), 它们等价于
实际上,C语言允许这样的表达方式: pa[i]和*(a+i), 它们等价于*(pa+i)和a[i]。由此可见,引用数组元素有两种等价的形式: 通过下标引用和通过指针引用。以数组a为例, 假定pa指向a[0],元素的下标引用和指针引用的对应关系如下(写在同一行上的表达式是对同一元素的等价引用形式): a[0] *pa *a或*(a+0) a[1] *(pa+1) *(a+1) a[2] *(pa+2) *(a+2) … … … a[9] *(pa+9) *(a+9)

24 元素地址的对应关系如下:  &a[0] pa a或a+0 &a[1] pa a+1 &a[2] pa a+2 … … … &a[9] pa a+9

25 8.3.2 数组作函数参数 例 8.10 数组名作函数参数 main() { int *p, i, a[10];  p=a; 
数组作函数参数 例 数组名作函数参数 main() { int *p, i, a[10];  p=a;  for(i=0; i<10; i++) scanf("%d", p++);  sort(p, 10);  for(p=a, i=0; i<10; i++)

26 { printf("%5d", *p); p++; }
printf("\n");  } sort(int x[], int n) { int i, j, k, t;  for(i=0; i<n-1; i++) { k=i;  for(j=i+1; j<n; j++) if(x[j]>x[k]) k=j;  if(k!=i) { t=x[i]; x[i]=x[k]; x[k]=t; }

27 函数sort的形参x可以认为是main函数中数组a的别名, 所以在函数sort中对x的操作就是对a的操作,使得数组a得以排序, 完成了程序的要求。注意在main函数中使用指针p时,指针当前指向的变化。当然,main函数中调用sort函数时实参可以是指针,也可以是数组名,在函数sort中形参可以是数组名, 也可以是指针。 可将上例的程序改写如下: main() { int i, a[10];  for(i=0; i<10; i++) scanf("%d", &a[i]);  sort(a, 10); 

28 printf("%5d", a[i]);  } sort(int *x, int n) { int i, j, k, t;  for(i=0; i<n-1; i++) {k=i;  for(j=i+1; j<n; j++) if(x[j]>x[k]) k=j;  if(k!=i) { t=x[i]; x[i]=x[k]; x[k]=t;  }

29 8.3.3 指针和字符串 在C语言中,字符串(例如"I am a student")指在内存中存放的一串以′\0′结尾的若干个字符。例如,可以这样定义和初始化一个字符数组:  char string[]="I am a student";  数组长度由字符串长度加1确定。也可以定义一个字符数组, 然后用标准输入函数从外部设备读入一个字符串。例如:  char string[20];  scanf("%s", string);

30 数组长度应能足够存放读入的最大长度的字符串。 
利用指针也可以表达字符串, 而且比用字符数组更为方便灵活。例如,可以这样定义和初始化一个字符指针:  char *point="I am a student";  point是指向字符串"I am a student"的指针,即字符串的首地址赋给了字符指针,因此使一个字符指针指向一个字符串。 也可以采用下面的方式:  char *point;  point="I am a student";

31 例 8.11 字符串拷贝函数。 #include<stdio.h> void my-strcpy(char *t, char *s)
例 字符串拷贝函数。 #include<stdio.h> void my-strcpy(char *t, char *s) {  while((*t=*s)!=′\0′) { s++;  t++;  } }

32 下列对my-strcpy函数的调用都是正确的:
(1)my-strcpy(s1,"I am a student");  (2) ps1=&s1[0];  ps2="I am a student";  my-strcpy(ps1, ps2);  (3) ps2=&s2[0];  my-strcpy(ps2,"I am a student");  my-strcpy(s1,s2); 或 my-strcpy(s1,ps2);

33 my-strcpy的定义可以写成更简练的形式; 
void my-strcpy(char *t, char *s) {  while((*t++=*s++)!=′\0′);  } 复制过程继续的条件是被复制的字符为非0(非′\0′)。 由于组成字符串的任何字符(′\0′除外)的值都为非0, 所以my-strcpy还可以进一步简化为  void my-strcpy(char *t, char *s) while(*t++=*s++);  }

34 例 8.12 字符串比较函数。 int my-strcmp(char *s, char *t) { 
例 字符串比较函数。 int my-strcmp(char *s, char *t) {  for(; *s==*t; s++, t++) if(*s==′\0′) return 0;  return(*s-*t);  }

35 8.3.4 指向多维数组的指针 设有一个二维数组的定义为
static int a[2][4]={{1, 3, 5, 7}, {2, 4, 6, 8}}; 我们可以假设数组a在内存中的分配情况如下:

36 详细区分说明如下:

37 例 8.13 多维数组。 运行结果:  1 3 5 7 2 4 6 8 10 20 30 40 main() { 
例 多维数组。 main() {  static int a[3][4]={{1, 3, 5, 7}, {2, 4, 6, 8}, {10, 20, 30, 40}};  int *p;  for(p=a[0]; p<a[0]+12; p++) /* 注1 */ { if((p-a[0])%4==0) printf("\n"); /* 注2 */ printf("%4d", *p);  } } 运行结果: 

38 例 8.14 多维数组。 运行结果: 1 3 5 7 2 4 6 8 10 20 30 40 main() { 
例 多维数组。 main() {  static int a[3][4]={{1, 3, 5, 7}, {2, 4, 6, 8}, {10, 20, 30, 40}};  int i, j, (*p)[4];  p=a;  for(i=0; i<3; i++) { for(j=0; j<4; j++) printf("%4d", *(*(p+i)+j));  printf("\n");  } } 运行结果:

39 (1) 形参说明为指向数组元素的指针, 实参为数组元素的地址或指向元素的指针。
 例如:  调用函数f, 用数组元素的地址作实参: int a[2][3];  void f(int *, int);  … f(a[0], 2*3);  …

40 调用函数f,用指向数组元素的指针作实参:
int a[2][3], *pi;  void f(int *, int);  … pi=a[0]; /* 或pi=&a[0][0] */ f(pi, 2*3);  … pi是指向元素 a[0][0]的指针。  函数f的定义: 〖HT5”〗 void f(int *pi, int size) { } 形参pi说明为列指针。

41 (2) 形参说明为行指针, 实参为行地址或行指针。例如,
调用函数f, 用行指针作实参:  { int a[2][3];  void f(int (*)[3], int);  … f(a, 2);  }

42 实参 a是行指针,类型为 int(*)[3];实参 2是二维数组 a的行数。 
调用函数f, 用行指针作实参:  int a[2][3], (*pa)[3];  void f(int(*)[3], int); … pa=a;  f(pa, 2); 

43 pa是行指针,赋值语句“pa=a; ”使pa指向a的第0行, pa的类型与a的类型相同。 
函数f的定义:  void f(int (*pa)[3], int size) {  … }

44 例 行指针作函数参数 输入一个用年、月、日表示的日期,定义函数day- of-year将它转换成该年的第几天;输入某年的第几天,定义函数 month-day将它转换成该年的某月某日。 #include<stdio.h> /* day-of-year: 从月份和日期计算为一年中的第几天 */ int day-of-year(int year, int month, int day, int *pi) {  int i, leap;  leap=year%4==0&&year%100![KG-*4]=0||year%400==0;  for(i=1; i<month; i++) day+=*(pi+leap*13+i);  return (day);  }

45 /* month-day: 从一年中的第几天计算月份和日期 */
void month-day(int year, int yday, int (*pdaytab)[13], int *pmonth, int pday) {  int i, leap;  leap=year%4==0&&year%100![KG-*4]=0||year%400==0;  for(i=1; yday>*(*(pdaytab+leap)+i); i++) yday-=*(*(pdaytab+leap)+i);  *pmonth=i;  *pday=yday;  } int main(void) {

46 int daytab[2][13]={ {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} }, y, m, d, yd;  printf("input year, month, day: \n");  scanf("%d%d%d", &y, &m, &d);  yd=day-of-year(y, m, d, &daytab[0][0]);  printf("day of year is %d\n", yd);  printf("input year, day-of-year: \n");  scanf("%d%d", &y, &yd); 

47 month-day(y, yd, daytab, &m, &d); 
printf("%d, %d in %d\n", m, d, y);  return 0;  } input year, month, day: (输出)   (输入) day of year is (输出) input year, day-of-year: (输出)  (输入) 10, 7 in (输出)

48 指针数组 指针变量可以同其它变量一样作为数组的元素, 由指针变量组成的数组称为指针数组,组成数组的每个元素都是相同类型的指针。  指针数组说明的形式为 类型名 *数组名[常量表达式]:  其中“*数组名[常量表达式]”是指针数组说明符。 例如: int *ps[10];

49 设有二维数组说明:  int a[4][4];  用指针数组表示数组a, 就是把a看成4个一维数组, 并说明一个有4个元素的指针数组pa,用于集中存放a的每一行元素的首地址,且使指针数组的每个元素pa[i]指向a的相应行。于是可以用指针数组名pa或指针数组元素pa[i]引用数组a的元素。  指针数组pa的说明和赋值如下:

50 int *pa[4], a[4][4];  pa[0]=&a[0][0]; 或pa[0]=a[0];  pa[1]=&a[1][0]; 或pa[1]=a[1];  pa[2]=&a[2][0]; 或pa[2]=a[2];  pa[3]=&a[3][0]; 或pa[3]=a[3];  *(*(pa+i)+0), *pa[i]或*(pa[i]+0)(i=0, 1, 2, 3)引用第i行第0列元素 a[i][0] ; *(*(pa+i)+1)或*(pa[i]+1)引用第i行第1列元素 a[i][1]; …。

51 用指针数组表示二维数组在效果上与数组的下标表示是相同的,只是表示形式不同。用指针数组表示时,需要额外增加用作指针的存贮开销;但用指针方式存取数组元素比用下标速度快,而且每个指针所指向的数组元素的个数可以不相同。例如,可用有5个元素的指针数组和5个不同长度的整型数组来描述下面的三角矩阵:

52 存贮三角矩阵的数组和每一行的指针可说明如下: 
int a1[1], a2[2], a3[3], a4[4], a5[5], *pa[5];  下面的语句使pa的每个元素指向三角矩阵的每一行:  pa[1]=&a1[0];  pa[2]=&a2[0];  pa[3]=&a3[0];  pa[4]=&a4[0];  pa[5]=&a5[0];

53 8.4 指 针 与 函 数 8.4.1 指向函数的指针 C语言可以定义指向函数的指针,函数型指针的定义形式:
8.4 指 针 与 函 数 8.4.1 指向函数的指针 C语言可以定义指向函数的指针,函数型指针的定义形式: 类型标识符 (*指针名)(); 例如:  int (*fp)(); 说明: fp是指向int类型函数的指针。与指向数组的指针说明类似,说明符中用于改变运算顺序的()不能省。如果将(*fp)()写成* fp(), 则 fp成为返回值为指针类型的函数

54 例 指向函数的指针。  设一个函数operate,在调用它的时候,每次实现不同的功能。输入a和b两个数,第一次调用operate得到a和b中最大值,第二次得到最小值,第三次得到a与b之和。 main() {  int max(), min(), sum(), a, b; /* 必须进行函数声明, 否则无法调用 */ printf("Enter two number: ");  scanf("%d%d", &a, &b);  printf("max="); operate(a, b, max);  printf("min="); operate(a, b, min);  printf("sum="); operate(a, b, sum);

55 } max(int x, int y) {  if(x>y) return(x);  else return(y);  min(int x, int y) if(x<y) return(x);  sum(int x, int y) return(x+y);  operate(int x, int y, int (*fun)()) printf("%d\n", (*fun)(x, y));  }

56 程序运行情况:  Enter two number: 5 9 max=9 min=5 sum=14

57 返回指针的函数 C的函数可以返回除数组,共用体变量和函数以外的任何类型数据和指向任何类型的指针。 指针函数定义的一般形式:  类型标识符 *函数名(参数表){ } 其中“*函数名(参数表)”是指针函数定义符。 例如:  int *a(int x, int y){ }

58 例 8.17 指针函数 #include<string.h> #define STRLEN 81
例 指针函数 #include<string.h> #define STRLEN 81 #include <stdio.h> char *maxstr(char *str1, char *str2) {  if(strcmp(str1, str2)>=0) return(str1);  else return(str2);  } main() char string1[STRLEN], string2[STRLEN], *result;  printf("Input two strings: \n");  scanf("%s%s", string1, string2);  result=maxstr(string1, string2);  printf("The max string is: %s\n", result); 

59 程序运行情况:  Input two strings:  abcde abcee The max string is: abcee

60 8.5 复 杂 指 针 8.5.1 指向指针的指针 定义了指针pointer,它指向char型,用它可以存放字符型变量的地址,并且可以用它对所指向的变量进行间接访问。 进一步定义:  char **p-p;  从运算符*的结合性可以知道, 上述定义相当于:  char *(*p-p);

61 例 8.18 指向指针的指针。 main() {  int a, *pointer, **p-p;  a=20; 
例 指向指针的指针。 main() {  int a, *pointer, **p-p;  a=20;  pointer=&a;  p-p=&pointer;  printf("%d, %u, %u\n" , a, pointer, p-p);  printf("%d, %u, %d\n", *pointer, *p-p, * *p-p);  }

62 运行结果:  20, 2000, 2050 20, 2000, 20 图 8.12 内存分配表

63 例 指向指针的指针。 main() {  static char *country[]={"CHINA", "ENGLAND", “ FRANCE", "GERMANY"};  char * *p;  int i;  for(i=0; i<4; i++) p=country+i;  printf("%s\n", *p);  } }

64 运行结果:  CHINA ENGLAND FRANCE GERMANY

65 8.5.2 命令行参数 例 8.20 echo程序。 #include<stdio.h>
命令行参数 例 echo程序。 #include<stdio.h> main(int argc, char *argv[]) {  int i;  for(i=1; i<argc; i++) printf("%s%s", argv[i], (i<argc-1)? " ": "\n");  printf("\n");  return 0;  }

66 程序运行的命令行情况:  echo hello world  运行结果:  hello world argv[0]为所调用程序的名字,所以argc至少为1。如果argc为1,则在程序名后面没有命令行参数。

67 例 8.21 命令行的三个参数,前两个为两个整数,第三个确定程序输出的为最大值还是最小值。
#include<stdio.h> #include<stdlib.h> #include<string.h> int max(int x, int y) {  return(x>y?x: y);  } int min(int x, int y) return(x>y?y: x);  }

68 main(int argc, char *argv[])
{  int a, b;  char *operate-flag;  if(argc<2) printf("usage: abc integer1 interger2 operate-flag\n");  exit(0);  } a=atoi(argv[1]);  b=atoi(argv[2]);  operate-flag=argv[3];  if(strcmp(operate-flag, "max")==0)

69 程序生成abc.exe后, 设命令行为 abc 20 30 max 输出为 The max of 20 and 30 is: 30
printf("The %s of%s and %s is: %d\n", argv[3], argv[1], argv[ 2], max(a, b));  else if(strcmp(operate-flag, "min")==0)  printf("The %s of %s and %s is: %d\n", argv[3], argv[1], argv [2], min(a, b));  else  printf("operate-flag should be max or min\n");  } 程序生成abc.exe后, 设命令行为 abc max 输出为 The max of 20 and 30 is: 30

70 8.5.3 复杂指针的理解 注意:(1) 数组的元素不能为函数(可以为函数的指针)。
(2) 函数的返回值不能为数组或函数(可以为函数或数组的指针)。 *是指针类型的标志,[ ]是数组类型的标志,( )是函数类型的标志。( )和[ ]的优先级高于*,当[ ]、 *或( )和*同时出现时,先解释()和[]后解释*;()和[]属于同一优先级,二者同时出现时,按从左到右顺序解释; 整个说明符解释完之后,再加上类型标识符就是最终的解释。()也用于改变由优先级和结合性隐含的运算顺序,对嵌套的(),从内向外解释。

71 (1) char **pp;  pp: 指向字符指针的指针。  (2) int (*daytab)[13];  daytab: 指向由13个整型元素组成的数组的指针。  (3) int *daytab[13];  daytab: 由13个整型指针构成的指针数组。  (4) void *comp();  comp: 返回值为空类型指针的函数。  (5) void (*comp)();  comp: 指向无返回值函数的指针。

72 (6) int *p(char *a); p是一个函数, 它的形参是一字符型指针, 它的返回值是一整型指针。  (7) int (*p)(char*a); p是一个指针, 它指向一个函数, 函数有一字符指针作形参, 返回值为整型量。 (8) int p(char (*a) [ ]);  p是一个返回整型量的函数, 它有一个形参, 形参的类型是指向字符数组的指针。 (9) int *p(char *a[ ]);  p是一个函数, 返回一整型指针, 它的形参是一个字符指针数组。


Download ppt "第八章 指 针 8.1 指针的概念与定义 8.2 指针作函数参数 8.3 指针与数组 8.4 指针与函数 8.5 复杂指针."

Similar presentations


Ads by Google