第八章 使用指针.

Slides:



Advertisements
Similar presentations
C语言程序设计教程 单位:德州学院计算机系.
Advertisements

第一章 C语言概述 计算机公共教学部.
第8章 指针 ● 8.1 指针简介 ● 8.2 指针变量的操作 ● 8.3 数组与指针 ● 8.4 二维数组与指针 ●本章小结 ●本章练习.
C语言基础——指针的高级应用 Week 05.
高级语言程序设计 C++程序设计教程(下) 2006年春季学期 与一些教材的区别 偏重理论,不去讨论某个系统的具体使用方法,但会涉及实现技术
第九章 指针 目录 指针与指针变量的概念 变量的指针和指向变量的指针变量 数组的指针和指向数组的指针变量
C语言程序设计 第八章 函数.
C语言程序设计 第十二章 位运算.
第5章 函数与模块化设计 学习目的与要求: 掌握函数的定义及调用方法 理解并掌握参数的传递方法 理解函数的嵌套与递归调用
程序设计基础.
第六章 数 组 主讲教师 贾月乐 联系电话:
C语言程序设计 课程 第5章 数组 主讲:李祥 博士、副教授 单位:软件学院软件工程系.
第5章 函数与预处理 《 C语言程序设计》 (Visual C++ 6.0环境) 本章导读
第一章 C语言概述.
由C程序结构所知,一个完整的C语言程序是由一个且只能有一个main()函数(又称主函数)和若干个其他函数组合而成的。而前面各章仅学习main()函数的编程,本章将介绍其他函数的编程,包括其他函数的定义、调用、参数传递及变量的作用域等。
選擇排序法 通訊一甲 B 楊穎穆.
补充内容 结构体 概述 定义结构体类型和定义结构体变量 结构体变量的引用 结构体变量的初始化 指针与结构体 用typedef定义类型的别名.
C 程式設計— 指標.
目录 10.1 指针的基本概念 10.2 指向变量的指针变量 10.3 指向数组的指针变量 10.4 指向函数的指针变量和指针型函数
项目六 用指针优化学生成绩排名 项目要求 项目分析
程序讲解 第一题: 将指定文件的m行到n行字符写到显示屏上,m和n值从键盘输入。 运行时输入及结果: please enter m,n:
Introduction to the C Programming Language
Introduction to the C Programming Language
目录 第八章 数组 1 简单学生成绩管理系统的开发 2 一维数组 3 多维数组 4 字符数组 5 数组作函数参数.
第七章 函数 目录 有参的加法函数的开发 函数定义的一般形式 函数参数和函数的值 函数的调用
计算概论 第十八讲 C语言高级编程 结构与习题课 北京大学信息学院.
Object-Oriented Programming in C++ 第一章 C++的初步知识
Chap 8 指针 8.1 寻找保险箱密码 8.2 角色互换 8.3 冒泡排序 8.4 电码加密 8.5 任意个整数求和*
C语言程序设计 李祥.
第八章 函数.
第8章 善于利用指针 8.1 指针是什么 8.2 指针变量 8.3 通过指针引用数组 8.4 通过指针引用字符串 8.5 指向函数的指针
第五章 指针 5.1 指针的概念和定义 5.2 指针运算 5.3 指针和数组 5.4 字符串指针 5.5 指针数组 5.6 指向指针的指针
第6章 函数 学习的意义.
谭浩强 编著 中国高等院校计算机基础教育课程体系规划教材 C++程序设计.
第十章 指针.
第七章 函数及变量存贮类型 7.1 函数基础与C程序结构 7.2 函数的定义和声明 7.3 函数的调用 7.4 函数的嵌套与递归
数组 梁春燕 华电信息管理教研室.
第1章 概述 本章要点: C语言程序结构和特点 C语言程序的基本符号与关键字 C语言程序的编辑及运行 学习方法建议:
六、函数 教学目标: 函数的概念、定义、调用和返回 带自定义函数的程序设计 递推算法 递归思想及算法实现 函数的参数传递方式 C语言程序设计.
第8章 函数 函数参数和函数的值 概述 函数定义的一般形式 函数的调用(嵌套调用、递归调用) 数组作为函数参数
C语言概述 第一章.
C语言大学实用教程 第5章 函数与程序结构 西南财经大学经济信息工程学院 刘家芬
資料結構與C++程式設計進階 排序與搜尋 講師:林業峻 CSIE, NTU 6/ 14, 2010.
C语言复习3----指针.
for 迴圈 while迴圈 do-while迴圈 break 與 continue goto 與 標籤 程式觀摩
第一章 程序设计和C语言 主讲人:高晓娟 计算机学院.
C语言程序示例: 1.输入10个数,按从小到大的顺序排序。 2.汉诺塔问题。.
C程序设计.
C语言大学实用教程 第6章 数组 西南财经大学经济信息工程学院 刘家芬
函数 概述 模块化程序设计 基本思想:将一个大的程序按功能分割成一些小模块, 特点: 开发方法: 自上向下,逐步分解,分而治之
第八章 指標 (Pointer).
第六章 指针 C++程序设计中使用指针可以: 使程序简洁、紧凑、高效 有效地表示复杂的数据结构 动态分配内存 得到多于一个的函数返回值.
Chap 5 函数 5.1 计算圆柱体积 5.2 使用函数编写程序 5.3 变量与函数.
7.1 C程序的结构 7.2 作用域和作用域规则 7.3 存储属性和生存期 7.4 变量的初始化
第十四章 若干深入问题和C独有的特性 作业: 函数指针 函数作参数 函数副作用 运算 语句 位段 存储类别 编译预处理
第十章 指针 指针是C语言的重要概念,是C语言的特色,是C语言的精华。 10.1 地址和指针的概念 内存中的每一个字节都有一个地址。
C程序设计.
第一章 C语言概述 教师:周芸.
第九章 指针.
第二章 类型、对象、运算符和表达式.
累堆排序法 (Heap Sort).
C程序设计.
第四章 函数 丘志杰 电子科技大学 计算机学院 软件学院.
第七章  数 组.
第九章 指针 C程序设计中使用指针可以: 使程序简洁、紧凑、高效 有效地表示复杂的数据结构 动态分配内存 得到多于一个的函数返回值.
C/C++基礎程式設計班 陣列 講師:林業峻 CSIE, NTU 3/14, 2015.
第9章 C++程序设计初步 9.1 C++的特点 9.2 最简单的C++程序 9.3 C++的输入输出 9.4 函数的重载
第六章 复合数据类型 指针的声明与使用 数组的声明与使用 指针与数组的相互引用 字符串及相关库函数 new与delete
C程序设计 复习 1、计算机系统的组成 外部设备:输入、输出设备(同人打交道《十进制》)
C语言基础学习 从外行到入门.
Presentation transcript:

