1 / 58

第九讲 指针

第九讲 指针. 合肥师范学院计算机科学与技术系. 关于指针的原则. 学习原则 一定要学会 其实通常的应用很简单 与使用变量几乎无异 使用原则 永远要清楚每个指针指向了哪里 永远要清楚指针指向的位置是什么. 1000 20 x. 1004 1 y. 1008 155 z. 2000 1000 p. 2002. 1.14.1 指针的基本概念. 内存单元 地址 内容 变量名.

ivory-bauer
Télécharger la présentation

第九讲 指针

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第九讲 指针 合肥师范学院计算机科学与技术系

  2. 关于指针的原则 • 学习原则 • 一定要学会 • 其实通常的应用很简单 • 与使用变量几乎无异 • 使用原则 • 永远要清楚每个指针指向了哪里 • 永远要清楚指针指向的位置是什么

  3. 1000 20 x 1004 1 y 1008 155 z 2000 1000 p 2002 1.14.1 指针的基本概念 内存单元 地址 内容 变量名 直接访问:通过变量名访问 int x = 20, y = 1, z = 155; printf("%d", x;) • 间接访问:通过另一个变量访问 • 把变量的地址放到另一变量中 • 使用时先找到后者 • 再从中取出前者的地址 地址 指针变量

  4. 1.14.1 指针的基本概念 • 指针也是一种数据类型 • 指针变量 • 具有指针类型的变量,专门存放地址数据的变量

  5. 如何定义指针变量? 类型名 *指针变量名 指针声明符 指针变量所指向的变量的类型 • int *p; • p 是整型指针,指向整型变量 • float *fp; • fp 是浮点型指针,指向浮点型变量 • char *cp; • cp 是字符型指针,指向字符型变量

  6. p a &a 3 指针变量的引用 ——&与*操作符 指针变量同普通变量一样,使用之前不仅要定义说明,而且必须赋予具体的值。 1、取地址运算和间接访问运算 & 取地址运算符,给出变量的地址 int *p, a = 3; p = &a;把 a 的地址赋给 p,即 p 指向 a *p * 间接访问运算符,访问指针所指向的变量 *p:指针变量 p 所指向的变量

  7. &用来取变量的地址 int i, *p;p = &i; int *p, a[10];p = a; int *p, a[10];p = &a[5]; *用来取指针指向地址的内容 int i, *p;p = &i;*p = 0; int *p, a[10];p = a;*p = 0; int *p, a[10];p = &a[5];*p = 0; &与*操作符

  8. 指针变量赋值—— 判断是真?是假? 应在类型相同的指针变量之间赋值 一个指针变量不能指向与其类型不同的变量! int i,*p; p=&i; int i; float *p; p=&i; int *p; float *q; p=q; int *p; p=100; 指针变量只 存放地址! 我是真的, 你猜对了吗?

  9. *p p a &a 3 说明 int a = 3, *p; p = &a; (1) 当 p = &a 后,*p 与 a 相同 (2) int *p; 定义指针变量 p *p =10; 指针p所指向的变量,即a (3) &*p 与 &a 相同,是地址 *&a 与 a 相同,是变量 (4) (*p)++ 等价于 a++,将 p 所指向的变量值加1 *p++ 等价于 *(p++), 先取*p,然后P 自加,此时p不再指向a

  10. 指针变量与其它类型变量的对比 • 共性 • 在内存中占据一定大小的存储单元 • 先定义,后使用 • 特殊性 • 它的内容只能是地址,而不能是数据 • 必须初始化后才能使用,否则指向不确定的存储单元 • 只能指向同一基类型的变量 • 可参与的运算:加、减一个整数,自增、自减、关系、赋值

  11. 1.14.2 指针运算(1/3) • 赋值运算 • 指针在使用前一定要赋值 • 为指针变量赋的值必须是一个地址 main() { int a,*p=&a; scanf("%d",p); … } main() { int *p; scanf("%d",p); … } 错!

  12. p-1 p p+1 指针运算(2/3) • 算术运算 • int *p, a[10]; p = a; p++; /*p的值增加多少?*/ • 指针的加减运算是以 • 其指向的类型的字长为单位的 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011

  13. 例程2.14.2.cpp • int * pn1, * pn2; • char * pc1, * pc2; • pn1 = (int *) 100; //地址pn1为100 • pn2 = (int *) 200; //地址pn2为200 • printf("%d\n", pn2 - pn1); //输出 25, 因为(200-100)/sizeof(int) • pc1 = (char * ) pn1; //地址pc1为100 • pc2 = (char * ) pn2; //地址pc2为200 • printf("%d\n", pc1 - pc2 ); //输出 -100,因为(100-200)/sizeof(char)

  14. 指针运算(3/3) • 关系运算 • 只有指向同一种数据类型的两个指针才能进行关系运算。 • 值为1或0 • p > q p < q p == q • 指针不与非指针量进行比较,但可与NULL(即0值)进行等或不等的关系运算 • 判断p是否为空指针 • P == NULL • p != NULL

  15. p a[0] a[1] a[2] a[3] a[0] a[1] a[2] a[3] 1.14.5指针和数组 a 6000 6001 6002 6003 6004 6005 6006 6007 a+1 • 数组名就是一个指针 • 只是不能修改这个指针的指向 • 可以定义函数的参数为数组 • 指针也可当作数组名使用 • int *p, a[10];p = a; • 数组元素的几种等价引用形式 • a[i] • *(a+i) • p[i] • *(p+i) a+2 a 6000 6001 6002 6003 6004 6005 6006 6007 p++ p++

  16. 地址 内容 数组元素 p a 3000 a[0] p+1 a+1 3002 a[1] p+i a+i a[i] p+99 a+99 3198 a[99] 指针和数组的关系 任何由数组下标来实现的操作都能用指针来完成 int a[100], *p; p = a; 或 p = &a[0]; &a[i]a[i] a+i*(a+i) p = a; sum = 0; for(i = 0; i < 100; i++) sum = sum + p[i]; 等价 p+i *(p+i) &p[i]p[i] 等价

  17. 地址 内容 数组元素 a 3000 a[0] a+1 3002 a[1] a+i a[i] a+99 3198 a[99] 用指针完成对数组的操作 int a[100], *p; 移动指针 p p p p sum = 0; for(p = a; p <= &a[99]; p++) sum = sum + *p;

  18. 输入输出数组的全部元素 方法1:下标法 方法2:指针法 main() { int a[10]; int i; for (i=0; i<10; i++) scanf("%d", &a[i]); for (i=0; i<10; i++) printf("%d ", a[i]); } main() { int a[10]; int *p, i; for (p=a; p<(a+10); p++) scanf("%d", p); for (p=a; p<(a+10); p++) printf("%d ", *p); }

  19. 例程2.14.5.1.cpp • #include <stdio.h> • int main() • { • int an[200]; • int * p ; • p = an; // p指向数组an的起始地址,亦即p指向了an[0] • * p = 10; //使得an[0] = 10 • *( p + 1 ) = 20; //使得 an[1] = 20 • p[0] = 30; // p[i] 和 *(p+i) 是等效的,此句使得an[0] = 30 • p[4] = 40; //使得 a[4] = 40 • for( int i = 0;i < 10;i ++) //通过一个循环对数组an的前10个元素进行赋值 • *( p + i) = i; • p++; // p指向 a[1] • printf("%d\n", p[0]); // 输出a[1]的值,即1。p[0] 等效于 * p • p = an + 6; // p指向a[6] • printf("%d\n", * p); // 输出 6 • return 0;

  20. 指针作为函数的参数 函数参数包括实参和形参,两者的类型要一致,可以是指针类型。 如果实参是某个变量的地址,相应的形参就是指针。 调用哪个函数,可以交换main ()中变量a和b的值? int main (void) { int a = 1, b = 2; int *pa = &a, *pb = &b; void swap1(int x, int y), swap2( int *px, int *py ), swap3 (int *px, int *py); swap1 (a, b); printf (“After calling swap1: a=%d b=%d\n”, a, b);   a = 1; b = 2; swap2(pa, pb); printf (“After calling swap2: a=%d b=%d\n”, a, b); a = 1; b = 2; swap3(pa, pb); printf (“After calling swap3: a=%d b=%d\n”, a, b); return 0; }

  21. 主调函数 被调 函数 编写函数实现两数的互换 程序 2 程序 1 main() { int a, b; a = 15; b = 8; Swap( &a, &b ); printf("a=%d,b=%d",a,b); } main() { int a, b; a = 15; b = 8; Swap(a, b); printf("a=%d,b=%d",a,b); } void Swap(int x,int y) { int temp; temp = x; x = y; y = temp; } 实 参 void Swap(int *x,int *y) { int temp; temp = *x; *x = *y; *y = temp; } 形 参 Not Work!Why? 结果有何不同?

  22. x y a b 程序 1 形 参 实 参 主调函数 被调函数 void Swap(int x, int y) { int temp; temp = x; x = y; y = temp; } main() { int a, b; a = 15; b = 8; Swap(a, b); printf("a=%d,b=%d",a,b); } y x b a 8 8 15 15

  23. x y a b 程序 2 形 参 实 参 主调函数 被调函数 void Swap(int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp; } main() { int a, b; a = 15; b = 8; Swap(&a, &b); printf("a=%d,b=%d",a,b); } a b 8 15 x y &a &b &a &b

  24. b y a x 1 1 2 2 2 1 例 swap1() swap1 (a, b); void swap1 (int x, int y) { int t; t = x; x = y; y = t; }

  25. b a 1 2 2 1 py px 例swap2() swap2 (&a, &b); void swap2 (int *px, int *py) { int t; t = *px; *px = *py; *py = t; } 值传递,地址未变, 但存放的变量的值改变了

  26. b a 1 2 2 1 py px 指针作为函数参数的应用 swap3 (&a, &b); void swap3 (int *px, int *py) { int *t; t = px; px = py; py = t; } After calling swap1: a=1, b=2 After calling swap2: a=2, b=1 After calling swap3: a=1, b=2 要通过函数调用来改变主调函数中某个变量的值: (1) 在主调函数中,将该变量的地址或者指向该变量的指针作为实参 (2) 在被调函数中,用指针类型形参接受该变量的地址 (3) 在被调函数中,改变形参所指向变量的值

  27. 通过指针实现函数调用返回多个值 例输入年和天数,输出对应的年、月、日。 例如:输入2000和61,输出2000-3-1。 定义函数month_day(year, yearday, *pmonth, *pday) 用2个指针作为函数的参数,带回2个结果 int main (void) { int day, month, year, yearday; void month_day(int year,int yearday, int *pmonth,int *pday); printf(“input year and yearday: ”); scanf ("%d%d", &year, &yearday ); month_day (year, yearday, &month, &day ); printf ("%d-%d-%d \n", year, month, day ); return 0; }

  28. day month 3 1 pday pmonth void month_day ( int year, int yearday, int * pmonth, int * pday) { int k, leap; int tab [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 }, };   /* 建立闰年判别条件leap */ leap = (year%4 == 0 && year%100 != 0) || year%400 == 0; for ( k = 1; yearday > tab[leap][k]; k++) yearday -= tab [leap][k]; *pmonth = k; *pday = yearday; } input year and yearday: 2000 61 2000-3-1

  29. 冒泡排序算法分析 相邻两个数比较,小的调到前面,大的调到后面 9 8 8 8 8 8 5 4 4 0 8 9 5 5 5 5 4 5 0 4 5 5 9 4 4 4 6 0 5 4 4 4 9 6 6 0 6 6 6 6 6 9 0 8 0 0 0 0 0 9

  30. i=5 i=4 i=3 i=2 i=1 9 8 8 8 8 8 5 4 4 0 8 9 5 5 5 5 4 5 0 4 5 5 9 4 4 4 6 0 5 4 4 4 9 6 6 0 6 6 6 6 6 9 0 8 0 0 0 0 0 9 j=0 to 2 j=0 to 3 j=0 to 4 j=0 to 6-1-i a[j]>a[j+1]

  31. int main(void ) { int i, j, n, t, a[10]; n = 6; for(i = 0; i < n; i++) scanf("%d", &a[i]); for(i = 1; i < n; i++) for(j = 0; j < n-i; j++) if(a[j] > a[j+1]) { t = a[j]; a[j] = a[j+1]; a[j+1] = t; } return 0; } 9 8 5 4 6 0 i=1 j=0: 8 9 5 4 6 0 j=1: 8 5 9 4 6 0 j=2: 8 5 4 9 6 0 j=3: 8 5 4 6 9 0 j=4: 8 5 4 6 0 9

  32. void sort(int *array, int n) { int i, j, t; for(i=1; i<n; i++) for(j=0; j<n-i; j++) if(array[j]>array[j+1]){ t = array[j]; array[j] = array[j+1]; array[j+1] = t; } } int main(void ) { int i, a[10]; for(i=0; i<10; i++) scanf("%d", &a[i]); sort(a, 10); for(i=0; i<10; i++) printf("%d ", a[i]); printf("\n"); return 0; }

  33. a[0][0] a[0][1] a[0][2] a[1][0] a[0][0] a[0] a[0][1] a[0][2] a[1][0] a[1] a[1][1] a[1][2] a a[1][1] a[1][2] 指针与二维数组 • C语言将二维数组看作一维数组,其每个数组元素又是一个一维数组 &a[0][0] a a[0]+0 int a[2][3]; &a[0][1] a[0]+1 &a[0][2] a[0]+2 a+1 a[1]+0 &a[1][0] &a[1][1] a[1]+1 &a[1][2] a[1]+2

  34. a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3] 行地址 列地址 a[0]+0 a[0]+1 a[0]+2 a[0]+3 a+0 &a[0] 一维数组a[0] a[0] 列地址 a+1 &a[1] a[1]+0 a[1]+1 a[1]+2 a[1]+3 a[1] 一维数组a[1] a+2 &a[2] a[2] 列地址 a[2]+0 a[2]+1 a[2]+2 a[2]+3 一维数组a 一维数组a[2] 指针与二维数组

  35. 指针与二维数组 • a • 代表二维数组的首地址,第0行的地址 • a+i • 代表第i行的地址 • *(a+i) 即 a[i] • 代表第i行第0列的地址 • *(a+i)+j 即 a[i]+j • 代表第i行第j列的地址 • *(*(a+i)+j ) 即 a[i][j] • 代表第i行第j列的元素

  36. a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] 指针与二维数组 二维数组指针变量说明的一般形式为: • 类型说明符 (*指针变量名)[长度] int (*p)[3]; p = a;//用行地址初始化 for (i=0; i<n; i++) for (j=0; j<m; j++) printf("%d",*(*(p+i)+j)); p p++

  37. 1.14.6字符串和字符指针 • 字符串常量 "array" "point" • 用一对双引号括起来的字符序列 • 被看做一个特殊的一维字符数组,在内存中连续存放 • 实质上是一个指向该字符串首字符的指针常量 char sa[ ] = "array"; char *sp = "point";

  38. printf("%s ", sa+2); printf("%s ", sp+3); printf("%s\n", string"+1); char sa[ ] = "array"; char *sp = "point"; printf("%s ", sa); printf("%s ", sp); printf("%s\n", "string"); ray nt tring array point string 数组名sa、指针sp和字符串 "string" 的值都是地址

  39. 字符数组与字符指针的重要区别 char sa[ ] = "This is a string"; char *sp = "This is a string"; sa sp 如果要改变数组sa所代表的字符串,只能改变数组元素的内容 如果要改变指针sp所代表的字符串,通常直接改变指针的值,让它指向新的字符串

  40. 示例 char sa[ ] = "This is a string"; char *sp = "This is a string"; strcpy (sa, "Hello"); sp = "Hello"; sa = “Hello”; 非法 数组名是常量,不能对它赋值

  41. 字符指针-先赋值,后引用 定义字符指针后,如果没有对它赋值,指针的值不确定。 char *s ; scanf(“%s”, s); char *s, str[20]; s = str; scanf(“%s”, s); 定义指针时,先将它的初值置为空 char *s = NULL 不要引用未赋值的指针

  42. 若定义了一个指针变量,并使它指向一个字符串,就可以用下标形式引用指针变量所指的字符串中的字符。若定义了一个指针变量,并使它指向一个字符串,就可以用下标形式引用指针变量所指的字符串中的字符。 • #include <stdio.h> • voidmain() • {char*a=″I love China!″; • int i; • printf ( “ The sixth character is %c\n",a[5]); • for(i=0;a[i]!=′\0′;i++) • printf(″%c″,a[i]); • }

  43. 入口地址 指令1 指令2 指令3 … 指令n 1.14.8指向函数的指针 • 每个函数都占用一段内存单元,有一个入口地址(起始地址) • 函数名:函数的入口地址 • 函数指针:一个指针变量,接收函数的入口地址,让它指向函数 • 通过函数指针调用函数 • 做为函数的参数

  44. 函数指针的定义和赋值 int fun(x ,y) { return x > y ? x : y; } • 定义 类型名 (*变量名)( ); 所指向函数的返回值的类型 int (*funptr)( ); • 定义一个函数指针funpt • funpt指向一个返回值类型为int的函数 • 赋值 funptr = fun; • 函数fun的入口地址赋给funptr • funptr指向函数fun

  45. 通过函数指针调用函数 int fun(x ,y) { return x > y ? x : y; } int (*funptr)( ); funptr = fun; • 调用函数 • 函数名 z = fun(3, 5); • 函数指针 (*funptr)(3, 5);或funptr(3,5) (*函数指针名)(参数表)

  46. 函数指针做为函数的参数 • 实参:函数名或已赋值的函数指针 • 形参:函数指针,指向实参所代表函数的入口地址 例编写一个函数calc(f, a, b),用梯形公式求函数f(x)在[a, b]上的数值积分。 然后调用calc(f, a, b)计算下列数值积分。 分析: • 函数定义时,形参:函数指针f、积分区间上下限参数a,b • 函数调用时,实参:被积函数的名称(或函数指针)和积分区间的上下限

  47. 源程序 double f1 ( double x ) { return (x*x); } double f2 ( double x ) { return (sin(x)/x); } double calc ( double (*f)(double), double a, double b ) { double z; z = (b-a)/2 * ( (*f)(a) + (*f)(b) ); /* 调用 f 指向的函数 */ return ( z ); } int main ( void ) { double result; result = calc(f1, 0.0, 1.0); /* 函数名f1作为函数calc的实参 */ printf("1: resule=%.4f\n", result); result = calc(f2, 1.0, 2.0); /* 函数指针funp作为函数calc的实参 */ printf("2: resule=%.4f\n", result); return 0; } 1: resule=0.5000 2: resule=0.6481

  48. 指针数组的概念 char *color[5]; 类型名 *数组名[数组长度] 数组元素是指针类型,用于存放内存地址 int a[10]; • a是一个数组,它有10个元素 • 每个元素的类型都是整型 char *color[5]; • color是一个数组,它有5个元素 • 每个元素的类型都是字符指针

  49. 指针数组的概念 char *color[5] = {"red", "blue", "yellow", "green", "purple"}; • color是一个数组,它有5个元素 • 每个元素的类型都是字符指针 • 数组元素可以处理字符串 对指针数组元素的操作相当于对同类型指针变量的操作 printf("%10s %10s\n", color[i], color[j]);

  50. 例:字符串按字典顺序排序 char str[N][10] = {"Pascal","Basic","Fortran", "Java","Visual C"}; for (i=0; i<N-1; i++) { for (j = i+1; j<N; j++) { if (strcmp(str[j], str[i]) < 0) { strcpy(temp,str[i]); strcpy(str[i],str[j]); strcpy(str[j],temp); } } } 方法1:二维数组

More Related