第7章 指针 存储地址的变量的类型就是指针类型 能直接对内存地址操作, 实现动态存储管理 容易产生副作用, 初学者常会出错

Slides:



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

第七章 指针 计算机公共教学部.
第六章 指针 指针的概念 指针变量 指针与数组 指针与函数 返回指针值的函数.
第十章 指针 分析C程序的变量所存放的数据:  数值型数据:整数、实数  字符型数据:字符、字符串 这些变量具有以下性质:
第七章 指针 教 材: C程序设计导论 主 讲: 谭 成 予 武汉大学计算机学院.
电子成绩单项目实现.
第10章 结构体与链表 本章要点: 结构体类型与结构体变量的定义 结构体变量的引用与初始化 结构体数组 链表处理 共用体类型和枚举类型
二级指针与二维数组.
C语言程序设计基础 第10章 指针进阶 刘新国.
10.1 二级指针 10.2 指针与二维数组 10.3 指针的动态存储分配 10.4 函数指针 10.5 main函数的参数
第 6 章 第 6 章 指 针 指 针 1.
第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]的地址;
8.1 指针的概念 8.2 指针变量 8.3 指针变量的基础类型 8.4 指针的运算 8.5 指针与一维数组 8.6 指针应用实例
第九章 指针 目录 指针与指针变量的概念 变量的指针和指向变量的指针变量 数组的指针和指向数组的指针变量
第5章 函数与模块化设计 学习目的与要求: 掌握函数的定义及调用方法 理解并掌握参数的传递方法 理解函数的嵌套与递归调用
程序设计基础.
第6章 指针 学习目的与要求: 了解指针的概念和相关术语 熟练掌握指向变量、数组和字符串的指针变量的使用方法 了解指向函数的指针变量
指 针 为什么要使用指针 指针变量 指针与数组 返回指针值的函数 动态内存分配 通过指针引用字符串 指向函数的指针 小 结 习 题.
补充内容 结构体 概述 定义结构体类型和定义结构体变量 结构体变量的引用 结构体变量的初始化 指针与结构体 用typedef定义类型的别名.
目录 10.1 指针的基本概念 10.2 指向变量的指针变量 10.3 指向数组的指针变量 10.4 指向函数的指针变量和指针型函数
第八章 指 针 8.1 指针的概念与定义 8.2 指针作函数参数 8.3 指针与数组 8.4 指针与函数 8.5 复杂指针.
第 十 章 指 针.
项目六 用指针优化学生成绩排名 项目要求 项目分析
第七章 函数 目录 有参的加法函数的开发 函数定义的一般形式 函数参数和函数的值 函数的调用
第九章 结构体和共用体 结构体的定义 结构体的使用 共用体的定义 共用体的使用 主讲:李祥 时间:2015年10月.
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 指向函数的指针
第五章 指针 5.1 指针的概念和定义 5.2 指针运算 5.3 指针和数组 5.4 字符串指针 5.5 指针数组 5.6 指向指针的指针
二维数组的指针表示 与复杂的指针例子 专题研讨课之三.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第8章 指针.
第八章 使用指针.
第十章 指针.
欲穷千里,更上层楼 第十章 指 针 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; 能很方便地使用数组和字符串; 并能象汇编语言一样处理内存地址,从而编出精练而高效的程序。指针极大地丰富了C语言的功能。 学习指针是学习C语言中最重要的一环,
第五章 习题课 电子信息与计算机科学系 曾庆尚.
第五章 指针 5.1 指针的概念 5.2 指针与数组 5.3 字符串指针.
9.1 地址、指针和变量 9.2 指针运算 9.3 指针与数组 9.4 函数与指针 9.5 程序综合举例 9.6 上机实训.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
第十章 用户自定义数据类型 目录 学生信息管理系统的开发 结构体数据类型的概述 结构体变量的使用 结构体数组
第十章 结构体与链表 西安工程大学.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
C语言复习3----指针.
C语言大学实用教程 第7章 指针 西南财经大学经济信息工程学院 刘家芬
函数 概述 模块化程序设计 基本思想:将一个大的程序按功能分割成一些小模块, 特点: 开发方法: 自上向下,逐步分解,分而治之
第八章 指標 (Pointer).
指针 几个概念:  指针也是一种数据类型,具有指针类型的变量,称为指针变量。
第6讲 指针与引用 6.1 指针 6.2 引用.
第六章 指针 C++程序设计中使用指针可以: 使程序简洁、紧凑、高效 有效地表示复杂的数据结构 动态分配内存 得到多于一个的函数返回值.
第6章 指针 6.1 指针的概念 6.2 变量与指针 6.3 数组与指针 6.4 字符串与指针 6.5 函数与指针 6.6 返回指针值的函数
第十章 指针 指针是C语言的重要概念,是C语言的特色,是C语言的精华。 10.1 地址和指针的概念 内存中的每一个字节都有一个地址。
C程序设计.
第九章 指针.
第九节 赋值运算符和赋值表达式.
第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章 指针.
第九章 指针 C程序设计中使用指针可以: 使程序简洁、紧凑、高效 有效地表示复杂的数据结构 动态分配内存 得到多于一个的函数返回值.
第八章 指 针 北京邮电大学出版社.
安排座位.
第7章 地址和指针 7.1 地址和指针的概念 7.2 指针变量的定义和指针变量的基类型 7.3 给指针变量赋值 7.4 对指针变量的操作
Presentation transcript:

第7章 指针 存储地址的变量的类型就是指针类型 能直接对内存地址操作, 实现动态存储管理 容易产生副作用, 初学者常会出错 第7章 指针 存储地址的变量的类型就是指针类型 能直接对内存地址操作, 实现动态存储管理 容易产生副作用, 初学者常会出错 学习时应特别细心, 多动脑、多对比、多上机

7.1.1 变量的地址和变量的值 内存编址: 内存分配: 变量引用: 变量赋值: 内存是连续的存储空间。 对内存进行了编址 7.1.1 变量的地址和变量的值 内存编址: 内存是连续的存储空间。 对内存进行了编址 内存编址是连续的, 它的基本单位为字节 内存分配: 系统根据类型为变量分配内存单元 变量内存单元的起始地址即为变量的地址 编译后, 每个变量名对应一个地址 对变量的访问就是通过这个地址进行的 变量引用: 从变量名对应的地址开始的若干内存单元(字节数由定义类型确定)中取出数据; 变量赋值: 将数据按该变量定义的类型存入对应的内存单元中 内存单元的内容就是变量的值。

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

指针变量的定义 指针变量与其所指向的变量之间的关系 一般形式: [存储类型] 数据类型 *指针变量名; 例 int *p1,*p2; 3 变量i 2000 i_pointer *i_pointer i &i i=3; *i_pointer=3 3 变量i 2000 i_pointer *i_pointer i &i i=3; *i_pointer=3 指针变量的定义 一般形式: [存储类型] 数据类型 *指针变量名; 例 int *p1,*p2; float *q ; int i=3, *i_pointer; i_pointer=&i; 表示定义指针变量 不是‘*’运算符 合法标识符 指针的目标变量的数据类型 注意: 1、int *p1, *p2; 与 int *p1, p2; 2、指针变量名是p1,p2 ,不是*p1,*p2 3、指针变量只能指向定义时所规定类型的变量 4、指针变量定义后,变量值不确定,应用前必须先赋值

指针变量:专门存放变量地址的变量叫指针变量 指针:一个变量的地址 指针变量:专门存放变量地址的变量叫指针变量 int a=10, *p; p=&a; 整型变量 指针 …... 2000 2004 2006 2005 变量a 10 变量p 2001 2002 2003 变量的内容 变量的地址 2000 指针变量 指针变量 变量 变量地址(指针) 变量值 指向 地址存入

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