第八章 使用指针

主要内容 8.1 地址和指针的概念 8.2 变量的指针和指向变量的指针变量 8.3 通过指针引用数组 8.4 通过指针字符串 8.5 指向函数的指针 8.6 返回指针值的函数 8.7 指针数组和多重指针 8.8 动态内存分配与指向它的指针变量

8.1地址和指针的概念 内存区的每一个字节有一个编号,这就是“地址” 。如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。 1、按变量地址存取变量值的方式称为“直接访问”方式 printf(″%d″,i); scanf(″%d″,&i); k=i+j;

2. 另一种存取变量值的方式称为“间接访问”的方式。即,将变量i的地址存放在另一个变量中。 在C语言中,指针是一种特殊的变量,它是存放地址的。

指针和指针变量的定义: 一个变量的地址称为该变量的“指针”。 例如,地址2000是变量i的指针。如果有一个变量专门用来存放另一变量的地址(即指针),则它称为“指针变量”。上述的i_pointer就是一个指针变量。

8.2 变量的指针和指向变量的指针变量 8.2.1 怎样定义指针变量 定义指针变量的一般形式为 基类型 *指针变量名;

下面都是合法的定义: float *pointer_3; char *pointer_4; 可以用赋值语句使一个指针变量得到另一个变 量的地址,从而使它指向一个该变量。 例如: pointer_1=&i; pointer_2=&j;

在定义指针变量时要注意两点: 指针变量前面的“*”,表示该变量的类型为指针型变量。 例: float *pointer_1; 指针变量名是pointer_1 ,而不是* pointer_1 。 (2) 在定义指针变量时必须指定基类型。 需要特别注意的是,只有整型变量的地址才能放到指向整型变量的指针变量中。下面的赋值是错误的∶ float a; int * pointer_1; pointer_1=&a;

⑴ 指针变量中只能存放地址(指针),不要将一个 整数赋给一个指针变量。 在对指针变量赋值时需要注意两点: ⑴ 指针变量中只能存放地址(指针),不要将一个 整数赋给一个指针变量。 例: * pointer_1=100; /* pointer_1是指针变量,100是整数,不合法 */ (2) 赋给指针变量的变是量地址不能是任意的类型,而只能是与指针变量的基类型具有相同类型的变量的 地址。

