1 / 128

第七章 指针

第七章 指针. 教 材 : C 程序设计导论. 主 讲 : 谭 成 予 nadinetan@163.com. 武汉大学计算机学院. 指针定义和引用. 地址运算. 多级指针. 数组与指针关系、字符指针. 指针做函数参数、命令行参数. 本讲重点. 指针的优点. C 程序设计中使用指针可以 : 使程序 简洁 、 紧凑 、 高效 有效 地表示复杂的数据结构 动态 分配内存 得到 多于一个 的函数返回值. 内存. 0. …. 2000. 2001. 2002. 2003. 2005.

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. 第七章 指针 教 材: C程序设计导论 主 讲: 谭 成 予 nadinetan@163.com 武汉大学计算机学院

  2. 指针定义和引用 地址运算 多级指针 数组与指针关系、字符指针 指针做函数参数、命令行参数 本讲重点

  3. 指针的优点 C程序设计中使用指针可以: • 使程序简洁、紧凑、高效 • 有效地表示复杂的数据结构 • 动态分配内存 • 得到多于一个的函数返回值

  4. 内存 0 …... 2000 2001 2002 2003 2005 …... 内存中每个字节有一个编号-----地址 指针与地址 • 变量与地址 程序中: int i; float k; i 编译或函数调用时为其分配内存单元 k 变量是对程序中数据 存储空间的抽象

  5. 指针与指针变量 • 指针:一个变量的地址; • 指针变量:专门存放变量地址的变量。

  6. …... 整型变量i 2000 10 2001 2002 2003 变量i_pointer 2004 2005 2006 …... 指针与指针变量 指针 变量的内容 变量的地址 2000 指针变量

  7. 变量地址(指针) 指针变量 地址存入 指针变量 指向 变量值 变量 指针与指针变量

  8. …... 整型变量i 2000 10 2001 2002 2003 变量i_pointer 2004 2000 指针变量 2005 2006 …... 直接访问与间接访问 • 直接访问:按变量地址存取变量值 • 间接访问:通过存放变量地址的变量去访问变量 例i=3; -----直接访问 3 20 例 *i_pointer=20; -----间接访问

  9. i *i_pointer i_pointer 变量i &i i_pointer 2000 3 i=3; *i_pointer=3 *i_pointer 指针变量的定义和引用 • 指针变量的定义 • 一般形式: [存储类型] 数据类型 *指针名; 表示定义指针变量 不是‘*’运算符 合法标识符 指针变量本身的存储类型 指针的目标变量的数据类型

  10. 例 int *p1,*p2; float *q ; static char *name; 指针变量的定义和引用 注意: 1、int *p1, *p2; 与 int *p1, p2; 2、指针变量名是p1,p2 ,不是*p1,*p2 3、指针变量只能指向定义时所规定类型的变量 4、指针变量定义后,变量值不确定,应用前必须先赋值

  11. 例int *p=&i; int i; 例 int i; int *p=&i; 赋给指针变量, 不是赋给目标变量 变量必须已说明过 类型应一致 • 指针变量的初始化 一般形式:[存储类型] 数据类型 *指针名=初始地址值; 例 int i; int *p=&i; int *q=p; 用已初始化指针变量作初值 例int main( ) { int i; static int *p=&i; .............. } () 不能用auto变量的地址 去初始化static型指针

  12. #include<stdio.h> int main() { int i=10; int *p; *p=i; printf("%2d",*p); return 0; } …... 整型变量i 2000 10 2001 2002 2003 指针变量p 2004 2005 随机 2006 …... 指针变量必须先赋值,再使用 危险! #include<stdio.h> int main( ) { int i=10,k; int *p; p=&k; *p=i; printf("%2d",*p); return 0; }

  13. 零指针与空类型指针 • 零指针:(空指针) • 定义:指针变量值为零 • 表示: int * p=0; p指向地址为0的单元, 系统保证该单元不作它用 表示指针变量值没有意义 #define NULL 0 int *p=NULL:

  14. 零指针与空类型指针 • p=NULL与未对p赋值不同 • 用途: • 避免指针变量的非法引用 • 在程序中常作为状态比较 例 int *p; ...... while(p!=NULL) { ...… }

  15. 零指针与空类型指针 表示不指定p是指向哪一种 类型数据的指针变量 • void *类型指针 • 表示: void *p; • 使用时要进行强制类型转换 例 char *p1; void *p2; p1=(char *)p2; p2=(void *)p1;

  16. 空指针到底是什么? 每一种指针类型都有一个特殊值—— “空指针” :它与同类型的其它所有指针值都不相同, 它“与任何对象或函数的指针值都不相等”。对malloc() 的成功调用也不会返回空指针, 如果失败, malloc() 的确返回空指针, 这是空指针的典型用法:表示“未分配” 或者“尚未指向任何地方” 的指针。 空指针不同于未初始化的指针。 空指针可以确保不指向任何对象或函数; 未初始化指针则可能指向任何地方。 每种指针类型都有一个空指针, 而不同类型的空指针的内部表示可能不尽相同。尽管程序员不必知道内部值, 但编译器必须时刻明确需要那种空指针, 以便在需要的时候加以区分。

  17. *i_pointer i_pointer 10 2000 &i_pointer i …... 整型变量i 2000 10 2001 2002 2003 变量i_pointer 2004 2000 指针变量 2005 2006 …... 含义: 取指针所指向变量的内容 单目运算符 优先级: 2 结合性:自右向左 含义: 取变量的地址 单目运算符 优先级: 2 结合性:自右向左 • 两者关系:互为逆运算 • 理解 指针变量的引用:&与*运算符 • 含义 i_pointer &i &(*i_pointer) i *i_pointer *(&i) i_pointer = &i = &(*i_pointer) i = *i_pointer = *(&i) i_pointer-----指针变量,它的内容是地址量 *i_pointer----指针的目标变量,它的内容是数据 &i_pointer---指针变量占用内存的地址

  18. …... 整型变量i 2000 10 2001 整型变量k 2002 2003 变量i_pointer 2004 2005 2000 指针变量 2006 …... 例k=i; --直接访问 k=*i_pointer; --间接访问 例k=i; k=*i_pointer; 10

  19. …... 整型变量a f86 10 f87 指针变量pa f88 f86 f89 f8a f8b f8c …... a:10 *pa:10 &a:f86(hex) pa:f86(hex) &pa:f88(hex) 例 指针的概念 #include<stdio.h> int main() { int a; int *pa=&a; a=10; printf("a:%d\n",a); printf("*pa:%d\n",*pa); printf("&a:%x(hex)\n",&a); printf("pa:%x(hex)\n",pa); printf("&pa:%x(hex)\n",&pa); return 0; }

  20. …... 2000 指针变量p1 2002 2004 指针变量p 指针变量p2 整型变量a 整型变量b 2006 2008 …... 例7.1 输入两个数,并使其从大到小输出. #include<stdio.h> int main() { int *p1,*p2,*p,a,b; scanf("%d,%d",&a,&b); p1=&a; p2=&b; if(a<b) { p=p1; p1=p2; p2=p;} printf("a=%d,b=%d\n",a,b); printf("max=%d,min=%d\n",*p1,*p2); return 0; } 2008 2006 2006 2008 2006 5 9 5,9 max=9,min=5

  21. 动态存储分配 • 实际编程中可能遇到所需内存空间无法预先确定的情况,需要根据实际的数据的多少来确定。 • C语言提供动态分配内存函数 (stdlib.h)。 • malloc()函数 void *malloc(unsigned int size); 功能:在动态存储区域中分配一个size字节的连续空间。 函数的返回值为指针,它的值是所分配的存储区域的起始地址。 如没有足够的存储空间分配,则返回0(记为NULL)值。

  22. 动态存储分配 /*L7-2.C:动态分配内存范例*/ #include <stdio.h> #include <stdlib.h> char count,*ptr,*p; int main() { ptr=(char *)malloc(30*sizeof(char)); if(ptr==NULL){ puts("memory allocation error."); return (1); } p=ptr; for(count=65;count<91;count++) *p++=count; *p='\0'; puts(ptr); free(ptr); system("PAUSE"); return (0); } ABCD……XYZ

  23. calloc()函数 calloc函数的原型为:void *calloc(unsigned int n, unsigned int size); 函数的功能是:在动态存储区域中分配n个为size字节的连续空间,并将该存储空间自动置初值0。函数的返回值为指针,它的值是所分配的存储区域的起始地址。如分配不成功,则返回0值。 n 表示对象的个数 size 每个对象占用内存单元的字节数 动态分配内存

  24. #include <stdio.h> #include <stdlib.h> int main(){ unsigned num; int *ptr,I,*p; printf("enter the number of type int to allocate:"); scanf("%d",&num); ptr=(int *)calloc(num,sizeof(int)); if(ptr!=NULL){ puts("memory allocation was successful."); p=ptr; for(I=0;I<num;I++) *p++=I; for(I=0;I<num;I++) printf("%d",ptr[I]); printf("\n"); } else puts("memory allocation error."); free(ptr); return (0); } enter the number of type int to allocate:20 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

  25. 动态分配内存 • free()函数 void free(void *ptr) ; 函数的功能是:释放由ptr所指向的存储空间。ptr的值必须是malloc或calloc函数返回的地址。此函数无返回值。

  26. 动态分配内存 realloc函数realloc函数的原型为:void *realloc(void *ptr,size_t size); • realloc函数的作用是对p所指向的存储区进行重新分配即改变大小; • ptr:是已经由malloc或calloc函数分配的存储区的指针; • size:是重新分配的存储区的大小(字节数),新存储区包含着和旧存储区相同的内容,如果新存储区较大,则新增加的部分未被初始化。 • 此函数返回值是新存储区的首地址;如果没有足够的内存空间则返回NULL,此时旧存储区内容不变。

  27. /*L7-3.C*/ #include <stdio.h> #include <string.h> #include <stdlib.h> int main(void) { char *p; /*定义字符指针*/ p=malloc(19); /*申请30个字节的存储区*/ if(!p){ /*检查返回指针的有效性*/ printf(“Allocation error\n”); exit(1); } /*指针无效,终止程序运行*/ strcpy(p,”this is an example”); printf(“%x,%s\n”,p,p); p=realloc(p,20); /*申请重新分配存储区*/ if (!p) { printf(“Allocation error\n”); exit(1); } strcat(p,”.”); printf(“%x,%s\n”,p,p); /*输出字符串首地址和内容*/ free(p); /*释放存储区*/ return 0; } 动态存储分配函数的使用举例

  28. 动态存储分配函数的使用举例 /*L7-4.C*/ #include <stdio.h> #include <stdlib.h> int *get_mem(void) { int *p; p=calloc(100,sizeof(int)); if (!p) { printf(“Allocation error\n”); exit(1); /*指针无效,终止程序运行*/ } return p; }

  29. free() 怎么知道有多少字节需要释放? malloc/free 的实现会在分配的时候记下每一块的 大小, 所以在释放的时候就不必再考虑了。 calloc() 和malloc() 有什么区别?利用calloc的零填充功能安全吗?free() 可以释放calloc()分配的内存吗, 还是需要一个cfree()? calloc(m, n) 本质上等价于p = malloc(m * n); memset(p, 0, m * n); 填充的零是全零, 因此不能确保生成有用的空指针值或浮点零值。 free() 可以安全地用来释放calloc() 分配的内存。

  30. 指针定义和引用 地址运算 多级指针 数组与指针关系、字符指针 指针做函数参数、命令行参数 本讲重点

  31. 指针的运算 • 指针变量的赋值运算 • p=&a; (将变量a地址p) • p=array; (将数组array首地址p) • p=&array[i]; (将数组元素地址p) • p1=p2; (指针变量p2值p1) • 不能把一个整数p, 也不能把p的值整型变量 • 所有类型的指针都可置为NULL 如int i, *p; p=1000; () i=p; () 指针变量与其指向的变量具有相同数据类型

  32. 指针的算术运算 • pi  p id (i为整型数,d为p指向的变量所占字节数) • p++, p--, p+i, p-i, p+=i, p-=i等 • 若p1与p2指向同一数组,p1-p2=两指针间元素个数(p1-p2)/d • p1+p2 无意义

  33. a数组 p a[0] p+1,a+1 a[1] a[2] a[3] a[4] p+i,a+i a[5] a[6] a[7] a[8] p+9,a+9 a[9] 指针的算术运算 例 p指向float数,则 p+1  p+1 4 例 p指向int型数组,且p=&a[0]; 则p+1 指向a[1] 1 例 int a[10]; int *p=&a[2]; p++; *p=1; 例 int a[10]; int *p1=&a[2]; int *p2=&a[5]; 则:p2-p1=3;

  34. 指针变量的关系运算 • 若p1和p2指向同一数组,则: • p1<p2 表示p1指的元素在前; • p1>p2 表示p1指的元素在后; • p1==p2表示p1与p2指向同一元素。 • 若p1与p2不指向同一数组,比较无意义; • p==NULL或p!=NULL

  35. stack[0] p1 p1 p1 stack[1] stack[2] …… 例 7.5 堆栈(stack)是一种先进后出(first-in last-out)的表,好比将若干个盘子堆放起来,每次放或者取一个盘子,最先堆放的盘子最后被取走,将一个数据压入堆栈称为入栈,从堆栈中取走一个数据称为出栈操作。现在编程实现该算法。 push(10) 10 push(3) 3 pop() pop()

  36. #include <stdio.h> #include <stdlib.h> #define SIZE 50 void push(int i); /*入栈函数使用说明*/ int pop(void); /*出栈函数使用说明*/ int *tos,*p1,stack[SIZE]; /*堆栈、栈顶及栈底指针定义*/ int main(void) { int value; tos=stack; p1=stack; do{ printf(“输入一个整数:“); scanf(“%d”,&value); if (value!=0) push(value); else printf(“出栈数据是%d\n”,pop()); }while(value!=-1); return 0; }

  37. /*将入栈操作定义成用户自定义函数*/ void push(int i) { p1++; if(p1==(tos+SIZE)) /*判断堆栈是否已满*/ { printf(“堆栈已满\n”); exit(1); } *p1=i; } /*将出栈操作定义成自定义函数*/ int pop(void) { if(p1==tos) /*判断堆栈是否空*/ { printf(“堆栈空\n”); exit(1); } p1--; return *(p1+1); }

  38. 指针定义和引用 地址运算 多级指针 数组与指针关系、字符指针 指针做函数参数、命令行参数 本讲重点

  39. 指针变量与数组 指向数组元素的指针变量 p array[0] array[1] array[2] array[3] ... array[9] 整型指针p &array[0] 例 int array[10]; int *p; p=&array[0]; // p=array; 或 int *p=&array[0]; 或 int *p=array; 数组名是表示数组首地址的地址常量, 就是数组的指针。

  40. 地址 p[0] a[0] a[0] a[0] *p *a 元素 地址 元素 *(p+1) *(a+1) p[1] a[1] a[1] a[1] *(p+2) *(a+2) p[2] a[2] a[2] a[2] p a a[3] a[3] p+1 a+1 p+2 a+2 a[9] a[9] p[9] *(p+9) *(a+9) a[9] ... ... p+9 a+9 指针法 下标法 数组元素表示方法 [] 变址运算符 a[i] *(a+i) a[i]  p[i]  *(p+i) *(a+i)

  41. pa a[0] 1 a[1] 2 a[2] 3 a[3] 4 a[4] 5 例7.6数组元素的引用方法 #include <stdio.h> int main() { int a[5],*pa,i; for(i=0;i<5;i++) a[i]=i+1; pa=a; for(i=0;i<5;i++) printf("*(pa+%d):%d\n",i,*(pa+i)); for(i=0;i<5;i++) printf("*(a+%d):%d\n",i,*(a+i)); for(i=0;i<5;i++) printf("pa[%d]:%d\n",i,pa[i]); for(i=0;i<5;i++) printf("a[%d]:%d\n",i,a[i]); return 0; }

  42. 例 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) ()

  43. a 0 5 1 8 p p 2 7 3 6 4 2 5 7 6 3 例7.7注意指针变量的运算 例 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]); return 0; } 6 5 6

  44. a 0 5 1 8 p p p p p p p p 2 7 3 6 4 2 5 7 6 3 例7.8注意指针的当前值 #include <stdio.h> 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); return 0; } p=a; 指针变量可以指到数组后的内存单元

  45. 一级指针变量与一维数组的关系 int *p与int q[10] • 数组名是指针(地址)常量 • p=q; p+i 是q[i]的地址 • 数组元素的表示方法:下标法和指针法, 即若p=q, 则 p[i]  q[i]  *(p+i)  *(q+i) • 形参数组实质上是指针变量,即int q[ ]  int *q • 在定义指针变量(不是形参)时,不能把int *p 写成int p[]; • 系统只给p分配能保存一个指针值的内存区(一般2字节);而给q分配2*10字节的内存区

  46. 在C语言中“指针和数组等价”到底是什么意思?在C语言中“指针和数组等价”到底是什么意思? 不表示它们相同, 甚至也不能互换。 可以用指针方便的访问数组或者模拟数组。 等价的基础:一旦数组出现在表达式中, 编译器会隐式地生成一个指向数组第一个成员地指针, 就像程序员写出了&a[0] 一样。 例外的情况是, 数组为sizeof 或&操作符的操作数, 或者为字符数组的字符串初始值。 这个定义的后果:编译器不严格区分数组下标操作符和指针。 a[i], 根据上边的规则, 数组蜕化为指针然后按照指针变量的方式如p[i] 那样寻址, 尽管最终的内存访问并不一样。如果你把数组地址赋给指针:p = a; 那么p[3] 和a[3] 将会访问同样的成员。

  47. string string[0] I string[1] string[2] l string[3] o string[4] v string[5] e string[6] string[7] C string[8] h string[9] i string[10] n string[11] a string[12] ! string[13] \0 指针与字符串 字符串 • 字符数组 #include <stdio.h> int main( ) { char string[]=“I love China!”; printf(“%s\n”,string); printf(“%s\n”,string+7); return 0; }

  48. string I l o string v e C h i n a ! \0 用字符指针实现 字符指针初始化:把字符串首地址赋给string  char *string; string=“I love China!”; #include <stdio.h> int main( ) { char *string=“I love China!”; printf(“%s\n”,string); string+=7; while(*string) { putchar(string[0]); string++; } return 0; } *string!=0

  49. 字符指针变量与字符数组的区别 char *cp;与char str[20]; • str由若干元素组成,每个元素放一个字符;而cp中存放字符串首地址 • char str[20]; str=“I love China!”; () char *cp; cp=“I love China!”; () • str是地址常量;cp是地址变量 • cp接受键入字符串时,必须先开辟存储空间 改为: char *cp,str[10]; cp=str; scanf(“%s”,cp); () 例 char str[10]; scanf(“%s”,str); () 而 char *cp; scanf(“%s”, cp); () 或改为: char *cp; cp=malloc(20) ; scanf(“%s”,cp); ()

  50. 如果字符指针在定义是初始化,如:char *s=”I am a teacher”;以后使用字符指针s时,字符串的长度不能超过”I am a teacher”的长度,否则数组溢出,而编译程序不进行这种错误的检查,将可能造成“死机”或者程序结果错误。 字符指针变量与字符数组的区别 • 字符数组名str是非左值表达式,而字符指针pstr是左值表达式。因而以下表达式是正确的:char str[20],*pstr=str; • pstr++使pstr指向下一个字符下列表达式是错误的:str++数组名是常量,不能参与自增或自减运算 • str=”I am a teacher”不能向数组名赋值

More Related