1 / 38

第 9 章、指针

第 9 章、指针. 主讲教师:陈荣钦 QQ : 241881888. 本章介绍. 指针基本概念 指针变量 指针与数组 指针数组 动态内存分配 多级指针 指针参数 函数指针. 为何需要指针. 直接访问内存,避免大块数据复制,高效 通过形参获得返回值 按需分配内存,用多少申请多少. 指针是 C 语言的精髓,不懂 C 语言中的指针就不懂什么是 C 语言。. 内存. 教学楼. 地址. 存储单元. 0. …. …. 50. 2000. 数据. 2001. 2002. 2003. 2004. 2005. 402. 101. 102.

lassie
Télécharger la présentation

第 9 章、指针

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. 第9章、指针 主讲教师:陈荣钦 QQ:241881888

  2. 本章介绍 • 指针基本概念 • 指针变量 • 指针与数组 • 指针数组 • 动态内存分配 • 多级指针 • 指针参数 • 函数指针

  3. 为何需要指针 • 直接访问内存,避免大块数据复制,高效 • 通过形参获得返回值 • 按需分配内存,用多少申请多少 指针是C语言的精髓,不懂C语言中的指针就不懂什么是C语言。

  4. 内存 教学楼 地址 存储单元 0 …... …... 50 2000 数据 2001 2002 2003 2004 2005 402 101 102 201 301 302 502 202 501 601 602 401 …... …... 教室号码 指针基本概念 教室

  5. 内存 0 …... …... a的地址 2000 2001 2002 2003 b的地址 2004 2005 …... …... 变量地址 short a;//2个字节 float b;//4个字节 a 系统为每个变量都分配内存,每个内存单元的起始地址称为变量的地址! b 变量是对存储空间的抽象

  6. …... short变量a 2000 10 2001 2002 2003 2004 指针变量p 2005 2006 …... 指针变量 • 指针变量:也是一个变量,这个变量里存放另一个变量的地址 short a = 10; 称为指针p指向变量a 2000 指针变量定义: 类型 *标识符; short *p = &a;

  7. *p p &p 10 2000 a …... 变量a 2000 2001 10 2002 2003 2000 2004 指针变量p 2005 2006 …... &与*运算符 short a = 10; short *p; //指针变量 p = &a; printf(“%d”, *p); 区别: short *p;//这是定义指针 p=&a; //a的地址赋给p,表明p指向a *p----获得目标变量,这里是a &p---这是指针变量p本身的地址 • 两者互为逆运算 p =&a=&(*a) a=*p=*(&a)

  8. …... 整型变量a 2000 2001 10 2002 2003 2004 指针变量p 2000 2005 2006 …... 通过指针修改目标变量 short a = 10; short *p = &a; 20 10 *p = 20; //*p即为变量a

  9. 指针变量初始化 • 初始化格式: [存储类型] 数据类型 *指针名 = 初始地址值; int i; int *p = &i; short *p2 = &i; //错 变量必须已说明过,类型应一致

  10. 整型变量a 2000 …... …... 指针变量p 指针变量q …... 指针赋值 20 int a = 20 ; 2000 int *p, *q; p = &a; 2000 q = p;

  11. 零指针与空类型指针 • 零指针:(空指针) #define NULL 0 int *p = NULL: int * p = NULL; //即指针值为0 int *p; ...... while (p != NULL) {...… } • p = NULL与未对p赋值不同 • 用途: • 避免指针变量的非法引用 • 在程序中常作为状态比较 • void *类型指针(空类型指针) • 表示: void *p; • p不指向某种类型的变量 • 使用时要进行强制类型转换 例char *p1; void *p2; p1=(char *)p2;

  12. pi可操作单元 … a 2000 …... …... pi pc …... 不同类型的指针操作同一内存变量 pc可操作单元 #include <stdio.h> int main ( ) { unsigned short a; unsigned short *pi = &a; char *pc = (char *)&a; *pi = 0XF0F0; *pc = 0; printf ("a = %X", a); } F0 00 F0 2000 2000 输出结果: a = F000

  13. a a[0] 2000 a a[1] 2002 a+9 a+1 a+2 a[2] 2004 …... …... …… a[9] 2018 a+k &a[k] *(a+k) a[k] 指针与数组 数组名是数组的起始地址,也就是第一个元素的地址。 short a[10];

  14. 元素 地址 p a[0] 2000 *a p[0] *p 2000 a a[1] p 2002 a+1 a+9 a+2 a[2] *(p+1) p[1] *(a+1) p+1 2004 p+2 *(a+2) *(p+2) p[2] …... …... …… …… …… …… …… a[9] 2018 p+9 p[9] *(a+9) *(p+9) 指向数组的指针 将数组首地址赋给指针变量p,那么p就是指向数组的指针。 short int a[10], *p = a; 注意:p + n会根据p的类型计算内存地址,即p+n*sizeof(类型)。如p的当前值为2000,则p+1为2000+1*2=2002,而不是2001。

  15. 指向数组的指针 p仍然指向数组首地址 char str[10], *p; p = str; for (int k = 0; k < 10; k++) p[k] = 'A' + k;//也可写成*(p+k) = 'A' + k p指向数组元素str[9]的下一内存单元 char str[10], *p; p = str; for (int k = 0; k < 10; k++) *p++ = 'A' + k;//相当于 *p = 'A' + k; p++; 注意:数组名是地址常量,切不可对其赋值,也不可做++或--运算。例如:int a[10];如果在程序中出现a++或a--则是错误的。

  16. a 0 5 1 8 p p 2 7 3 6 4 2 5 7 6 3 例: int a[ ] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, *p = a, i; 数组元素地址的正确表示:(A)&(a+1) (B)a++ (C)&p (D)&p[i]  数组名是地址常量 p++,p-- () a++,a-- () a+1, *(a+2) () 6 int main() { int a[ ] = {5, 8, 7, 6, 2, 7, 3}; int y, *p = &a[1]; y = (*--p)++;//先运算后++ printf (“%d ”, y); printf (“%d”, a[0]); } 输出结果: 5 6

  17. a 0 5 1 8 p p p p p p p p 2 7 3 6 4 2 5 7 6 3 注意指针的当前值 int main ( ) { int i, *p, a[7]; p = a; for (i = 0; i < 7; i++) scanf ("%d", p++); printf ("\n"); for (i = 0; i < 7; i++, p++) printf ("%d",*p); } p=a; 指针变量可以指到数组后的内存单元

  18. *(a[0]+1) 2000 2000 a *(*(a+0)+1) a[0] a[0][0] 2002 a[0]+1 a[0][1] *(a+0)+1 a[0][2] a[0][3] 2008 2008 a+1 a[1] a[1][0] 2010 a[1]+1 a[1][1] *(a+1)+1 a[1][2] a[1][3] 2016 a+2 2016 a[2] a[2][0] 2018 a[2]+1 a[2][1] *(a+2)+1 a[2][2] a[2][3] 二维数组的理解 列指针 行指针 元素 int a[3][4];

  19. 2000 a a[0] a[0][0] 2000 2002 a[0][1] a[0][2] a[0][3] 2008 a+1 a[1] a[1][0] 2008 2010 a[1][1] a[1][2] a[1][3] a+2 2016 a[2] 2016 a[2][0] 2018 a[2][1] a[2][2] a[2][3] 二维数组的理解 int a[3][4]; • a+i-----第i行的首地址 • a[i]+j  *(a+i)+j -----第i行第j列的元素地址 • a+i与a[i]的值相等,但含义不同 • a+i指向行,是行指针 • a[i]指向列,是列指针

  20. 指向二维数组的指针 • 初始化格式: 数据类型符 (*指针变量名)[常量表达式]; 括号不能省 二维数组第二维的大小 例:int a[2][3]; int (*p)[3]; () int *p[3]; () int (*p)[2]; ()

  21. p = a p 2000 2000 a[0][0] 1 a[0](2000) 2002 a[1] a[0] a[0][1]2 a[1](2006) 2004 a[0][2] 3 2006 a[1][0] 4 2008 a[1][1]5 2010 a[1][2]6 p++后p的指向 指向二维数组的指针 #include <stdio.h> int main ( ) { short a[2][3] = {{1, 2, 3},{4, 5, 6}}; short (*p)[3]; short i, j; p = a; printf("%d ", p[0][1]); p++; printf("%d", p[0][1]); } 看成一维数组 运行结果: 2 5 4 5 6

  22. p[0]2000 p[1]2001 指针数组p p[2]2002 c[0]'a' 2000 数组c 2001 c[1]'b' 2002 c[2]'c' 指针数组 当某个数组元素都是指针型数据时,称为指针数组。 数据类型符 *变量名[常量表达式]; 例: char c[3] = {'a', 'b', 'c'}; char *p[3]; p[0] = &c[0]; p[1] = &c[1]; p[2] = &c[2];

  23. 数组指针 指针数组 int (* p)[3]; int* p[3]; p是指针变量 p是数组变量 p中的单元都是指针 p指向列数为3的数组 p中的单元都是int型的指针 p指向的数组是int型数组 指针数组与数组指针的比较

  24. src src src src src src 指针与字符串 p p p p p p E L L O \0 H dst #include <stdio.h> char *strcpy(char* dst, char* src){ char *p = dst; while(*dst) *p++ = *src++; *p = '\0'; return dst; } int main(){ char str[10]; strcpy(str, "HELLO"); puts(str); } 使用指针实现字符串拷贝函数

  25. 野指针 如果一个指针没有指向一个有效内存就被引用,则被称为“野指针”操作或空指针赋值。野指针操作尽管编译时不会出错,但很容易引起程序运行时表现异常,甚至导致系统崩溃。 char *pstr; char str[8]; scanf ("%s",pstr); //野指针操作,pstr没有指向有效内存 strcpy (pstr, "hello"); //野指针操作 pstr = str; //pstr指向数组str所对应内存单元的首地址 strcpy (pstr, "0123456789"); //不是野指针,但会造成数组越界

  26. 随机值,有可能指向系统程序区 0001 系统程序区 假设首地址为0001 此时对pstr指向的内存单元赋值 野指针危害 指针pstr所占内存 pstr 0001 char *pstr; *pstr = ‘a’;

  27. 内存分配 当定义变量或数组时,系统会按照其数据类型及大小来分配相应的内存单元,这种内存分配方式称为静态内存分配 。 short k; //2个字节 char ch[10]; //10个字节 int n; scanf ("%d", &n); int a[n]; 错误!如果在程序运行过程中决定大小,需要动态内存分配

  28. 动态内存分配 动态内存分配是指在程序运行过程中,根据程序的实际需要来分配一块大小合适的连续的内存单元。 void *malloc( unsigned int size ); • size:是分配的内存的大小(以字节为单位)。 • 返回值: • 失败,则返回值是NULL(空指针)。 成功,则返回值void*空类型指针,使用时需要转型 int n; char *p = (char*)malloc(n*sizeof(char)); //p就指向了一个有n个元素的字符数组 for(int i=0;i<n;i++) scanf(“%c”, &p[i]);

  29. 动态内存释放 • 内存是宝贵的资源,所有程序共享使用,因此需要及时释放 • 静态分配的内存根据生存周期自动释放 • malloc动态分配的内存,需要手工释放 void free (void *block); char *p = (char*)malloc(…) free( p);

  30. p2(2级) a(整型) p1(1级) &p1 3 &a 多级指针 • 当指针p1指向的普通变量时,p1称为一级指针 • 当指针p2指向的变量是指针变量p1时,p2便成了二级指针 • 当指针p3指向的变量是指针变量p2时,p3便成了三级指针 • …… 一级指针 目标变量 二级指针 int a = 3; int *p1; int **p2; P1 = &a; P2 = &p1; **p2 = 5; 5 *p2是p1指针,**p2是a变量

  31. 变量b 形参a 0 0 5 指针p 变量b &b 0 &b void func (int a) { a = 5; } void main ( ) { int b = 0; func (b); printf ("b = %d\n", b); } void func (int *p) { *p = 5; } void main ( ) { int b = 0; func (&b); printf ("b = %d\n", b); } 传值调用 传址调用 5 指针作为函数的参数 指针作为参数时,传递的是地址,属于双向传递 运行结果:b = 5 运行结果:b = 0

  32. …... (main) 2000 2002 变量a 2004 (swap) 2006 5 变量b 变量y 变量x 变量t 2008 9 200A …... 交换两个变量 void swap (int x, int y) { int t = x; x = y; y = t; } int main ( ) { int a=5, b=9; swap(a,b); printf("%d,%d\n",a,b); } 5 9 COPY 9 5 5

  33. …... (main) 2000 2002 整型变量a 指针p2 指针p1 整型p 2004 指针p_2 指针p_1 2006 2000 整型变量b 2008 2002 (swap) 200A 200C 200E ... 2010 交换两个变量 void swap(int *p1, int *p2) { int p; p = *p1; *p1 = *p2; *p2 = p; } int main ( ) { int a=5, b=9; swap(&a,&b); printf("%d,%d\n",a,b); } 9 5 5 9 2000 2002 COPY 5

  34. TOJ 3106 贫富差距 • 题目简介: • 有n个人(最多10000),每个人有m种财富,m最大达到100万,需要按照财富总和从大到小排序,除了财富为0的之外,按顺序输出。 • 问题分析: • 如果用二维数组,则有10000行1000000列,需要内存:4*10^10=40GB,内存不够用 • 事实上,100万财富的人数很少(可能只有1个人) • 解决方法: • 使用指针数组动态分配大约只要4*10^6=4M。

  35. 题目分析 定义指针数组 int* p[10000]; p[i]的前三个元素p[i][0], p[i][1], p[i][2]分别存储财富数目、财富总和、编号。

  36. 核心代码1 int* a[10000]; for(i=0;i<n;i++)//初始化 { scanf("%d", &m); a[i] = (int*)malloc((m+3)*sizeof(int)); int t = 0; for(j=0;j<m;j++){ scanf("%d", &a[i][j+3]); t += a[i][j+3]; } a[i][0] = m; a[i][1] = t; a[i][2] = i+1; }

  37. 核心代码2 for(i=0;i<n-1;i++)//冒泡排序 { for(j=0;j<n-i-1;j++) { if(a[j][1]<a[j+1][1] || a[j][1]==a[j+1][1]&&a[j][2]>a[j+1][2]) {//交换 int *t; t = a[j]; a[j] = a[j+1]; a[j+1] = t; } } }

  38. max 指令1 函数max所占内存单元 指令2 指令n 函数指针 • 定义格式 函数类型(*指针变量)( [形参类型1, 形参类型2,…, 形参类型n] ) 函数在编译时被分配的入口地址,用函数名表示 函数指针= [ & ]函数名; int max (int a, int b) { return (a > b? a : b); } int (*p)(int, int);//函数指针p p = max;

More Related