8.2.2 怎样引用指针变量 在引用指针变量时,可能有三种情况: ⑴给指针变量赋值。如: p=&a; ⑵引用指针变量的值。如: printf(“%o”,p); ⑶引用指针变量指向的变量。 有关的两个运算符: & 取地址运算符。 &a是变量a的地址。 * 指针运算符 (或称“间接访问”运算符),*p是指针变量p指向的对象的值。

printf(″%d,%d\n″,*pointer_1, *pointer_2); 例8.1 通过指针变量访问整型变量 #include <stdio.h> void main ( ) { int a,b; int*pointer_1, *pointer_2; a=100;b=10; pointer_1=&a; /*把变量a的地址赋给 pointer_1 */ pointer_2=&b; /*把变量b的地址赋给 pointer_2 */ printf(″%d,%d\n″,a,b); printf(″%d,%d\n″,*pointer_1, *pointer_2); }

例8 . 2 输入a和b两个整数,按先大后小的顺序输出 a和b。 #include <stdio.h> void 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\n″,a,b); printf(″max=%d,min=%d\n″,*p1,*p2); }

运行情况如下: 5,9↙ a=5,b=9 max=9,min=5 当输入a=5,b=9时,由于a<b,将p1和p2交换。交换前的情况见图(a),交换后见图(b)。

8.2.3 指针变量作为函数参数 例8 . 3 对输入的两个整数按大小顺序输出 #include <stdio.h> 例8 . 3 对输入的两个整数按大小顺序输出 #include <stdio.h> void main() {void swap(int *p1,int *p2);  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);   }

void swap(int *p1,int *p2) { int temp; temp=*p1; *p1=*p2; *p2=temp; }

#include <stdio.h> void main() 例8.4输入3个整数a,b,c,要求按大小顺序将它们输出。 用函数实现改变这3个变量的值。 #include <stdio.h> void main() { void exchange(int *q1, int *q2, int *q3);   int a,b,c,*p1,*p2,*p3;   scanf(″%d,%d,%d″,&a, &b, &c);  p1=&a;p2=&b;p3=&c;   exchange (p1,p2,p3);   printf(″\n%d,%d,%d\n″,a,b,c); }

