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

Slides:



Advertisements
Similar presentations
第九章 指针 西安工程大学.
Advertisements

第七章 指针 计算机公共教学部.
第六章 指针 指针的概念 指针变量 指针与数组 指针与函数 返回指针值的函数.
第7章 指针 存储地址的变量的类型就是指针类型 能直接对内存地址操作, 实现动态存储管理 容易产生副作用, 初学者常会出错
电子成绩单项目实现.
二级指针与二维数组.
C语言程序设计基础 第10章 指针进阶 刘新国.
10.1 二级指针 10.2 指针与二维数组 10.3 指针的动态存储分配 10.4 函数指针 10.5 main函数的参数
C语言程序设计教程 单位:德州学院计算机系.
第一章 C语言概述 计算机公共教学部.
第8章 指针 ● 8.1 指针简介 ● 8.2 指针变量的操作 ● 8.3 数组与指针 ● 8.4 二维数组与指针 ●本章小结 ●本章练习.
C语言基础——指针的高级应用 Week 05.
第6章 指针 6.1 指针的概念 6.2 变量与指针 6.3 数组与指针 6.4 字符串与指针 6.5 函数与指针 6.6 返回指针值的函数
6.4 字符串与指针 1. 用字符数组存放一个字符串.
第六节 二维数组和指针 二维数组的地址 对于一维数组: (1)数组名array表示数组的首地址, 即array[0]的地址;
第九章 指针 目录 指针与指针变量的概念 变量的指针和指向变量的指针变量 数组的指针和指向数组的指针变量
第九章 系 统 安 全 性 9.1 结构体 9.2 结构体型数组  9.3 结构体型指针 9.4 内存的动态分配 9.5 共用体
第7章 结构体、联合体和枚举类型 本章导读 本章主要知识点 《 C语言程序设计》 (Visual C++ 6.0环境)
第5章 函数与模块化设计 学习目的与要求: 掌握函数的定义及调用方法 理解并掌握参数的传递方法 理解函数的嵌套与递归调用
程序设计基础.
第6章 指针 学习目的与要求: 了解指针的概念和相关术语 熟练掌握指向变量、数组和字符串的指针变量的使用方法 了解指向函数的指针变量
指 针 为什么要使用指针 指针变量 指针与数组 返回指针值的函数 动态内存分配 通过指针引用字符串 指向函数的指针 小 结 习 题.
C 程式設計— 指標.
目录 10.1 指针的基本概念 10.2 指向变量的指针变量 10.3 指向数组的指针变量 10.4 指向函数的指针变量和指针型函数
第 十 章 指 针.
项目六 用指针优化学生成绩排名 项目要求 项目分析
目录 第八章 数组 1 简单学生成绩管理系统的开发 2 一维数组 3 多维数组 4 字符数组 5 数组作函数参数.
第七章 函数 目录 有参的加法函数的开发 函数定义的一般形式 函数参数和函数的值 函数的调用
Chap 8 指针 8.1 寻找保险箱密码 8.2 角色互换 8.3 冒泡排序 8.4 电码加密 8.5 任意个整数求和*
C语言程序设计 李祥.
第八章 函数.
第8章 善于利用指针 8.1 指针是什么 8.2 指针变量 8.3 通过指针引用数组 8.4 通过指针引用字符串 8.5 指向函数的指针
第8章 善于利用指针 8.1 指针是什么 8.2 指针变量 8.3 通过指针引用数组 8.4 通过指针引用字符串 8.5 指向函数的指针
二维数组的指针表示 与复杂的指针例子 专题研讨课之三.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
6.4.1指针与二维数组 1、二维数组结构的分析 设有数组定义为:int a[3][4]; 则有: a表示数组在内存中的首地址。
C语言 程序设计基础与试验 刘新国、2012年秋.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第8章 指针.
第八章 使用指针.
第十章 指针.
欲穷千里,更上层楼 第十章 指 针 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; 能很方便地使用数组和字符串; 并能象汇编语言一样处理内存地址,从而编出精练而高效的程序。指针极大地丰富了C语言的功能。 学习指针是学习C语言中最重要的一环,
第五章 习题课 电子信息与计算机科学系 曾庆尚.
数组 梁春燕 华电信息管理教研室.
第1章 概述 本章要点: C语言程序结构和特点 C语言程序的基本符号与关键字 C语言程序的编辑及运行 学习方法建议:
9.1 地址、指针和变量 9.2 指针运算 9.3 指针与数组 9.4 函数与指针 9.5 程序综合举例 9.6 上机实训.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C语言概述 第一章.
C语言大学实用教程 第5章 函数与程序结构 西南财经大学经济信息工程学院 刘家芬
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
C语言复习3----指针.
C语言大学实用教程 第6章 数组 西南财经大学经济信息工程学院 刘家芬
C语言大学实用教程 第7章 指针 西南财经大学经济信息工程学院 刘家芬
函数 概述 模块化程序设计 基本思想:将一个大的程序按功能分割成一些小模块, 特点: 开发方法: 自上向下,逐步分解,分而治之
第八章 指標 (Pointer).
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第6讲 指针与引用 6.1 指针 6.2 引用.
第六章 指针 C++程序设计中使用指针可以: 使程序简洁、紧凑、高效 有效地表示复杂的数据结构 动态分配内存 得到多于一个的函数返回值.
7.1 C程序的结构 7.2 作用域和作用域规则 7.3 存储属性和生存期 7.4 变量的初始化
第十章 指针 指针是C语言的重要概念,是C语言的特色,是C语言的精华。 10.1 地址和指针的概念 内存中的每一个字节都有一个地址。
第九章 指针.
第九节 赋值运算符和赋值表达式.
第8章 善于利用指针 8.1 指针是什么 8.2 指针变量 8.3 通过指针引用数组 8.4 通过指针引用字符串 8.5 指向函数的指针
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
C程序设计.
C语言程序设计 第8章 指针.
Chap 7 数 组 7.1 排序问题 7.2 找出矩阵中最大值所在的位置 7.3 进制转换.
第九章 指针 C程序设计中使用指针可以: 使程序简洁、紧凑、高效 有效地表示复杂的数据结构 动态分配内存 得到多于一个的函数返回值.
第八章 指 针 北京邮电大学出版社.
Presentation transcript:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