指针变量必须先赋值,再使用 main() 例 main( ) { { int i=10; int *p; int i=10,*p; printf(“%d”,*p); } 例 main( ) { int i=10; int *p; *p=i; printf(“%d”,*p); } …... 2000 2004 2006 2005 整型变量i 10 指针变量p 2001 2002 2003 随机 危险! 例 main( ) { int i=10,k=3; int *p; p=&k; *p=i; printf(“%d”,*p); }

7.2.2 指针变量的引用 #include <stdio.h> void main() { int *p; int a; p=&a; printf("please input first int number:"); scanf("%d",&a); printf("%d %d\n",*p, a); printf("please input second int number:"); scanf("%d", p); *p=*p+2; a=a+2; }

空指针 p指向地址为0的单元, 系统保证该单元不作它用 表示指针变量值没有意义 #define NULL 0 int *p=NULL: 定义:指针变量值为零 表示: int * p=0; #define NULL 0 int *p=NULL: p=NULL与未对p赋值不同 用途: 避免指针变量的非法引用 在程序中常作为状态比较 例 int *p; ...... while(p!=NULL) { ...… }

i_pointer &i &(*i_pointer) i *i_pointer *(&i) &与*运算符 含义 含义: 取变量的地址 单目运算符 优先级: 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---指针变量占用内存的地址

注意: (1)  指针变量定义和引用时“*”含义有差别。 在引用中 “*”是运算符, 表示指针变量指向的变量。 在指针变量定义时“*”理解为指针类型定义符 表示定义的变量是指针变量。 (2) 不能引用没有赋值的指针变量。(盲指针) (3) p=&a;是给指针变量p赋值。 *p=3; 是给p指向的变量赋值。 两者含义完全不同。 (4)必须用同类型的指针给指针变量赋值。 指针变量只存放地址。 不能直接用整型量(或非地址量)赋值给指针变量。

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

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

例 7.1 取地址运算符&和指向运算符*的应用。 main() { int m, n; int *p=&m,*q=&n; printf("Input m,n:"); scanf("%d %d",p,&n); /* 指针变量p之前不加&,它与&m相同 */ printf("m=%d &m=%X\n",m,&m); printf("*p=%d p=%X\n",*p,p); printf("n=%d &n=%X\n",n,&n); printf("*q=%d q=%X\n",*q,q); }

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

特点:共享内存,“双向”传递 例 将数从大到小输出 swap(int x,int y) { int temp; 5 temp=x; x=y; …... 2000 2008 200A 2002 2004 2006 变量a 变量b (main) swap(int x,int y) { int temp; temp=x; x=y; y=temp; } main() { int a,b; scanf("%d,%d",&a,&b); if(a<b) swap(a,b); printf("\n%d,%d\n",a,b); 5 9 值传递 运行结果:5, 9

int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); 例 将数从大到小输出 swap(int *p1, int *p2) { int p; p=*p1; *p1=*p2; *p2=p; } 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); …... 2000 2008 200A 2002 2004 2006 200C 200E 2010 ... 整型变量a 整型变量b (main) 指针pointer_1 指针pointer_2 9 5 5 9 2000 2000 2002 2002 COPY (swap) 指针p1 指针p2 整型p 5

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

7.3 指针与数组 数组名代表该数组0号元素的地址(数组首地址); 数组名是指向该数组首元素的指针常量; 数组名与指针有相同的概念 ; 数组名是指针类型的符号常量; 7.3.1 指向一维数组元素的指针 1.定义指向数组元素的指针变量 int a[10]; /* 数组元素是整型变量 */ int *p; /* 定义p是指向整型变量的指针变量 */ p=&a[0]; /* 赋值后p指向a数组的0号元素 */ p=&a[5]; /* 赋值后p指向a数组的5号元素 */ p=&a[0]; 和 p=a; 两句等价

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

2.指针运算 1、指针变量可以和整数做加减操作 2、指针变量加减一个整数n, 表示指针前后移动n个元素 3、地址值增减量等于所指向变量占的字节数sizeof(type) (步长 d) int a[10]; int *p1=&a[5]; /* 定义了p1指向整数类型(d=2),初值为a[5]的地址 */ p1- -; /* p1减1, p1指向a[4],地址值减(1×sizeof(int)) */ p1+=3; /* p1加3, p1指向a[7],地址值加(3×sizeof(int)) */ 两个同类型指针可以相减得到一个整数,等于对应元素下标差, 等于地址值的差除以地址步进单位。 两个指针之间不能进行加法、乘法、除法等算术运算。

指针的运算 指针变量的赋值运算 如 int i, *p; p=1000; () i=p; () p=&a; (将变量a地址p) p=array; (将数组array首地址p) p=&array[i]; (将数组元素地址p) p1=p2; (指针变量p2值p1) 不能把一个整数p,也不能把p的值整型变量 如 int i, *p; p=1000; () i=p; ()

指针的算术运算: 1 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 无意义 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] a数组 p p+1,a+1 p+i,a+i p+9,a+9 例 p指向int型数组,且p=&a[0]; 则p+1 指向a[1] 例 int a[10]; int *p=&a[2]; p++; *p=1; 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