void exchange(int *q1, int *q2, int *q3) { void swap(int *pt1, int *pt2); if(*q1<*q2) swap(q1,q2);   if(*q1<*q3) swap(q1,q3);   if(*q2<*q3= swap(q2,q3); } void swap(int *pt1, int *pt2) {int temp; temp=*pt1; *pt1=*pt2; *pt2=temp;

8.3 通过指针引用数组 8.3.1 数组元素的指针 一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中)。所谓数组元素的指针就是数组元素的地址。

例如:int a[10]; (定义a为包含10个整型数据的数组) int *p; (定义p为指向整型变量的指针变量) 可以用一个指针变量指向一个数组元素。 例如:int a[10]; (定义a为包含10个整型数据的数组) int *p; (定义p为指向整型变量的指针变量) p=&a[0]; (把a[0]元素的地址赋给指针变量p) 也就是使p指向a数组的第0号元素 。

两个指针相减,如p1-p2 (只有p1和p2都指向同一数组中的元素时才有意义)。 8.3.2 指针的运算 C语言规定在指针指向数组元素时,可以对指针进行以下运算: 加一个整数(用+或+=),如p+1 减一个整数(用-或-=),如p-1 自加运算,如p++,++p 自减运算,如p--,--p 两个指针相减,如p1-p2 (只有p1和p2都指向同一数组中的元素时才有意义)。

如果指针变量p已指向数组中的一个元素,则p+1 指向同一数组中的下一个元素,p-1指向同一数组中 的上一个元素。 分别说明如下: 如果指针变量p已指向数组中的一个元素,则p+1 指向同一数组中的下一个元素,p-1指向同一数组中 的上一个元素。 (2) 如果p原来指向a[0],执行++p后p的值改变了,在p的原值基础上加d,这样p就指向数组的下一个元素a[1]。 (3) 如果p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址,或者说,它们指向a数组的第 i个元素 。 *(p+i)或*(a+i)是p+i或a+i所指向的数组元素,即a[i]。 (5) 如果指针变量p1和p2都指向同一数组,如执行p2-p1,结果是两个地址之差除以数组元素的长度。

8.3.3 通过指针引用数组元素 引用一个数组元素,可以用: (1) 下标法,如a[i]形式;   (1) 下标法,如a[i]形式;   (2) 指针法,如*(a+i)或*(p+i)。其中a是数组名,p是指向数组元素的指针变量,其初值p=a。 例8.5 输出数组中的全部元素 假设有一个a数组,整型,有10个元素。要输出各元素的值有三种方法:

(1)下标法 #include <stdio.h> void main() { int a[10];   int i;  for(i=0;i<10;i++)   scanf(″%d″,&a[i]);   printf(″\n″);   printf(″%d″,a[i]); }

(2) 通过数组名计算数组元素地址,找出元素的值。 #include <stdio.h> void main() { int a[10];   int i;  for(i=0;i<10;i++ )   scanf(″%d″,&a[i]);   printf(″\n″);   for(i=0;i<10;i++)    printf(″%d″,*(a+i));   }

(3) 用指针变量指向数组元素。 #include <stdio.h> void main() { int a[10];   int *p,i;   for(i=0;i<10;i++)     scanf(″%d″,&a[i]);   printf(″\n″);   for(p=a;p<(a+10);p++)    printf(″%d ″,*p); }

例8.6 通过指针变量输出a数组的10个元素。 #include <stdio.h> void main() { int*p,i,a[10];  p=a;  for(i=0;i<10;i++ )  scanf(″%d″,p++); printf(″\n″); for(i=0;i<10;i++,p++ )   printf(″%d″,*p); } 程序运行情况: 1 2 3 4 5 6 7 8 9 0↙ 22153 234 0 0 30036 25202 11631 8259 8237 28483 显然输出的数值并不是a数组中各元素的值

#include <stdio.h> void  main() { int*p,i,a[10];  p=a;  for(i=0;i<10;i++)  scanf(″%d″,p++); printg(″\n″); p=a;   for(i=0;i<10;i++,p++ )   printf(″%d″,*p); } 程序运行情况: 1 2 3 4 5 6 7 8 9 0↙ 1 2 3 4 5 6 7 8 9 0

8.3.4 用数组名作函数参数 在第7章中介绍过可以用数组名作函数的参数。 如: void main() 8.3.4 用数组名作函数参数 在第7章中介绍过可以用数组名作函数的参数。 如: void main() {if(int arr[],int n); int array[10]; ┇ f(array,10); } void f(int arr[ ],int n) { }

例8.7 将数组a中n个整数按相反顺序存放

#include <stdio.h> void main() { void inv(int x[ ],int n); int i,a[10]={3,7,9,11,0, 6,7,5,4,2};  printf(″The original array:\n″);  for(i=0;i<10;i++)  printf (″%d,″,a[i]);  printf(″\n″);   inv (a,10);  printf(″The array has been in verted:\n″);   for(i=0;i<10;i++)   printf (″%d,″,a[i]);   printf (″\n″); }

void inv(int x[ ],int n) /*形参x是数组名*/ { int temp,i,j,m=(n-1)/2;    for(i=0;i<=m;i++)     { j=n-1-i;      temp=x[i]; x[i]=x[j]; x[j]=temp; }    return; 运行情况如下: The original array: 3,7,9,11,0,6,7,5,4,2 The array has been inverted: 2,4,5,7,6,0,11,9,7,3

对刚才的程序可以作一些改动。将函数inv中的形参x改成指针变量。 #include <stdio.h> void main() {void inv(int *x,int n); int i,a[10]={3,7,9,11,0, 6,7,5,4,2};   printf( ″The original array:\n″ );    for(i=0;i<10;i++)   printf (″%d,″,a[i]);   printf (″\n″);    inv(a,10);   printf ( ″The array has been in verted:\n″ );  printf (″%d,″,a[i]);  printf (″\n″);}

void inv(int *x,int n) /*形参x为指针变量*/ {intp,temp,*i,*j,m=(n-1)/2; i=x;j=x+n-1;p=x+m; for(;i<=p;i++,j--) {temp=*i;*i=*j;*j=temp;}  return; }

如果有一个实参数组,想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况: (1) 形参和实参都用数组名,如: void main() void f(int x[ ],int n) { int a[10]; { … … f(a,10); } }

(2) 实参用数组名,形参用指针变量。如: void main() void f(int *x,int n) {int a[10]; { … … f(a,10); } } (3)实参形参都用指针变量。例如: void main() void f(int *x,int n) {int a[10], *p=a; { ┇ ┇ f(p,10); } }

(4) 实参为指针变量,形参为数组名。如: void main() void f(int x[ ],int n) {int a[10],*p=a; { ┇ ┇ f(p,10); } }

例8.8 用实参指针变量改写例8.7。 #include <stdio.h> void main() { void inv(int *x,int n); int i,arr[10],*p=arr; printf(″The original array:\n ″); for(i=0;i<10;i++,p++)  scanf(″%d″,p);  printf(″\n″); p=arr; inv(p,10); /* 实参为指针变量 */ printf(″The array has been inverted :\n″); for(p=arr;p<arr+10;p++ ) printf(″%d″,*p); printf(″\n″);   }

void inv(int *x,int n) {intp,m,temp,*i,*j;  m=(n-1)/2; i=x;j=x+n-1;p=x+m;  for(;i<=p;i++,j--) {temp=*i;*i=*j;*j=temp;}  return; }

例8.9 用选择法对10个整数按由大到小顺序排序 #include <stdio.h> void main() { void sort(int x[ ],int n); 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++) {printf(″%d″,*p);p++;} }

void sort(int x[ ],int n) 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;}   } }