例 指针的概念 #include<stdio.h> int main() { int a; int *pa=&a; a=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; } …... f86 f8a f8c f8b 整型变量a 10 指针变量pa f87 f88 f89

例7.1 输入两个数,并使其从大到小输出. 5 9 …... #include<stdio.h> int main() 例7.1 输入两个数,并使其从大到小输出. …... 指针变量p1 指针变量p 2000 2008 2002 2004 2006 指针变量p2 整型变量b 整型变量a #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; } 2006 2008 2006 2008 2006 5 9 5,9 max=9,min=5

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

动态存储分配 /*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

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

#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; 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

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

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

动态存储分配函数的使用举例 /*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) { exit(1); } strcat(p,”.”); printf(“%x,%s\n”,p,p); /*输出字符串首地址和内容*/ free(p); /*释放存储区*/ return 0; } 动态存储分配函数的使用举例

动态存储分配函数的使用举例 /*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;

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

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

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

指针的算术运算 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 无意义

指针的算术运算 1 a数组 p 例 p指向float数,则 p+1  p+1 4 a[0] p+1,a+1 a[1] p+i,a+i p+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;

指针变量的关系运算 若p1和p2指向同一数组,则: p1<p2 表示p1指的元素在前; p1>p2 表示p1指的元素在后; p==NULL或p!=NULL

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

#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; }

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

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

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

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

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

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

例7.7 注意指针变量的运算 例 int main() { int a []={5,8,7,6,2,7,3}; 例7.7 注意指针变量的运算 5 8 7 6 2 3 1 4 a 例 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; } p 6 p 5 6

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

一级指针变量与一维数组的关系 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字节的内存区

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

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

用字符指针实现 string *string!=0 string 字符指针初始化:把字符串首地址赋给string I l o v e C h i string 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 *string!=0

字符指针变量与字符数组的区别 char *cp; 与 char str[20]; 改为: char *cp,str[10]; 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); ()

字符指针变量与字符数组的区别 pstr++ 使pstr指向下一个字符 下列表达式是错误的: str++ 数组名是常量,不能参与自增或自减运算 如果字符指针在定义是初始化,如: 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” 不能向数组名赋值

例7.9 输入两个字符串,变成比较两个字符串的大小。 例7.9 输入两个字符串,变成比较两个字符串的大小。 分析: 程序中定义两个字符数组s和t存放输入的两个字符串 定义两个字符指针ps和pt,它们的初值为分别指向s和t两个字符串首字符。 逐个判断s和t的每一对字符是否相等,从左到右直到找到第一对不相等的字符或者所有字符判断完成为止。

#include <stdio.h> #define SIZE 20 int main(void) { char s[SIZE],t[SIZE],*ps,*pt; int n; ps=s; pt=t; gets(ps); gets(pt); while((*ps==*pt)&&(*ps!='\0')) { ps++;pt++; } n=*ps-*pt; if (n>0) printf("%s>%s\n",s,t); else if(n<0) printf("%s<%s\n",s,t); printf("%s = %s\n",s,t); return 0; }

例 7.10 编程从键盘输入一个字符串,然后按照字符顺序从小到大进行排列,并删除重复的字符。 分析:定义字符数组str表示输入的字符串,程序中采用冒泡排序法进行排序。

#include <stdio.h> #include <string.h> int main(void) { char str[100],*p,*q,*r,c; printf(“输入字符串:”); gets(str); /*排序*/ for(p=str;*p;p++) { for(q=r=p;*q;q++) if(*r>*q) r=q; if(r!=p) { c=*r;*r=*p;*p=c; } } /*删除重复的字符*/ { for(q=p;*p==*q;q++); if(p!=q) strcpy(p+1,q); printf(“结果字符串是:%s\n”,str); return 0;

多维数组的指针表示 二维数组的地址 int array[10]; array 对于一维数组: (3)array+i是元素array[i]的地址 (4)array[i]  *(array+i)

int a[3][4]; a a+1 a+2 行指针与列指针 *(a[0]+1) *(*(a+0)+1) 2000 2000 a[0] 2008 2016 a a+1 a+2 2000 2002 2008 2010 2016 2018 a[0][0] a[0][1] a[1][0] a[1][1] a[2][0] a[2][1] a[0][2] a[0][3] a[1][2] a[1][3] a[2][2] a[2][3] a[0]+1 a[1]+1 a[2]+1 *(a+0)+1 *(a+1)+1 *(a+2)+1 对于二维数组: (1)a是数组名, 包含三个元素 a[0],a[1],a[2] (2)每个元素a[i] 又是一个一维 数组,包含4个 元素 基类型

int a[3][4]; a a+1 a+2 a[0] a[1] a[2] 2000 2008 2016 2002 2010 2018 a+i-----第i行的首地址 a[i]  *(a+i)------第i行第0列的元素地址 a[i]+j  *(a+i)+j -----第i行第j列的元素地址 *(a[i]+j)  *(*(a+i)+j)  a[i][j] a+i=&a[i]=a[i]=*(a+i) =&a[i][0], 值相等,含义不同 a+i  &a[i],表示第i行首地址,指向行 a[i]  *(a+i)  &a[i][0],表示第i行第0列元素地址,指向列

int a[3][4]; 地址表示: (1) a+1 行指针 (2) &a[1][0] (3) a[1] (4) *(a+1) 列指针 地址表示: (1) &a[1][2] (2) a[1]+2 (3) *(a+1)+2 (4)&a[0][0]+1*4+2 二维数组元素表示形式: (1)a[1][2] (2)*(a[1]+2) (3)*(*(a+1)+2) (4)*(&a[0][0]+1*4+2)

*(a[1]+2),*(*(a+1)+2),a[1][2] 表示形式 含义 地址 a 二维数组名,数组首地址 a[0],*(a+0),*a 第0行第0列元素地址 a+1 第1行首地址 a[1],*(a+1) 第1行第0列元素地址 a[1]+2,*(a+1)+2,&a[1][2] 第1行第2列元素地址 *(a[1]+2),*(*(a+1)+2),a[1][2] 第1行第2列元素值 2000 2008 2012 13

二维数组的指针变量 p 例7.11 指向二维数组元素的指针变量 #include <stdio.h> int main() int a[3][4]; a[0][0] a[0][1] a[1][0] a[1][1] a[2][0] a[2][1] a[0][2] a[0][3] a[1][2] a[1][3] a[2][2] a[2][3] p 例7.11 指向二维数组元素的指针变量 #include <stdio.h> int 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); } return 0; p=*a; p=&a[0][0]; p=(int *)a; p=a;

指向一维数组的指针变量 a a+1 a+2 p p+1 p+2 可让p指向二维数组某一行 如 int a[3][4], (*p)[4]=a; 定义形式: 数据类型 (*指针名)[一维数组维数]; 例 int (*p)[4]; int a[3][4]; a[0][0] a[0][1] a[1][0] a[1][1] a[2][0] a[2][1] a[0][2] a[0][3] a[1][2] a[1][3] a[2][2] a[2][3] a a+1 a+2 p p+1 p+2 可让p指向二维数组某一行 如 int a[3][4], (*p)[4]=a; p[0]+1或 *p+1 p[1]+2或 *(p+1)+2 *(*p+1)或 (*p)[1] *(*(p+1)+2) p的值是一维数组的 首地址,p是行指针 ( )不能少 int (*p)[4]与int *p[4]不同 一维数组指针变量维数和 二维数组列数必须相同

例 一维数组指针变量举例 p p p #include <stdio.h> int main() 例 一维数组指针变量举例 int a[3][4]; a[0][0] a[0][1] a[1][0] a[1][1] a[2][0] a[2][1] a[0][2] a[0][3] a[1][2] a[1][3] a[2][2] a[2][3] p #include <stdio.h> int main() { static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; int i,j,(*p)[4]; for(p=a,i=0;i<3;i++,p++) for(j=0;j<4;j++) printf("%d ",*(*p+j)); printf("\n"); return 0; } p  p[0][j] p p=a[0]; p=*a; p=&a[0][0]; p=&a[0]; p=a[0]; p=*a; p=&a[0][0]; p=&a[0];

字符串与数组关系 字符串用一维字符数组存放; 字符数组具有一维数组的所有特点: 数组名是指向数组首地址的地址常量; 数组元素的引用方法可用指针法和下标法; 区别 存储格式:字符串结束标志; 赋值方式与初始化; 输入输出方式:%s %c scanf(“%s”,str); printf(“%s”,str); gets(str); puts(str); char str[]={“Hello!”}; () char str[]=“Hello!”; () char str[]={‘H’,‘e’,‘l’,‘l’,‘o’,‘!’}; () char *cp=“Hello”; () int a[]={1,2,3,4,5}; () int *p={1,2,3,4,5}; () char str[10],*cp; int a[10],*p; str=“Hello”; () cp=“Hello!”; () a={1,2,3,4,5}; () p={1,2,3,4,5}; ()

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

例 二维数组与指针运算 #include <stdio.h> int main() { int a[3][4]={{1,2,3,4},{3,4,5,6},{5,6,7,8}}; int i; int (*p)[4]=a,*q=a[0]; for(i=0;i<3;i++) { if(i==0) (*p)[i+i/2]=*q+1; else p++,++q; } printf("%d,",a[i][i]); printf("%d,%d\n",*((int *)p),*q); return 0; p q p q p q 1 2 3 4 5 6 7 8 2 2,4,7,5,3

若int a[3][4]; int (*p1)[4]=a; int *p2=a[0]; 二维数组的指针作函数参数 用指向变量的指针变量; 用指向一维数组的指针变量; 用二维数组名。 实参 形参 数组名int x[][4] 指针变量int (*q)[4] 指针变量int (*q)[4] 数组名a 指针变量p1 若int a[3][4]; int (*p1)[4]=a; int *p2=a[0]; 指针变量p2 指针变量int *q

二维数组与一维数组指针变量的关系 int a[5][10] 与 int (*p)[10] 二维数组名是一个指向有10个元素的一维数组的指针常量; p=a+i 使 p指向二维数组的第i行; *(*(p+i)+j)  a[i][j] 二维数组形参实际上是一维数组指针变量, 即 int x[ ][10]  int (*x)[10] 变量定义(不是形参)时两者不等价; 系统只给p分配能保存一个指针值的内存区(一般2字节);而给a分配2*5*10字节的内存区;

指针数组 指针数组:用于处理二维数组或多个字符串 定义:数组中的元素为指针变量 定义形式:[存储类型] 数据类型 *数组名[数组长度说明]; 例 int *p[4]; 指针数组赋值与初始化 指针本身的存储类型 指针所指向变量的数据类型 赋值: main() { int b[2][3],*pb[2]; pb[0]=b[0]; pb[1]=b[1]; …….. } int *pb[2] pb[0] pb[1] int b[2][3] 1 2 3 4 6 区分int *p[4]与int (*p)[4] 初始化: int main() { int b[2][3],*pb[ ]={b[0],b[1]}; …….. } int *pb[2] pb[0] pb[1] int b[2][3] 1 2 3 4 6

指针数组赋值与初始化 赋值: 或: int main() { char a[]="Fortran"; { char *p[4]; char b[]="Lisp"; char c[]="Basic"; char *p[4]; p[0]=a; p[1]=b; p[2]=c; p[3]=NULL; …….. } 或: { char *p[4]; p[0]= "Fortran"; p[1]= "Lisp"; p[2]= "Basic"; p[3]=NULL; L i s p \0 F o r t r a n \0 B a s i c \0 p[0] p[1] p[2] p[3]

指针数组赋值与初始化 初始化: int main() { char *p[]={"Fortran", "Lisp", "Basic",NULL}; …….. } L i s p \0 F o r t r a n \0 B a s i c \0 p[0] p[1] p[2] p[3]

二维数组与指针数组区别 指针数组元素的作用相当于二维数组的行名 二维数组存储空间固定 但指针数组中元素是指针变量 char name[5][9]={“gain”,“much”,“stronger”, “point”,“bye”}; g a i n \0 s t r o n g e r \0 p o i n t \0 m u c h \0 b y e \0 g a i n \0 s t r o n g e r \0 p o i n t \0 m u c h \0 name[0] name[1] name[2] name[3] name[4] b y e \0 char *name[5]={“gain”,“much”,“stronger”, “point”,“bye”}; 二维数组存储空间固定 字符指针数组相当于可变列长的二维数组 分配内存单元=数组维数*2+各字符串长度 指针数组元素的作用相当于二维数组的行名 但指针数组中元素是指针变量 二维数组的行名是地址常量

例 用指针数组处理二维数组 #include <stdio.h> int main() 例 用指针数组处理二维数组 #include <stdio.h> int main() { int b[2][3],*pb[2]; int i,j; for(i=0;i<2;i++) for(j=0;j<3;j++) b[i][j]=(i+1)*(j+1); pb[0]=b[0]; pb[1]=b[1]; for(j=0;j<3;j++,pb[i]++) printf("b[%d][%d]:%2d\n",i,j,*pb[i]); return 0; } int *pb[2] pb[0] pb[1] int b[2][3] b[0][0] *pb[0] b[0][1] *(pb[0]+1) b[0][2] *(pb[0]+2) b[1][0] *pb[1] b[1][1] *(pb[1]+1) b[1][2] *(pb[1]+2) 1 2 3 4 6

/*例7.12 输入一个表示月份的整数,输出该月份的英文名称以及各个月份英文名称的首字母。 */ #include <stdio.h> int main(void) { char *month[]= {“Illegal month”, ”January”, ”February”, ”March”, ”April”,“May”,”June”,”July”,”Augest”,”September”,”October”, ”November”,”December”}; int i; /*输入月份*/ printf(“输入月份数字:”); scanf(“%d”,&i); /*输出月份名称*/ if (i>=1&&i<=12) printf(“%d月名称%s\n”,i,month[i]); else printf(“%d是%s\n”,month[0]); /*输出每个月份名称的首字母*/ for(i=1;i<=12’i++) printf(‘%c\t”,*month[i]); printf(“\n”); return 0 ; }

例7.13 输入若干字符串,按照从小到大的顺序排序并输出排序的结果。 #include <stdio.h> #include <string.h> #include <stdlib.h> #define NUMBER 6 #define LENGTH 30 int main(void) { char *string[NUMBER]; int i,j,k; char *temp; for(i=0;i<NUMBER;i++) { string[i]=(char *)malloc(LENGTH); gets(string[i]); }

for(i=0;i<NUMBER-1;i++) { k=i; for(j=i+1;j<NUMBER;j++) if(strcmp(string[k],string[j])>0) k=j; if(k!=i) { temp=string[i]; string[i]=string[k]; string[k]=temp; } for(i=0;i<NUMBER;i++) printf("%s\n",string[i]); return 0;

多级指针:指向指针的指针 定义: 指向指针的指针 一级指针:指针变量中存放目标变量的地址 例 int *p; int i=3; p=&i; 单级间接寻址 二级指针:指针变量中存放一级指针变量的地址 例 int **p1; int *p2; int i=3; p2=&i; p1=&p2; **p1=5; p1 &p2 &i 3 P2(指针变量) i(整型变量) 二级指针 一级指针 目标变量 二级间接寻址

定义形式:[存储类型] 数据类型 **指针名; 如 char **p; *p是p间接指向对象的地址 **p是p间接指向对象的值 指针本身的存储类型 最终目标变量的数据类型 例 int i=3; int *p1; int **p2; p1=&i; p2=&p1; **p=5; i p1 p2 3 &i &p1 **p2, *p1 *p2 例 int i, **p; p=&i; ()//p是二级指针,不能用变量地址为其赋值 多级指针 例 三级指针 int ***p; 四级指针 char ****p;

/*L7-14.C*/ #include <stdio.h> int main(void) { float f,*p,**newbalance; f=100.1; p=&f; newbalance=&p; printf(“%f\n”,**newbalance); return 0; }

/*L7-15.C*/ #include <stdio.h> int main(void) { int i; char **p,*name[2]={“1234567”,”abcdefgh”}; for(p=name;p<name+2;p++) printf(“%s\n”,*p); for(i=0,p=name;p<name+2;p++,i++) printf(“%c\n”,*(*p+i)); return 0; }

· · · /*L7-16.C: 多级指针的应用示例*/ #include <stdio.h> int main() { char *c[]={“ENTER”,”NEW”,”POINT”,”FIRST”}; char **cp[4];char ***cpp; cp[3]=c;cp[2]=c+1;cp[1]=c+2;cp[0]=c+3; cpp=cp; printf(“%s”,**++cpp); /*1*/ printf(“%s”,*--*++cpp+3); /*2*/ printf(“%s”,*cpp[-2]+3); /*3*/ printf(“%s\n”,cpp[-1][-1]+1); /*4*/ return 0; } 程序运行结果: POINTERSTEW cp c · cpp F I R S T \0 P O I N T \0 N E W \0 E N T E R \0 · cpp · cpp

例 用二级指针处理字符串 /*L7-17.C*/ #include <stdio.h> #define NULL 0 例 用二级指针处理字符串 /*L7-17.C*/ #include <stdio.h> #define NULL 0 int main() { char **p; char *name[]={"hello","good","world","bye",""}; p=name+1; printf("%o : %s ", *p,*p); p+=2; while(**p!=NULL) printf("%s\n",*p++); return 0; } 用*p可输出地址(%o或%x), 也可用它输出字符串(%s) name[0] name[1] name[2] name[3] name[4] char *name[5] world bye \0 hello good name p *(p++) p 644 : good bye

二级指针与指针数组的关系 int **p 与 int *q[10] 指针数组名是二级指针常量; p=q; p+i 是q[i]的地址; 指针数组作形参,int *q[ ]与int **q完全等价;但作为变量定义两者不同; 系统只给p分配能保存一个指针值的内存区;而给q分配10块内存区,每块可保存一个指针值.

指针的数据类型 定义 含义 int i; 定义整型变量i p为指向整型数据的指针变量 int *p; int a[n]; int *p[n]; int (*p)[n]; int f(); int *p(); int (*p)(); int **p; 定义整型变量i p为指向整型数据的指针变量 定义含n个元素的整型数组a n个指向整型数据的指针变量组成的指针数组p p为指向含n个元素的一维整型数组的指针变量 f为返回整型数的函数 p为返回指针的函数,该指针指向一个整型数据 p为指向函数的指针变量,该函数返回整型数 p为指针变量,它指向一个指向整型数据的指针变量 指针的数据类型

例 下列定义的含义 (1)int *p[3]; (2)int (*p)[3]; (3)int *p(int); (4)int (*p)(int); (5)int *(*p)(int); (6)int (*p[3])(int); (7)int *(*p[3])(int); 指针数组 指向一维数组的指针 返回指针的函数 指向函数的指针,函数返回int型变量 指向函数的指针,函数返回int 型指针 函数指针数组,函数返回int型变量 函数指针数组,函数返回int型指针

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

指针变量作为函数参数——指针传递参数地址 特点:共享内存,“双向”传递 …... 2000 2008 200A 2002 2004 2006 例7.18 将数从大到小输出 变量a 变量b (main) void swap(int x,int y) { int temp; temp=x; x=y; y=temp; } int main() { int a,b; scanf("%d,%d",&a,&b); if(a<b) swap(a,b); printf("\n%d,%d\n",a,b); return 0; 5 9 值传递 5, 9 5,9

例 将数从大到小输出 使用指针 传递参数地址 void swap(int *p1, int *p2) { int p; p=*p1; } int 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",a,b); return 0; 使用指针 传递参数地址 …... 2000 2008 200A 2002 2004 2006 200C 200E 2010 ... 整型变量a 整型变量b (main) 指针pointer_1 指针pointer_2 9 5 5 9 2000 2002 5,9 9,5

C有“按引用传递” 吗? 没有 另一方面, 类似函数的预处理宏可以提供一种“按名称传递”的形式。 C 总是按值传递。你可以自己模拟按引用传递, 定义接受指针的函数, 然后在调用时使用& 操作符。事实上, 当你向函数传入数组(传入指针的情况参见问题6.4 及其它) 时, 编译器本质上就是在模拟按引用传递。但是C 没有任何真正等同于正式的按引用传递或C++ 的引用参数的东西。 另一方面, 类似函数的预处理宏可以提供一种“按名称传递”的形式。

例 将数从大到小输出 swap(int *p1, int *p2) { int *p; *p=*p1; int x; *p1=*p2; } int 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",a,b); return 0; int x; int *p=&x; 指针变量在使用前 必须赋值! 编译警告! 结果不对!

例7.19 交换两个数---指针作函数的参数 swap(int *p1,int *p2) { int p; p=*p1; *p1=*p2; 例7.19 交换两个数---指针作函数的参数 swap(int *p1,int *p2) { int p; p=*p1; *p1=*p2; *p2=p; } main() { int a,b; scanf("%d,%d",&a,&b); printf(“a=%d,b=%d\n”,a,b); printf(“swapped:\n”); swap(&a,&b); printf(”a=%d,b=%d\n",a,b); a 5 9 b 调前: a 5 9 b 调swap: p1 &a &b p2 a 9 5 b 交换: p1 &a &b p2 a 9 5 b 返回:

数组作为函数参数 数组元素作函数实参——值传递 #include <stdio.h> main() { int a[10],b[10],i,n=0,m=0,k=0; printf("Enter array a:\n"); for(i=0;i<10;i++) scanf("%d",&a[i]); printf("Enter array b:\n"); scanf("%d",&b[i]); { if(large(a[i],b[i])==1) n=n+1; else if(large(a[i],b[i])==0) m=m+1; else k=k+1; } /* Output */ int large(int x,int y) { int flag; if(x>y) flag=1; else if(x<y) flag=-1; else flag=0; return(flag); 数组作为函数参数 数组元素作函数实参——值传递 例 两个数组大小比较 4 3 2 1 5 a 56 23 12 10 76 88 b 21 43 98 66 54 a和b为有10个元素的整型数组 比较两数组对应元素 变量n,m,k记录a[i]>b[i], a[i]==b[i], a[i]<b[i]的个数 最后 若n>k,认为数组a>b 若n<k,认为数组a<b 若n==k,认为数组a==b i n=0 m=0 k=1 i n=0 m=1 k=1 i n=1 m=1 k=1 i n=1 m=1 k=2 i n=2 m=1 k=2 i n=3 m=1 k=2 n=0 m=0 k=0

数组名作函数参数 “地址传递”:值传递的特例,形参是指针类型 在主调函数与被调函数分别定义数组,且类型应一致 形参数组大小(多维数组第一维)可不指定 形参数组名是地址变量 实参 形参 数组名 指针变量

一维数组名作函数参数 例 求学生的平均成绩 float average(int stu[10], int n) { int i; 例 求学生的平均成绩 形参用数组定义, int stu[ ] float average(int stu[10], int n) { int i; float av,total=0; for( i=0; i<n; i++ ) total += stu[i]; av = total/n; return av; } /*L7-20.C*/ #include <stdio.h> float average(int stu[10], int n); int main() { int score[10], i; float av; printf("Input 10 scores:\n"); for( i=0; i<10; i++ ) scanf("%d", &score[i]); av=average(score,10); printf("Average is:%.2f", av); return 0; } . 2 1 9 score 56 23 12 …. 88 stu 实参用数组名

#include <stdio.h> void swap2(int x,int y) { int z; 例7.21 数组元素与数组名作函数参数比较 #include <stdio.h> void swap2(int x,int y) { int z; z=x; x=y; y=z; } int main() { int a[2]={1,2}; swap2(a[0],a[1]); printf("a[0]=%d\na[1]=%d\n",a[0],a[1]); return 0; 值传递 1 2 a 调用 a[0] a[1] x y 2 1 x y 交换 1 2 a 调用前 a[0] a[1] 1 2 a 返回

#include <stdio.h> void swap2(int x[]) { int z; 例7.21 数组元素与数组名作函数参数比较 #include <stdio.h> void swap2(int x[]) { int z; z=x[0]; x[0]=x[1]; x[1]=z; } int main() { int a[2]={1,2}; swap2(a); printf("a[0]=%d\na[1]=%d\n",a[0],a[1]); return 0; 地址传递 1 2 a 调用前 1 2 a x 调用 2 1 a x 交换 2 1 a 返回

例7.22 数组排序----简单选择排序 a k array j j i=0 j k j k j j j j j int main() void sort(int array[],int n) { int i,j,k,t; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(array[j]<array[k]) k=j; if(k!=i) { t=array[i]; array[i]=array[k]; array[k]=t; } int main() { int a[10],i; for(i=0;i<10;i++) scanf("%d",&a[i]); sort(a,10); printf("%d ",a[i]); printf("\n"); return 0; 例7.22 数组排序----简单选择排序 1 2 3 4 5 6 7 8 9 a 9 k array 49 68 57 32 9 99 27 13 76 88 j j i=0 j k 49 j k j j j j j

例7.23 数组排序----简单选择排序 int main() { int a[10],i; for(i=0;i<10;i++) 例7.23 数组排序----简单选择排序 void sort(int array[],int n) { int i,j,k,t; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(array[j]<array[k]) k=j; if(k!=i) { t=array[i]; array[i]=array[k]; array[k]=t; } int main() { int a[10],i; for(i=0;i<10;i++) scanf("%d",&a[i]); sort(a,10); printf("%d ",a[i]); printf("\n"); return 0; }

例7.23 数组排序----简单选择排序 a array k j k i=1 j k j j j k j k j j 13 68 49 68 例7.23 数组排序----简单选择排序 1 2 3 4 5 6 7 8 9 a 49 68 57 32 99 27 13 76 88 array 13 k j k i=1 j k j j j k 68 j k j j

例7.24 数组排序----简单选择排序 void sort(int array[],int n) int main() 例7.24 数组排序----简单选择排序 void sort(int array[],int n) { int i,j,k,t; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(array[j]<array[k]) k=j; if(k!=i) { t=array[i]; array[i]=array[k]; array[k]=t; } int main() { int a[10],i; for(i=0;i<10;i++) scanf("%d",&a[i]); sort(a,10); printf("%d ",a[i]); printf("\n"); return 0; }

例7.24 数组排序----简单选择排序 a array i=8 13 1 27 2 32 3 49 4 57 5 68 6 76 7 88 例7.24 数组排序----简单选择排序 1 2 3 4 5 6 7 8 9 a 13 27 32 49 57 68 76 88 99 array i=8

字符串指针作函数参数 void copy_string(char from[],char to[]) { int i=0; b y u a r s t n d e to o . \0 I a e c h \0 r . t m 字符串指针作函数参数 a I m t e c h \0 r . from void copy_string(char from[],char to[]) { int i=0; while(from[i]!='\0') { to[i]=from[i]; i++; } to[i]='\0'; main() { char a[]="I am a teacher."; char b[]="You are a student."; printf("string_a=%s\n string_b=%s\n",a,b); copy_string(a,b); printf("\nstring_a=%s\nstring_b=%s\n",a,b); 例 用函数调用实现字符串复制 void copy_string(char *from,char *to) { for(;*from!='\0';from++,to++) *to=*from; *to='\0'; } int main() { char *a="I am a teacher."; char *b="You are a student."; printf("string_a=%s\nstring_b=%s\n",a,b); copy_string(a,b); printf("\nstring_a=%s\nstring_b=%s\n",a,b); return 0; (1)用字符数组作参数 (2)用字符指针变量作参数

编写一个函数求字符串的长度 /*版本1 */ int strlen(char *s) { int n=0; while(*s!=‘\0’) /*版本1 */ int strlen(char *s) { int n=0; while(*s!=‘\0’) { n++; s++; } return n; /*版本3*/ int strlen(char *s) { int n=0; while(*s++) n++; return n; } /*版本2*/ int strlen(char *s) { int n=0; while(*s++!=‘\0’) n++; return n; } /*版本4*/ int strlen(char *s) { char *p=s; while(*p++); return p-s; }

  /*L7-25.C: 编写一个函数实现任意两个字符串比较大小*/ int strcmp(char *sa,char *sb) { for(;*sa==*sb;sa++,sb++) if(*sa==‘\0’) return 0; return (*sa-*sb); } 如何调用上面定义的函数? char s1[20],s2[20]; gets(s1); gets(s2); if(strcmp(s1,s2)>0) printf(“s1>s2\n”); else if(strcmp(s1,s2)==0) printf(s1==s2\n”); else printf(s1<s2\n”); char *s1,*s2; gets(s1); gets(s2); if(strcmp(s1,s2)>0) printf(“s1>s2\n”); else if(strcmp(s1,s2)==0) printf(s1==s2\n”); else printf(s1<s2\n”);  

若int a[3][4]; int (*p1)[4]=a; int *p2=a[0]; 二维数组的指针作函数参数 用指向变量的指针变量 用指向一维数组的指针变量 用二维数组名 二维数组形参实际上是一维数组指针变量, 即 int x[ ][10]  int (*x)[10] 实参 形参 数组名int x[][4] 指针变量int (*q)[4] 指针变量int (*q)[4] 数组名a 指针变量p1 若int a[3][4]; int (*p1)[4]=a; int *p2=a[0]; 指针变量p2 指针变量int *q

二维数组名作函数参数 例7.26 求二维数组中最大元素值 j j j i 1 3 5 7 i 1 3 5 7 i 1 3 5 7 例7.26 求二维数组中最大元素值 1 3 5 7 2 4 6 8 15 17 34 12 i j max=1 1 3 5 7 2 4 6 8 15 17 34 12 i j max=3 1 3 5 7 2 4 6 8 15 17 34 12 i j max=5 j 1 3 5 7 2 4 6 8 15 17 34 12 i max=7 j 1 3 5 7 2 4 6 8 15 17 34 12 i max=7 j 1 3 5 7 2 4 6 8 15 17 34 12 i max=34

#include <stdio.h> int max_value(int array[3][4]) { int i,j,k,max; max=array[0][0]; for(i=0;i<3;i++) for(j=0;j<4;j++) if(array[i][j]>max) max=array[i][j]; return(max); } int main() { int a[3][4]={{1,3,5,7}, {2,4,6,8},{15,17,34,12}}; printf("max value is %d\n",max_value(a)); return 0; 多维形参数组第一维维数 可省略,第二维必须相同  int array[][4]

例 求二维数组中各行元素之和 get_sum_row(int x[][3], int result[] ,int row, int col) 例 求二维数组中各行元素之和 get_sum_row(int x[][3], int result[] ,int row, int col) { int i,j; for(i=0;i<row;i++) { result[i]=0; for(j=0;j<col;j++) result[i]+=x[i][j]; } int main() { int a[2][3]={3,6,9,1,4,7}; int sum_row[2],row=2,col=3,i; get_sum_row(a,sum_row,row,col); printf("The sum of row[%d]=%d\n",i+1,sum_row[i]); return 0; x result 3 1 4 6 7 9 a sum_row 18 12

void average(float *p,int n) { float *p_end, sum=0,aver; p_end=p+n-1; 函数说明 void average(float *p,int n) { float *p_end, sum=0,aver; p_end=p+n-1; for(;p<=p_end;p++) sum=sum+(*p); aver=sum/n; printf("average=%5.2f\n",aver); } void search(float (*p)[4], int n) { int i; printf(" No.%d :\n",n); for(i=0;i<4;i++) printf("%5.2f ",*(*(p+n)+i)); int main() { void average(float *p,int n); void search(float (*p)[4],int n); float score[3][4]= {{65,67,79,60},{80,87,90,81}, {90,99,100,98}}; average(*score,12); search(score,2); return 0; } 列指针 p 行指针 float p[][4] p 65 52 79 60 80 87 90 81 99 100 98  p[n][i]

例7.28 3个学生各学4门课,计算总平均分,并查找一门以上课 不及格学生, 输出其各门课成绩 例7.28 3个学生各学4门课,计算总平均分,并查找一门以上课 不及格学生, 输出其各门课成绩 void search(float (*p)[4], int n) { int i,j,flag; for(j=0;j<n;j++) { flag=0; for(i=0;i<4;i++) if(*(*(p+j)+i)<60) flag=1; if(flag==1) { printf("No.%d is fail,his scores are:\n",j+1); printf("%5.1f ",*(*(p+j)+i)); printf("\n"); } int main() { void search(float (*p)[4], int n); float score[3][4]={{...},{...},{...}}; search(score,3);return 0; 65 52 79 60 80 87 90 81 99 100 98 p  p[j][i]

例 二级指针做函数参数 1 2 2002 2000 COPY 2004 2006 2000 #include <stdio.h> 例 二级指针做函数参数 #include <stdio.h> void swap(int **r,int **s) { int *t; t=*r; *r=*s; *s=t; } int main() { int a=1,b=2,*p,*q; p=&a; q=&b; swap(&p,&q); printf("%d,%d\n",*p,*q); return 0; 2000 2008 200A 2002 2004 2006 1 2 变量a 变量b (main) 指针变量p 指针变量q 2002 COPY 2000 二级指针s 二级指针r (swap) 指针变量t 2006 2004 2000

例二级指针做函数参数 1 2 2002 2000 2,1 #include <stdio.h> 2008 200A 2002 2004 2006 1 2 变量a 变量b (main) 指针变量p 指针变量q #include <stdio.h> void swap(int **r,int **s) { int *t; t=*r; *r=*s; *s=t; } int main() { int a=1,b=2,*p,*q; p=&a; q=&b; swap(&p,&q); printf("%d,%d\n",*p,*q); return 0; 2002 2000 2,1

例二级指针做函数参数 2,1 a b p q #include <stdio.h> void swap(int **r,int **s) { int *t; t=*r; *r=*s; *s=t; } int main() { int a=1,b=2,*p,*q; p=&a; q=&b; swap(&p,&q); printf("%d,%d\n",*p,*q); return 0; a b r s p q a b r s p q b a p q 2,1

#include <stdio.h> #include <stdlib.h> struct node{ int data; struct node *next; }; int delete(struct node **phead,int n) { struct node *p,*q; p=*phead; while(p->data!=n&&p->next!=NULL) { q=p; p=p->next; } if(p->data==n) /*找到*/ { if(p==*phead) /*被删除的结点是链头*/ *phead=p->next; else /*被删除的结点不是链头*/ q->next=p->next; free(p); return (1); } else return (0); } 例7.29 编写函数,从一个已经创建好链表中删除指定结点,该链表由整数构成

long factorial(int n); #include <stdio.h> long sum(int a, int b); long factorial(int n); int main() { int n1,n2; long a; scanf("%d,%d",&n1,&n2); a=sum(n1,n2); printf("a=%ld",a); } long sum(int a,int b) { long c1,c2; c1=factorial(a); c2=factorial(b); return(c1+c2); long factorial(int n) { long rtn=1; int i; for(i=1;i<=n;i++) rtn*=i; return(rtn); 文件包含编译预处理命令 函数类型说明 long sum(int a, int b); long factorial(int n); 函数调用 实参 函数定义 形参 函数返回值

指向函数的指针变量 函数指针:函数在编译时被分配的入口地址,用函数名表示 指向函数的指针变量 定义形式: 数据类型 (*指针变量名)(); max …... 指令1 指令2 指向函数的指针变量 定义形式: 数据类型 (*指针变量名)(); 如 int (*p)(); 函数指针变量赋值:如p=max; ( )不能省 int (*p)() 与 int *p()不同 专门存放函数入口地址 可指向返回值类型相同的不同函数 函数返回值的数据类型 函数调用形式: c=max(a,b);  c=(*p)(a,b);  c=p (a,b); 对函数指针变量pn, p++, p--无意义 函数指针变量指向的函数必须有函数说明

例7.30 用函数指针变量调用函数,比较两个数大小 例7.30 用函数指针变量调用函数,比较两个数大小 int main() { int max(int ,int), (*p)(); int a,b,c; p=max; scanf("%d,%d",&a,&b); c=(*p)(a,b); printf("a=%d,b=%d,max=%d\n",a,b,c); return 0; } int max(int x,int y) { int z; if(x>y) z=x; else z=y; return(z); main() { int max(int ,int); int a,b,c; scanf("%d,%d",&a,&b); c=max(a,b); printf("a=%d,b=%d,max=%d\n",a,b,c); } int max(int x,int y) { int z; if(x>y) z=x; else z=y; return(z);

用函数指针变量作函数参数 例7.31 用函数指针变量作参数,求最大值、最小值和两数之和

例7.31 用函数指针变量作参数,求最大值、最小值和两数之和 int main() { int a,b,max(int,int), min(int,int),add(int,int); void process(int,int,int (*fun)()); scanf("%d,%d",&a,&b); process(a,b,max); process(a,b,min); process(a,b,add); return 0; } void process(int x,int y,int (*fun)()) { int result; result=(*fun)(x,y); printf("%d\n",result); int max(int x,int y) { printf(“max=”); return(x>y?x:y); int min(int x,int y) { printf(“min=”); return(x<y?x:y); int add(int x,int y) { printf(“sum=”); return(x+y);

C:\TC> copy[.exe] source.c temp.c 命令行参数 命令行:在操作系统状态下,为执行某个程序而键入的一行字符 命令行一般形式:命令名 参数1 参数2………参数n C:\TC> copy[.exe] source.c temp.c 带参数的main函数形式: 有3个字符串参数的命令行 main(int argc, char *argv[]) { ……… } 命令行参数传递 命令行实参 main(形参) 系统自动调用 main函数时传递 命令行中参数个数 形参名任意 元素指向命令行参数 中各字符串首地址 第一个参数: main所在的可执行文件名

例7.32 输出命令行参数 argc=3 1. 编译、链接test.c,生成可执行文件test.exe 例7.32 输出命令行参数 /*test.c*/ main(int argc, char *argv[]) { while(argc>1) { ++argv; printf("%s\n",*argv); --argc; } argv[0] argv[1] argv[2] char *argv[] world test hello argv argc=3 1. 编译、链接test.c,生成可执行文件test.exe 2. 在DOS状态下运行(test.exe所在路径下) 例如: C:\TC> test[.exe] hello world! hello world!

例7.32 输出命令行参数 int main(int argc, char *argv[]) { while(argc-->0) 例7.32 输出命令行参数 int main(int argc, char *argv[]) { while(argc-->0) printf("%s\n",*argv++); } argv[0] argv[1] argv[2] char *argv[] world test hello argv argc=3 test hello world!

例7.33,编程模拟实现UNIX中的grep命令的基本功能 grep -- print lines matching a pattern (将符合样式的该行列出) 语法: grep  PATTERN 自定义函数 int getline(char *s,int max); /*read a line into s, return length*/ /*max--- maxium input line size*/  

#include <stdio.h> #include <string.h> #define MAXLINE 1000 int getline(char *s,int max); /*print lines that match pattern from 1st arg*/ int main(int argc,char*argv[]) { char line[MAXLINE]; int found=0; if(argc!=2) printf(“Usage:find pattern\n”); else while(getline(line,MAXLINE)>0) if(strstr(line,argv[1])!=NULL) { printf(“%s”,line); found++; } return found;

/*read a line into s, return length*/ int getline(char *s,int max) { int c,i; for(i=0;i<max-1&&(c=getchar())!=EOF&& c!=‘\n’;++i) s[i]=c; if(c==‘\n’) { s[i]=c ++I; } return i;

例7.34,改进为带参数的命令 find -x –n pattern 或者为 find –xn pattern X: 表示形式与pattern不匹配的行 N: 要求形式行号

#include <stdio.h> #include <string.h> #define MAXLINE 1000 int getline(char *s,int max); /*print lines that match pattern from 1st arg*/ int main(int argc,char*argv[]) { char line[MAXLINE]; long lineno=0;/*行号*/ int c, except=0,number=0;/*分别记录x和n的状态*/ int found=0; while(--argc>0&&(*++argv)[0]==‘-’) while(c==*++argv[0]) switch(c) { case ‘x’:except=1;break; case ‘n’:number=1;break; default: printf(“find:illegal option %c\n”,c); argc=0; found=-1; break; }

if(argc!=1) printf(“Usage:find –x –n pattern\n”); else while(getline(line,MAXLINE)>0) { lineno++; if((strstr(line,*argv)!=NULL)!=except) { if(number) printf(“%ld:”,lineno); printf(“%s”,line); found++; } return found;

THE END