a[i]  p[i]  *(p+i) *(a+i) 数组元素表示方法 [] 变址运算符 a[i]  *(a+i) p=a; 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]  p[i]  *(p+i) *(a+i)

#include<stdio.h> void main() { int a[10],i; 3.通过指针引用数组元素 #include<stdio.h> void main() { int a[10],i; for(i=0;i<10;i++) scanf(“%d”,&a[i]); for(i=0;i<10;i++) printf(“%d”,a[i]); } #include<stdio.h> void main() { int a[10],i,*p; p=a; for(i=0;i<10;i++) scanf(“%d”,p+i); for(i=0;i<10;i++) printf(“%d”, *(p+i)); }

#include<stdio.h> void main() { int a[10],*p; for(p=a;p<a+10;p++) scanf(“%d”,p); for(p=a;p<a+10;p++) printf(“%d”, *p); } #include<stdio.h> void main() { int a[10],i,*p; for(p=a ,i=0; i<10; i++,p++) scanf(“%d”,p); for(p=a ,i=0; i<10; i++,p++) printf(“%d”, *p); }

for(i=0;i<7;i++,p++) printf("%d",*p); } 例 注意指针的当前值 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); } 5 8 7 6 2 3 1 4 a p p p p p p=a; p p p 指针变量可以指到数组后的内存单元

例 7.4 数组名与指向数组元素的指针变量。 main() { int a[5]={10,20,30,40,50}, *p=a; printf("p=%x p+1=%x\n",p,p+1); printf("a=%x a+1=%x\n",a,a+1); /* a地址值步进单位是2不是10 */ printf("&a[0]=%x &a[1]=%x \n",&a[0],&a[1]); printf("*p+2=%d *(p+2)=%d\n",*p+2,*(p+2)); /* 注意*p+2和*(p+2)的区别 */ printf("*a+2=%d *(a+2)=%d\n",*a+2,*(a+2)); }

4.地址越界问题 (1)不要引用没有赋值的指针变量, 使用指针变量前一定要对它正确赋值。 指针变量重新赋值后, 其中的地址值发生了变化, 新的地址值是否指向所需要的变量, 新的地址值是否有实际意义, 系统对此都不作检查, 需要由程序员自己检查 (1)不要引用没有赋值的指针变量, 使用指针变量前一定要对它正确赋值。 (2) 用指针变量访问数组元素, 随时要检查指针的变化范围, 始终不能超越上下界。 (3)指针运算中注意各运算符的优先级和结合顺序, 多使用括号, 使程序容易理解。

5.用数组名或指针变量作函数参数。 数组名作函数参数,是地址传递 数组名作函数参数,实参与形参的对应关系 实参 形参 数组名 指针变量

例 7.6 通过调用一个函数, 将整型数组的所有元素加10。 参数传递用四种方法实现。 (1)    程序如下:  #include<stdio.h> void add(int b[ ], int n) { int i; for(i=0; i<n; i++) b[i]+=10; } void main( ) { int i, a[10]= {1,2,3,4,5,6,7,8,9,10}; add(a,10); for(i=0; i<10; i++) printf("%4d",a[i]);

(2)   程序如下: #include<stdio.h> void add(int *p, int n) { int *pend=p+n; for(; p<pend; p++) *p+=10; } void main( ) { int a[10]={1,2,3,4,5,6,7,8,9,10}, *q=a; add(q,10); for(q=a;q<a+10;q++) printf("%4d",*q);

(3)   程序如下:  #include<stdio.h> void add(int *p, int n) { int *pend=p+n; for(; p<pend; p++) *p+=10; } void main( ) { int i, a[10]= {1,2,3,4,5,6,7,8,9,10}; add(a,10); for(i=0; i<10; i++) printf("%4d",a[i]);