8.3.5 通过指针引用多维数组 用指针变量可以指向一维数组中的元素,也可以指向多维数组中的元素。但在概念上和使用上,多维数组的指针比一维数组的指针要复杂一些。 1. 多维数组元素的地址 先回顾一下多维数组的性质,可以认为二维数组是“数组的数组”,例 : 定义int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}}; 则二维数组a是由3个一维数组所组成的。设二维数组的首行的首地址为2000 ,则

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

例8.10 用指向元素的指针变量输出二维数组元素的值 2 . 指向多维数组元素的指针变量 (1) 指向数组元素的指针变量 例8.10 用指向元素的指针变量输出二维数组元素的值 #include <stdio.h> void main() { 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); } }   运行结果如下: 1 3 5 7 9 11 13 15 19 21 23

(2) 用指向由m个元素组成的一维数组的指针变量 例8.11 输出二维数组任一行任一列元素的值 #include <stdio.h> void main ( ) { int a[3][4]={1,3,5,7,9,11, 13,15,17,19,21,23};  int (*p)[4],i,j; p=a;  scanf(″ i=%d,j=%d″,&i,&j); printf(″a[%d,%d]=%d\n″,i, j,*(*(p+i)+j)); } 运行情况如下: i=1,j=2↙(本行为键盘输入) a[1,2]=13

3. 用指向数组的指针作函数参数 例8.12 有一个班,3个学生,各学4门课,计算总平均分数以及第n个学生的成绩。这个题目是很简单的。只是为了说明用指向数组的指针作函数参数而举的例子。用函数average求总平均成绩,用函数search找出并输出第i个学生的成绩。

#include <sydio.h> void main() { void average(float *p,int n); void search(float (*p)[4],int n); float score[3][4]={{65,67,70,60},{80, 87,90,81},{90,99,100,98}}; average(*score,12);/*求12个分数的平均分*/ search(score,2);/*求序号为2的学生的成绩*/ }

void average(float *p,int n) float 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) / * p是指向具有4个元素的一维数组的指针 */{int i; printf(″the score of No. %d are:\n″,n); for(i=0;i<4;i++) printf(″%5.2f″,*(*(p+n)+i)); } 程序运行结果如下: average=82.25 The score of No.2 are: 90.00 99.00 100.00 98.00

例8.13 在上题基础上,查找有一门以上课程不及格的学生,打印出他们的全部课程的成绩。 #include <stdio.h> void main() {void search(float (*p)[4],int n);/*函数声明*/ float score[3][4]={{65,57,70,60},{58,87, 90,81},{90,99,100,98}}; search(score,3); }

void search(float (*p)[4],int n) 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 fails,his scores are:\n",j+1);    for(i=0;i<4;i++) printf(″%5.1f″,*(*(p+j)+i));   printf(″\n″);    } } }   程序运行结果如下: No.1 fails, his scores are: 65.0 57.0 70.0 60.0 No.2 fails, his scores are: 58.0 87.0 90.0 81.0

(1) 用字符数组存放一个字符串,然后输出该字符串。 8.4 通过指针引用字符串 8.4.1字符串的表示形式 (1) 用字符数组存放一个字符串,然后输出该字符串。 例 8.14 定义一个字符数组,对它初始化, 然后输出该字符串 #include <stdio.h> void main() {char string[]=″I love China!″; printf(″%s\n″,string); }

(2) 用字符指针指向一个字符串 可以不定义字符数组,而定义一个字符指针。用字符指针指向字符串中的字符。 例8.15 定义字符指针,使它指向一个字符串。 #include <stdio.h> void main() {charstring=″ I love China!″;  printf(″%s\n″,string); }

例8.16 将字符串a复制为字符串b #include <stdio.h> void main() {char a[ ]=″I am a boy.″,b[20]; int i; for(i=0;*(a+i)!=′\0′;i++)   *(b+i)=*(a+i);    *(b+i)=′\0′; printf(″string a is :%s\n″,a); printf(″string b is:″); for(i=0;b[i]!=′\0′;i++)   printf(″%c″,b[i]); printf(″\n″); }

 也可以设指针变量,用它的值的改变来指向字符串中的不同的字符。 例8.17 用指针变量来处理例8.16问题。 #include <stdio.h> void main() {char a[ ] =″I am a boy. ″,b[20],*p1,*p2; int i;   p1=a;p2=b; for(;*p1!=′\0′;p1++,p2++)

*p2=*p1; *p2=′\0′; printf(″string a is:%s\n″,a); printf(″string b is:″); for(i=0;b[i]!=′\0′;i++) printf(″%c″,b[i]); printf(″\n″); } 程序必须保证使p1和p2同步移动

8.4.2 字符指针作函数参数 (1) 用字符数组作参数 例8.18 用函数调用实现字符串的复制 (1) 用字符数组作参数 例8.18 用函数调用实现字符串的复制 #include <stdio.h> void main() {void copy_string(char *from, char *to); char *a="I am a teacher."; char b[]="You are a student."; char *p=b; printf("string a=%s\nstring b=%s\n",a,p); printf("copy string a to string b:\n "); copy_string(a,p); 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'; } 程序运行结果如下: string a=I am a teacher. string b =You are a student. copy string a to string b: string a =I am a teacher. stringb=I am a teacher.

对copy_string函数还可以改写得更精练一 些,请分析以下几种情况: 1、将copy_string函数改写为 void copy_string (char *from,char *to) {while((*to=*from)!=′\0′) {to++;from++;} } 2、 copy_string函数的函数体还可改为 { while((*to++=*from++)!=′\0′); }

3、copy_string函数的函数体还可写成 { while(*from!=′\0′)   *to++=*from++;   *to=′\0′; } 4、由于字符可以用其ASCII码来代替所以函数体可简化为: {while(*from) *to++=*from++; *to=′\0′; }

5、上面的while语句还可以进一步简化为下面的while语句: while(*to++=*from++); 它与下面语句等价: while((*to++=*from++)!=′\0′); 将*from赋给*to,如果赋值后的*to值等于′\0′则循环终止(′\0′已赋给*to) 6、函数体中while语句也可以改用for语句: for(;(*to++=*from++)!=0;); 或 for(;*to++=*from++;);

7、也可以用字符数组名作函数形参,在函数中另定义两个指针变量p1,p2。函数copy_string可写为 void copy_string (char from[ ],char to[ ]) {char*p1,*p2; p1=from;p2=to;  while((*p2++=*p1++)!=′\0′); } 8.4.3 对使用字符指针变量和字符数组的讨论 字符数组和字符指针变量二者之间的区别主要有以下几点:

(1) 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),决不是将字符串放到字符指针变量中。 (2)赋值方式。对字符数组只能对各个元素赋值,不 能用以下办法对字符数组赋值。 char str[14]; str=″I love China!″; 而对字符指针变量,可以采用下面方法赋值: char*a; a=″I love China!″;

(3)对字符指针变量赋初值: char *a=″I love China!″;等价于 char*a; a=″I love Chian!″; 而对数组的初始化: char str[14]={″I love China!″}; 不能等价于 char str[14]; str[ ]=″I love China!″;

(4) 定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个字符变量的地址。 例如: char str[10];   scanf(″%s″,str);

(5) 指针变量的值是可以改变的,例如: 例8.19 改变指针变量的值 #include <stdio.h> void main() {char*a=″I love China!″; a=a+7; printf(″%s″,a); }

(6) 若字符指针变量p指向字符串,就可以用指针变 量带下标的形式引用所指的字符串中的字符。如有: char *a=″I love China!″; (7) 字符数组中各元素的值是可以改变的(可以对它们再赋值),但字符指针变量指向的字符串中的内容是不可以被取代的(不能对它们再赋值)。如: char a[]=”House”; char *b=” House”; a[2]=’r’; /* 合法,r取代u */ b[2]=’r’; /* 非法,字符串常量不能改变 */

8.5 指向函数的指针 8.5.1 什么是函数指针 如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配一段存储空间,这段存储空间的起始地址(又称入口地址)称为这个函数的指针。 可以定义一个指向函数的指针变量,用来存放某一函数的起始地址,这就意味着此指针变量指向该函数。

8.5.2 用函数指针变量调用函数 如果想调用一个函数,除了可以通过函数名调用以外,还可以通过指向函数的指针变量来调用该函数。 例8.20 用函数求a和b中的大者。 (1)通过函数名调用函数。