(4) 程序如下: #include<stdio.h> void add(int b[ ], int n) { int i; for(i=0; i<n; i++) b[i]+=10; } void main( ) { int a[10]={1,2,3,4,5,6,7,8,9,10}, *q=a; add(q,10); for(q=a;q<a+10;q++) printf("%4d",*q);

例 7.7将一维数组的各元素循环右移m个位置, 用函数实现 #include<stdio.h> void rmove(int a[ ],int m, int n) { int i,*p,t; for(i=0; i<m; i++) { p=a+n-1; t=*p; for(; p>a; p--) *p=*(p-1); *p=t; } void main() { int m,n,a[100],*p; printf("\n n="); scanf("%d",&n); printf("Input %d numbers:\n",n); for(p=a; p<a+n; p++) scanf("%d", p); printf("How many place you want to move?"); scanf("%d", &m); rmove(a,m,n); printf("New:\n"); for(p=a; p<a+n; p++) printf("%5d",*p); 例 7.7将一维数组的各元素循环右移m个位置, 用函数实现

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

7.3.2 指向多维数组元素和指向分数组的指针 *(*(a+0)+1) *(a[0]+1) int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}}; a[0] a[1] a[2] 2000 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+1由2000步进2008,实际是4个元素空间 a[i]  *(a+i)  &a[i][0], 表示第i行第0列元素地址 a[0]+1由2000步进2002,实际是1个元素空间

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]+2*4+4)