#include <stdio.h> void main() { int max(int,int);   int a,b,c; printf("please enter a and b:");  scanf(″%d,%d″,&a,&b); c=max(a,b);  printf(″a=%d,b=%d,max=%d ″,a,b,c); } int max(int x,int y) { int z; if(x>y)z=x;   else z=y; return(z); 运行情况如下: please enter a and b:3,8↙ a=3,b=8,max=8

(2)通过指针变量来访问它指向的函数。 将 程序改写为 #include <stdio.h> void main() {int max(int,int); int (*p)(int,int); int a,b,c; p=max; printf("please enter a anb b:"); scanf("%d,%d",&a,&b); c=(*p)(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);}

例8.21 输入两个整数,然后让用户选择1或2,选1时调用max函数,输出二者中的大数,选1时调用min函数,输出二者中的小数。 #include <stdio.h> void main() {int max(int,int); int min(int ,int); int (*p)(int,int); int a,b,c,n; printf("please enter a and b:"); scanf("%d,%d",&a,&b); printf("please choose 1 or 2:"); scanf("%d",&n); if (n==1) p=max;

else if (n==2) p=min; c=(*p)(a,b); printf("a=%d,b=%d,",a,b); if (n==1) printf("max=%d\n",c); else printf("min=%d\n",c); } int max(int x,int y) {int z; if(x>y) z=x; else z=y; return(z); } int min(int x,int y) if(x<y) z=x; return(z);} 运行情况如下: please enter a and b: 4,9↙ please choose 1 or 2:1↙ a=3,b=8,max=8 再运行一次: please choose 1 or 2:2↙ a=3,b=8,min=3

8.5.3 用指向函数的指针作函数参数 函数指针变量常用的用途之一是把指针作为参数传递到其他函数。指向函数的指针也可以作为参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。

实参函数名 f1    f2      ↓ ↓ void fun(int (*x1)(int),int (*x2)(int,int)) { int a,b,i,j; a=(*x1)(i); /*调用f1函数*/ b=(*x2)(i,j);/*调用f2函数*/ }

例8.22 有两个整数a和b,由用户输入1,2或3。如输入1,程序就给出a和b中大者,输入2,就给出a和b中小者,输入3,则求a与b之和。 #include <stdio.h> void main() {int max(int,int); /* 函数声明 */ int min(int,int); /* 函数声明 */ int add(int,int); /* 函数声明 */ int fun(int x,int y, int (*p)(int,int)); /* 函数声明 */ int a=34,b=-21,n; printf("please choose 1,2 or 3:"); scanf("%d",&n); if (n==1) fun(a,b,max); else if (n==2) fun(a,b,min); else if (n==3) fun(a,b,add); } 

int min(int x,int y) {int z; if(x<y)z=x; else z=y; printf("min=%d"); return(z);} int add(int x,int y) z=x+y; printf("sum=%d"); int fun(int x,int y,int (*p)(int,int)) {int resout; resout=(*p)(x,y); printf("%d\n",resout); } 运行情况如下: ①please choose 1,2 or 3: 1↙ max=34 ②please choose 1,2 or 3: 2↙ min=21 ③please choose 1,2 or 3: 3↙ sum=13

8.6 返回指针值的函数 一个函数可以带回一个整型值、字符值、实型值等,也可以带回指针型的数据,即地址。其概念与以前类似,只是带回的值的类型是指针类型而已。 这种带回指针值的函数,一般定义形式为 类型名 *函数名(参数表列); 例如: int *a(int x,int y);

例8.23 有若干个学生的成绩(每个学生有4门课程),要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数来实现。 #include <stdio.h> void main() {float *score[ ][4]={{60,70,80,90}, {56,89,67,88},{34,78,90,66}}; float*search(float (*pointer)[4],int n); float*p; int i,m; printf(″enter the number of student:″); scanf(″%d″,&m); printf(″The scores of No.%d are:\n″,m);

p=search(score,m);  for(i=0;i<4;i++=  printf(″%5.2f\t″,*(p+i)); } float * search(float (*pointer)[4],int n) { float *pt; pt=*(pointer+n); return(pt); 运行情况如下: enter the number of student:1↙ The scores of No. 1 are: 56.00 89.00 67.00 88.00

8.7 指针数组和多重指针 8.7.1 指针数组的概念 一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都相当于一个指针变量。 一维指针数组的定义形式为: 类型名数组名[数组长度]; 例如: int*p[4];

例8.24 将若干字符串按字母顺序(由小到大)输出。 例8.24 将若干字符串按字母顺序(由小到大)输出。 #include <stdio.h> #include <string.h> void main() {void sort(char *name[ ],int n); void printf(char *name[ ],int n);   char *name[ ]={"Follow me","BASIC","Great Wall″,"FORTRAN","Computer design"};   int n=5;  sort(name,n); print(name,n); }

void sort(char *name[ ],int n) for(i=0;i<n-1;i++=  {k=i;   for(j=i+1;j<n;j++=  if(strcmp(name[k],name[j])>0)k=j;   if(k!=i)  temp=name[i]; name[i]=name[k]; name[k]=temp;}   } }

void print(char *name[ ],int n) {int i;  for(i=0;i<n;i++)    printf(″%s\n″,name[i]); } 运行结果为: BASIC Computer design FORTRAN Follow me Great Wall

8.7.2 指向指针数据的指针 定义一个指向指针数据的指针变量: char**p; 8.7.2 指向指针数据的指针 定义一个指向指针数据的指针变量: char**p; p的前面有两个*号。*运算符的结合性是从右到左,因此**p相当于*(*p),显然*p是指针变量的定义形式。如果没有最前面的*,那就是定义了一个指向字符数据的指针变量。现在它前面又有一个*号,表示指针变量p是指向一个字符指针变量的。*p就是p所指向的另一个指针变量。

例8.25 使用指向指针数据的指针变量 #include <stdio.h> void main() {char *name[]={"Follow me","BASIC","Great Wall″,"FORTRAN","Computer design"}; char **p; int i; for(i=0;i<5;i++)  {p=name+i;    printf(″%s\n″,*p); }

例8.26 一个指针数组的元素指向整型数据的简单例子 例8.26 一个指针数组的元素指向整型数据的简单例子 #include <stdio.h> void main() {int a[5]={1,3,5,7,9}; int *num[5]={&a[0],&a[1], &a[2],&a[3],&a[4]}; int **p,i; p=num;  for(i=0;i<5;i++=  { printf(″%d ″,**p); p++; }

8.8 动态内存分配与指向它的指针变量 8.8.1 什么是内存的动态分配 8.8 动态内存分配与指向它的指针变量 8.8.1 什么是内存的动态分配 前面介绍过全局变量和局部变量,全局变量是分配在内存中的静态存储区的,非静态的局部变量(包括形参)是分配在内存中的动态存储区的,这个存储区是一个称为栈(stack)的区域。除此以外,C语言还允许建立内存动态分配区域,以存放一些临时用的数据,这些数据是临时存放在一个的特别的自由存储区,称为堆(heap)区。

8.8.2 怎样建立内存的动态分配 对内存的动态分配是通过系统提供的库函数来实现的,主要有以下4个函数: (1) malloc函数   对内存的动态分配是通过系统提供的库函数来实现的,主要有以下4个函数: (1) malloc函数 函数原型为 void *malloc(unsigned int size); 作用是在内存的动态存储区中分配一个长度为size的连续空间。 (2)calloc函数 void *calloc(unsigned n,unsigned size); 作用是在内存的动态存储区中分配n个长度为size的连续空间。

8.8.2 怎样建立内存的动态分配 (3)free函数 函数原型为 void free(void *p); 作用是释放由指针变量p指向的动态空间,使这部分空间能被其他变量使用。 (4) realloc函数 void *realloc(void *p,unsigned int size); 如果已经通过malloc函数或calloc函数获得了临时分配域,想改变函数其大小,可以用recalloc函数重分配。

8.8.3 void指针类型 void指针类型,允许定义一个基类型为void的指针变量,它不指向任何具体的数据。请注意:不要把“指向void类型”理解为能指向“任何的类型”的数据,而应理解为“指向空类型”或“不指向确定的类型”的数据。在将它的值赋给另一指针变量时要进行强制类型转换使之适合于被赋值的变量的类型。例如: int a=3; /* 定义a为整型变量 */ int *p1=&a; /* 基类型为int型 */ void *p2; /* 基类型为void型 */ p2=(void )p1; /* 转换为void*类型然后赋值 */ printf(“%d”,*p1); /* 合法,输出a的值 */ printf(“%d”,*p2); /* 错误,p2是无指向的,不能指 向a */

例8.27 建立动态数组,输入5个学生的成绩,另外用一个 函数检查其中有无低于60分的,输出不合格的成绩。 #include <stdio.h> #include <stdlib.h> void main() { void check(int *); int *p1,i; void *p2; p2=malloc(5*sizeof(int)); p1=(int *)p2; for(i=0;i<5;i++) scanf("%d",p1+i); check(p1); free(p2); }

void check(int *p) { int i; printf("They are fail:"); for(i=0;i<5;i++) if (p[i]<60) printf("%d ",p[i]); printf("\n"); }