*(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 2016 2024 13 a为二维数组名 a[i] <=> *(a+i)

2.指向数组元素的指针变量 例 7.8 用指针变量输出数组元素的值。 main() { int a[3][5]={ {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11,12,13,14,15} }; int *p; for (p=a[0];p<a[0]+15;p++) { if((p-a[0])%5==0) printf("\n"); printf("%4d",*p); }

3.指向分数组的指针变量 a a+1 a+2 p p+1 p+2 表示*p有4个元素,每个元素为整型,即p是指向一维数组的指针。 定义形式: 数据类型 (*指针名)[数组维数]; 例 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有4个元素,每个元素为整型,即p是指向一维数组的指针。 可让p指向二维数组某一行 如 int a[3][4], (*p)[4]=a; p[0]+1或 *p+1 p[1]+2或 *(p+1)+2 p的值是一维数组的 首地址,p是行指针 ( )不能少 int (*p)[4]与int *p[4]不同 *(*p+1)或 (*p)[1] *(*(p+1)+2) 一维数组指针变量维数和 二维数组列数必须相同 指向多维数组中的分数组的指针变量,所指向的应该是降一维的整个分数组。

例 7.9 用指向二维数组中分数组的指针变量, 按行输出二维数组各元素的值。 main() { int a[3][5]={ {1, 2, 3, 4, 5 }, {6, 7, 8, 9, 10}, {11,12,13,14,15} }; int *q,(*p)[5]; for(p=a;p<a+3;p++) /* d=10 */ { for(q=*p;q<*p+5;q++) /* d=2 */ printf("%5d",*q); printf("\n"); }

4.用多维数组名和指针变量作函数参数 (1) 用多维数组名作实参或形参。 如: f(int a[][5], int n);   (1) 用多维数组名作实参或形参。 如: f(int a[][5], int n); (2)  用指向元素的指针变量作实参或形参。 f1(int *p); (3)   用指向分数组的指针变量作实参或形参。 如:f2(int (*q)[5], int m); 注意: 后两者之间指针移动时地址的增量是不同的。

例 7.10 调用函数求两个矩阵之和 #define M 2 main() #define N 4 void add(int (*p1)[N],int (*p2)[N]) { int *q1,*q2,(*u)[N]=p1+M; for(;p1<u;p1++,p2++) { q1=*p1; for(q2=*p2;q1<*p1+N;q1++) { *q1+=*q2; q2++; } void print(int (*p)[N]) { int *q,(*u)[N]=p+M; for(;p<u;p++) { for(q=*p;q<*p+N;q++) printf("%6d",*q); printf("\n"); main() { int i,j,a[M][N]={ {1,2,3,4}, {5,6,7,8} }; int b[M][N]={{10,20,30,40}, {50,60,70,80} print(a); print(b); add(a,b); for(i=0;i<M;i++) { for(j=0;j<N;j++) printf("%6d",a[i][j]); printf("\n"); }

7.3.3 用字符数组和字符型指针访问字符串 1.用字符型指针变量整体输入/输出字符串 用字符型数组和字符指针两种方法整体输入输出字符串。 7.3.3 用字符数组和字符型指针访问字符串 1.用字符型指针变量整体输入/输出字符串 用字符型数组和字符指针两种方法整体输入输出字符串。 #include<stdio.h> main() /*定义pt为指向字符串首字符的指针变量 */ { char s[20]="I am a student!",*p=s; char *pt=“You are a teacher!”; printf(“%s\n”,s); /*用字符型数组整体输出字符串 */ printf(“%s\n”,pt); /* 用字符指针整体输出字符串 */ scanf("%s",s); printf("%s\n",s); scanf("%s",p); printf("%s\n",p); }

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

2.对使用字符指针变量和字符数组的讨论 (1)存储内容不同:字符指针变量存放字符串0号元素的地址;字符数组存放若干字符; (2)分配的内存单元不同: (3)赋值方法不同:字符数组只能在变量定义是整体赋初值,不能用赋值语句整体赋值,用赋值语句只能对各个元素分开赋值。字符指针变量,赋值语句将字符串首地址赋值给它,起到整体赋值的效果。 (4)指针变量的值是可以改变的, 字符数组名是地址常量, 它的值是不能改变的 char *p , char s[16]; scanf("%s",s); /* s是地址常量, 有确定地址, 正确 */ scanf (“%s”,p); /* p未指向一个具体的地址,不应这样做 */ char str[16]="I am a srudent."; /* 只能在变量定义时整体赋值 */ 可以用赋值语句将字符串首地址赋值给字符指针变量 char *a; a="I am a srudent.";

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

3.用字符数组或字符指针作函数参数传递字符串 void strcpy(char s1[],char s2[]) { int i=0; while(s2[i]!='\0') { s1[i]=s2[i]; i++; } s1[i]='\0'; 用字符数组名或用指向字符的指针变量做函数参数,将实参字符串的首地址赋值给被调函数的形参。 void strcpy(char *s1, char *s2) { for( ; *s2!='\0'; s1++, s2++) *s1=*s2; *s1='\0'; }

7.3.4 指针数组和指向指针的指针 用于处理二维数组或多个字符串 1.指针数组 定义:数组中的元素为指针变量 7.3.4 指针数组和指向指针的指针 用于处理二维数组或多个字符串 1.指针数组 定义:数组中的元素为指针变量 定义形式:[存储类型] 数据类型 *数组名[数组长度说明]; 例 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] 初始化: 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

p[0]=a; p[1]=b; p[2]=c; p[3]=NULL; …….. } 或: { char *p[4]; 指针数组赋值与初始化 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] 赋值: main() { char a[]="Fortran"; 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; 初始化: 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]

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

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

例 7.15 指针数组的各元素指向整型数据的简单实例 #include <stdio.h> main() { static int a[5]={10,20,30,40,50}; /* 要用a的元素地址做初值,应加static */ int *q[5]={&a[0],&a[1],&a[2],&a[3],&a[4]}; int **p; for(p=q;p<q+5;p++) printf("%d\t",**p); }

例 7.16 利用指向分数组的指针变量, 输入多个字符串, 将它们按行存储在二维字符数组中, 然后输出全部字符串。再用字符型指针数组和指向指针变量的指针变量实现。 main() { char a[4][20]; char (*p)[20]; /* p是指向分数组的指针变量*/ printf("Input strings:\n"); for(p=a;p<a+4;p++) gets(*p); printf("Output strings:\n"); printf("%s\n",*p); }

3.指针数组作main( )函数的形参 例 7.17 /* 下标法 */ main( int argc, char *argv[ ]) { int i; for (i=1;i<argc;i++) printf("%s\n",argv[i]); } /* 指针法 */ main( int argc, char **argv) { while(--argc>0) printf("%s\n",*++argv); }

7.4结构体和指针 1.指向结构体变量的指针 定义形式:struct 结构体名 *结构体指针名; main() 例 struct student *p; main() { struct student { long int num; char name[20]; char sex; float score; }stu_1,*p; p=&stu_1; stu_1.num=89101; strcpy(stu_1.name,"Li Lin"); p->sex='M'; p->score=89.5; printf("\nNo:%ld\nname:%s\nsex:%c\nscore:%f\n", (*p).num,p->name,stu_1.sex,p->score); } 使用结构体指针变量引用成员形式 num name sex age stu p struct student { int num; char name[20]; char sex; int age; }stu; struct student *p=&stu; 存放结构体变量在内存的起始地址 (*结构体指针名).成员名 结构体指针名->成员名 结构体变量名.成员名 例 int n; int *p=&n; *p=10;  n=10 struct student stu1; struct student *p=&stu1; stu1.num=101;  (*p).num=101 例 指向结构体的指针变量 指向运算符 优先级: 1 结合方向:从左向右

*p表示p指向的结构体变量,在访问结构体成员时,由于成员运算符 “·”优先于“*”运算符,*p两侧应加圆括号,即(*p) 例: (*p).number -> 称为指向运算符,具有最高的优先级,按自左至右的方向结合。

7.4.2 指向结构体数组元素的指针 指针变量可以指向结构体数组中的元素,改变指针变量的值就可以通过它访问结构体数组中的各元素。 7.4.2 指向结构体数组元素的指针 指针变量可以指向结构体数组中的元素,改变指针变量的值就可以通过它访问结构体数组中的各元素。 struct examp { int n, m; }; struct examp a[10],*p=a; a[0].m=0; a[0].n=0; p++; p->n=1; p->m=1; p=p+2; p->n=a[0].n+1; p->m= a[0].m+1;

struct examp { int n, m; }; struct examp a[10],*p=a; p->n++ 引用p指向的结构体变量中成员n的值, 然后使n增1。 (p++)->n 引用p指向的结构体变量中成员n的值, 然后使p增1 ++p->n 使p指向的结构体变量中成员n的值先增1(不是p的值增1), 再引用n的值。 (++p)->n 使p的值增1, 再引用p指向的结构体变量中成员n的值。

指向结构体数组的指针 例 指向结构体数组的指针 struct student { int num; char name[20]; 例 指向结构体数组的指针 struct student { int num; char name[20]; char sex; int age; }stu[3]={{10101,"Li Lin",'M',18}, {10102,"Zhang Fun",'M',19}, {10104,"Wang Min",'F',20}}; main() { struct student *p; for(p=stu;p<stu+3;p++) printf("%d%s%c%d\n",p->num,p->name,p->sex,p->age); } num name sex age stu[0] p stu[1] stu[2] p+1

#define N 10 /* 结构体数组按成绩进行选择排序 */ #include<string.h> struct student { int num; char name[12]; float score;}; struct student s[N]; main( ) { struct student *p ,*q[N]; int i,j,k; printf (" Input %d student's num name score\n",N); p=s; for (i=0; i<N; i++) { scanf("%d%s%f",&p->num,p->name,&p->score); q[i]=p++; } for (i=0; i<N-1; i++) { k=i; for (j=i+1; j<N; j++) if (q[k]->score<q[j]->score) k=j; if (k!=i) { p=q[i]; q[i]=q[k]; q[k]=p; } printf("No.: Name: score\n"); printf("%5d%15s %8.1f\n",q[i]->num,q[i]->name,q[i]->score);

77 88 66 53 63 74 89 78 98 54 q0 q1 q2 q3 q4 q5 q6 q7 q8 q9 77 88 66 53 63 74 89 78 98 54 s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 q0 q1 q2 q3 q4 q5 q6 q7 q8 q9

7.4.3 用指向结构体的指针作函数参数 1、采取“值传递”的方式 2、采取“引用传递”的方式(传地址) 7.4.3 用指向结构体的指针作函数参数 1、采取“值传递”的方式 函数调用时, 将实参结构体的全部成员按顺序赋值给形参, 这种传递方式在空间和时间上开销较大,由于采用值传递方式, 如果在被调用函数中改变了结构体形参的值, 该值不能返回主调函数。因此一般较少使用 2、采取“引用传递”的方式(传地址) 用指向结构体变量(或数组)的指针作实参, 将结构体变量(或数组)的地址传给形参。如果在被调用函数中改变了形参指向的结构体的值, 该值可以带回主调函数

#define N 10 /*用指向结构体的指针作函数参数 */ #include<string.h> #define FMT "%4d %12s%8d%8d%8d%10.1f\n" struct student { int num; char name[12]; int sc[3]; float av; }; void input(struct student *s ); main() { struct student s[N],*p; printf("Input student:number name score1 score2 score3\n"); for(p=s; p<s+N; p++) input(p); printf("number name score1 score2 score3 average\n"); printf(FMT,p->nu,p->na,p->sc[0],p->sc[1],p->sc[2],p->av); } void input(struct student *s) { scanf("%d%s%d%d%d",&s->nu,s->na,&s->sc[0],&s->sc[1],&s->sc[2]); s->av=(s->sc[0]+s->sc[1]+s->sc[2])/3.0;

7.5 指针与函数 7.5.1 返回指针值的函数 定义格式: 类型名 *函数名(参数表); 7.5.1 返回指针值的函数 定义格式: 类型名 *函数名(参数表); 例如: int *f(int x, int y); char *strcpy(char *s1,char *s2) { char *p=s1; while(*s1++=*s2++); return(p); } main() { char s[20]= "I am a student."; printf("%s\n",strcpy(s,"You are a eacher."));

例 写一个函数,求两个int型变量中居于较大值的变量的地址 int *f1(int *x,int *y) { if(*x>*y) return x; else return y; } main() { int a=2,b=3; int *p; p=f1(&a, &b); printf("%d\n",*p); …... 2000 2008 200A 2002 2004 2006 变量a 变量b (main) 指针变量p COPY 2 3 2002 ** 指针变量y 指针变量x (f1) 2000 2002

7.5.2 指向函数的指针变量和函数参数 1.定义 类型说明符 (*变量名)( ); int (*p)( ); 2.赋值 变量名=函数名 7.5.2 指向函数的指针变量和函数参数 1.定义 类型说明符 (*变量名)( ); int (*p)( ); (*变量名)表示该变量名被定义为指针变量,其后的空括号()表示指针变量所指向的是一个函数。类型说明符定义了该函数的返回值的类型。 2.赋值 变量名=函数名 p=functionname; 3.调用 变量名(实参列表) p(x,y);

例 7.23 指向函数的指针变量程序举例。 #include<stdio.h> int f(int x); { return 3*x*x+5*x-7; } void main() { int (*p)( ); /* 定义p为指向函数的指针变量*/ int a,y; p=f; /* 对指向函数的指针变量p赋值*/ printf("input x= "); scanf("%d",&a); y=(*p)(a); /* 用函数指针变量形式调用函数, 相当于y=f(a) */ printf("y=%d\n ",y); }

用指向函数的指针变量作函数参数 例 用函数指针变量作参数,求最大值、最小值和两数之和 void main() 例 用函数指针变量作参数,求最大值、最小值和两数之和 void 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); } void process(int x,int y,int (*fun)()) { int result; result=(*fun)(x,y); printf("%d\n",result); max(int x,int y) { printf(“max=”); return(x>y?x:y); min(int x,int y) { printf(“min=”); return(x<y?x:y); add(int x,int y) { printf(“sum=”); return(x+y);

总 结 int i; 定义整形变量i; int *p; p为指向整形变量的指针变量; 总 结 int i; 定义整形变量i; int *p; p为指向整形变量的指针变量; int p[n]; 定义整形一维数组p,它有n 个整形元素; int *p[n];定义一维指针数组p,它有n个指向整形变量的指针元素; int (*p)[n];定义p为指向一维数组(含有n个整形元素)的指针变量; int p( ) ; p为返回整形函数值的函数; int *p( ); p为返回一个指针的函数,该指针指向整形数据; int (*p)( ); p为指向函数的指针变量,该函数返回一个整形值; int **p; p是一个指向整形指针变量的指针变量;

作业 7.1 ~~7.15 7.18~~7.20