第 13讲指针与一维数组
2
新的概念,指针( Pointer)
指针也是一种数据类型
一种特殊的数据类型,这种类型存储的是 地址
C/c++的特色
有些复杂但很实用
指针变量与指针常量
指针变量
专门存放地址数据的变量
指针常量
指一个固定的地址,例如:数组名
3
两种寻址方式
如何读写内存中的数据?
通过变量的地址访问变量所在的存储单元
两种寻址方式
直接 (寻址)访问
直接按变量地址来存取变量内容的访问方式
间接 (寻址)访问
通过指针变量来间接存取它所指向的变量的访问方式房间 1
东西房间 2
房间 1
的钥匙直接 寻址 间接 寻址
4
间接寻址访问:
int i;
int *p;
p=&i;
*p=3;
重点:直接寻址与间接寻址的表示
直接寻址访问:
int i;
i=3;
2000
0

变量 i

3010 变量 pi
地址 数据 变量名
如果能直接寻址那当然就不用间接寻址了,
但有时,只能用间接寻址的方法解决问题

数据类型,int *(指向整型变量的指针)
变量名称,p
变量的初值,&i
含义:向系统申请一个动态区的内存空间,用来存储整型指针变量 p的初值 &i,即将 p指向了变量 i。
通过 *p这种形式,实现对变量 i的 间接引用 。
①②2000
3
5
&与 *操作符
&用来取变量的地址
int i,*p;
p = &i;
int *p,a[10];
p = a;
int *p,a[10];
p = &a[0];
*用来取指针指向地址的内容
*p = 0;
搞清楚下边的两个赋值
int i,*p=&i;/*对指针变量赋值 */
*p=1;/*对指针变量引用的变量 I赋值 */
6
读程序 -1main(){int a=5,b=10;
int *pa=&a,*pb=&b;
*pb=-1;
printf(“b=%d\n”,*pb);
pa=pb;
printf(“*pa=%d,*pb=%d”,*pa,*pb);
}
int i,*p;
p=&i;
*p=100;?读程序的关键:
–指针变量指向哪儿?
–所指向的变量里存储的数据是多少?
–更改的是指针还是指针所指向的变量?
&a &b
pa pb
5 10
a b
-1
7
int i,*p=&i;
float j,*q=&j;
p=q;
int i;
float *p;
p=&i;
int *p;
p=100;必须进行赋值才能引用 !否则
p指向了哪呢?
一个指针变量不能指向与其类型不同的变量 !float又称变量 P的基类型应在类型相同的指针变量之间赋值
int *p;
scanf(“%p”,&p);
*p=100;
指针 变量 只存放地址 !
int *p;
*p=100;
对指针变量赋值只能通过 &求得!
8
指针变量与其它类型变量的对比
共性
在内存中占据一定大小的存储单元
先定义,再使用
指针变量的特殊性
指针变量只能存放地址,而不能存放数据
必须初始化后才能使用,否则指向不确定的存储单元
只能指向同一基类型的变量,否则 warning.
可参与的运算:加、减、关系、赋值
9
为什么引入指针的概念
指针有如下好处:
为函数提供修改变量值的手段
为 C的动态内存分配系统提供支持
可以改善某些子程序的效率
为动态数据结构(如例链表、队列、二叉树等)提供支持
10
主调函数 被调函数
main()
{ int a,b;
a = 15;
b = 8;
Swap(a,b);
printf
("a=%d,b=%d",a,b);
}
void Swap(int x,int y)
{ int temp;
temp = x;
x = y;
y = temp;
}
55
a b
实 参 形 参
99
x ya b
传数值调用
x y
5
temp
9 5
11
主调函数 被调函数
main()
{ int a,b;
a = 15;
b = 8;
Swap(&a,&b);
printf("a=%d,b=%d",a,b);
}
void Swap(int *x,int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
&a
实 参 形 参
&b
x ya b
传地址调用
x y
5
temp
5
a b
99 5 地址值
指针的好处之一,
通过 将主调函数变量的地址传给形参 实现了 改变主调函数中变量的值 。
当计算结果不只一个时,
可以用这种方法,实现数据的“返回”。
12
swap函数这样写行不行?
void Swap(int *p1,int *p2)
{
int *p;
p = p1; /*p1,p2为局部变量 */
p1 = p2;
p2 = p;
}
交换的是地址 !变量并没有交换
13
这样写呢?
void Swap(int *p1,int *p2)
{
int *p; /**/
*p = *p1;
*p1 = *p2;
*p2 = *p;
}
指针 p未初始化,*p1存在哪儿呢?
14
例 6-17:在一个班级的成绩表里找出最大值和相应的学号
算法:
1,输入班级成绩
2,求最大值以及对应的学号(写成函数)
3,输出查找结果
15
函数写法一:求最大值对应的下标
/*函数功能:在长度为 n的 score数组中找最大值,返回最大值所在的下标 */
int FindMax(float score[],int n )
{
int i,max_i=0;
for (i=1; i<n; i++)
{
if (score[i] > score[max_i])
{
max_i=i;
}
}
return max_i;
}
16
完整程序 1
#include <stdio.h>
#define ARR_SIZE 40
int FindMax(float score[],int n ); /*函数声明 */
void main()
{
float score[ARR_SIZE],maxScore;
int n,i,max_i;
long num[ARR_SIZE],maxNum;
printf("Please enter total number:");
scanf("%d",&n); /*从键盘输入学生人数 n*/
printf("Please enter the number and score:\n");
for (i=0; i<n; i++) /*分别以长整型和实型格式输入学生的学号和成绩 */
{
scanf("%ld%f",&num[i],&score[i]);
}
max_i=FindMax(score,n); /*计算最高分对应的下标 */
printf("maxScore = %.0f,maxNum = %ld\n",score[max_i],
num[max_i]);
}
/*函数功能:
在长度为 n的 score数组中找最大值,返回最大值所在的下标 */
int FindMax(float score[],int n )
{
int i,max_i=0;
for (i=1; i<n; i++)
{
if (score[i] > score[max_i])
{
max_i=i;
}
}
return max_i;
}
17
函数写法二:求最大值以及相应的学号
/*函数功能:在长度为 n的 score数组中找最大值,并将最大值和相应的学号保存至 指针变量 pMaxScore和 pMaxNum间接引用的变量中 */
void FindMax(float score[],long num[],int n,
float *pMaxScore,long *pMaxNum)
{
int i;
*pMaxScore = score[0];
*pMaxNum = num[0];
for (i=1; i<n; i++)
{
if (score[i] > *pMaxScore)
{
*pMaxScore = score[i];
*pMaxNum = num[i];
}
}
}
把结果存到指针变量间接引用的主函数中的变量中去,没有返回值。
18
完整程序 2
#include <stdio.h>
#define ARR_SIZE 40
void FindMax(float score[],long num[],int n,float *pMaxScore,
long *pMaxNum); /*函数声明 */
main()
{
float score[ARR_SIZE],maxScore;
int n,i;
long num[ARR_SIZE],maxNum;
printf("Please enter total number:");
scanf("%d",&n); /*从键盘输入学生人数 n*/
printf("Please enter the number and score:\n");
for (i=0; i<n; i++) /*分别以长整型和实型格式输入学生的学号和成绩 */
{
scanf("%ld%f",&num[i],&score[i]);
}
FindMax(score,num,n,&maxScore,&maxNum); /*计算最高分和学号
*/
printf("maxScore = %.0f,maxNum = %ld\n",maxScore,maxNum);
}
void FindMax(float score[],long num[],int n,
float *pMaxScore,long *pMaxNum)
{
int i;
*pMaxScore = score[0];
*pMaxNum = num[0];
for (i=1; i<n; i++)
{
if (score[i] > *pMaxScore)
{
*pMaxScore = score[i];
*pMaxNum = num[i];
}
}
}
19
指针运算-地址运算
单个指针的运算
加减运算:每加(减) 1表示指针指向后(前)一个数据单元
例,p++;--p;p=p+n;
两个指针的运算
比较:用来比较两个指针的前后位置
例,(p1>p2)
相减:两个指针相距多远?
主要用于对数组的处理
20
读程序- 2
#include <stdio.h>
void main ( )
{ short a[]={2,4,6,8,10};
int y=0,x,*p;
p=&a[0];
for(x=0;x<5;x++)
y + = * (p+x);
printf("%d\n",y);
}
两个重要内容:
指针指向了哪个数组元素?
是指针运算还是数组元素运算
6000
6001
6002
6003
6004
6005
6006
6007
6008
6009
a
a[0]
a[1]
a[2]
a[3]
a[4]
2
4
6
8
10
p
6000
p+1
p+2
p+3
p+4
除了用 a[i]的这种形式以外,还有多种形式。
21
用数组名(指针常量)
引用数组元素
6000
6001
6002
6003
6004
6005
6006
6007
a[0]
a[1]
a[2]
a[3]
a
a+1
a+2
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]);
}
输入输出数组元素:
方法 1:数组名+下标
main()
{int a[10],i;
for (i=0; i<10; i++)
scanf(“%d”,? );
for (i=0; i<10; i++)
printf("%d ",? );
}
输入输出数组元素:
方法 2:数组名 +偏移量
a+i
*( +i)
22
用指针变量引用数组元素
main()
{int a[10],i;
int *p=a;
for (i=0; i<10; i++)
scanf("%d",&p[i]);
for (i=0; i<10; i++)
printf("%d ",p[i]);
}
输入输出数组元素:
方法 3:指针变量+下标
也可以用指针变量引用一维数组元素
int a[10],*p=a;
6000
6001
6002
6003
6004
6005
6006
6007
a[0]
a[1]
a[2]
a[3]
a
p
p+1
p+2
main()
{int a[10],i;
for (i=0; i<10; i++)
scanf(“%d”,? );
for (i=0; i<10; i++)
printf("%d ",? );
}
输入输出数组元素:
方法 4:指针变量 +偏移量
p+i
*(p+i)
23
用指针变量引用数组元素
main()
{int a[10],i;
int *p;
for (p=a; p<(a+10); p++)
scanf("%d",p);
for (p=a; p<(a+10); p++)
printf("%d ",*p);
}
输入输出数组元素:
方法 5:指针变化
也可以用指针变量引用一维数组元素
int a[10],*p=a;
6000
6001
6002
6003
6004
6005
6006
6007
a[0]
a[1]
a[2]
a[3]
a
p
p++
p++
通过数组名的变化引用数组元素行不行?
数组元素的引用形式
a[i]或 *(a+i)
p[i]或 *(p+i)
*(p++)
24
读程序- 3
#include <stdio.h>
main()
{
int a[] = {1,2,3,4,5};
int *p = NULL;
p = a;
printf("%d,",*p);
printf("%d,",*(++p));
printf("%d,",*++p);
printf("%d,",*(p--));
printf("%d,",*p++);
printf("%d,",*p);
printf("%d,",++(*p));
printf("%d,",*p);
}
1,2,3,3,2,3,4,4
25
区分几个表达式
P++
*p++
*(p++)
*(++p)
(*p)++
指向下一个地址单元
间接引用 p指向的数据单元,然后,p指向下一个地址单元
同上(注意:虽然++括在括号里,但由于++在后边,因此仍然后加)
先将 p指向下一个地址单元,然后间接引用
p指向的数据单元
P指向的数据 +1
26
读程序- 4
#include <stdio.h>
char b[] = "program";
char *a =,PROGRAM”;
/*定义一个指针变量指向字符串,与上边定义的区别是:前者是先申请内存空间,后存入字符串,而后者是先将字符串存入在内存的某个地方,然后再用 a指向该字符串所在内存的开始位置。另外。 b是常量,a是变量 */
main()
{
int i = 0;
printf("%c%s\n",*a,b + 1);
while (putchar (*(a + i)))
{ i++;
}
printf("i = %d\n",i);
while ( -- i)
{ putchar (*(b + i));
}
printf("\n%s\n",&b[3]);
}
Program
PROGRAM
i=7
Margor
gram
27
例:编写函数实现求和 1
float sum(float a[4])
{int i;float sum= a [0];
for(i=1;i<4;i++)
sum+= a [i];
return(sum);
}
main()
{int i; float sco[4],s;
for(i=0;i<4;i++)
scanf(“%f”,&sco[i]);
s=sum( sco );
printf(“%f”,s);
}
形参是 数组名实参是 数组名
既然可以用 数组名 和指向数组的 指针 分别引用数组以及数组元素,可否将形参和实参用指针的形式表示?
如何表示?
28
例:编写函数实现求和 2
float sum(float *PP)
{int i;float sum= PP [0];
for(i=1;i<4;i++)
sum+= PP [i];
return(sum);
}
main()
{int i; float sco[4],s;
for(i=0;i<4;i++)
scanf(“%f”,&sco[i]);
s=sum( sco );
printf(“%f”,s);
}
形参是 指针实参是 数组名
29
例:编写函数实现求和 3
float sum(float *PP)
{int i;float sum= PP [0];
for(i=1;i<4;i++)
sum+= PP [i];
return(sum);
}
main()
{int i; float sco[4],s;
float *p=sco;
for(i=0;i<4;i++)
scanf(“%f”,&sco[i]);
s=sum( p );
printf(“%f”,s);
}
形参是 指针实参是 指针
30
例:编写函数实现求和 4
float sum(float a[4])
{int i;float sum= a [0];
for(i=1;i<4;i++)
sum+= a [i];
return(sum);
}
main()
{int i; float sco[4],s;
float *p=sco;
for(i=0;i<4;i++)
scanf(“%f”,&sco[i]);
s=sum( p );
printf(“%f”,s);
}
形参是 数组名实参是 指针
31
总之,
数组作函数参数有下列四种写法:
但无论哪种形式,本质上,
传递的都是数组的 首地址 。
实参 形参数组名 数组名数组名 指针变量指针变量 数组名指针变量 指针变量
32
小结
这节课讲的是这门课的 重点&难点,
指针做函数参数,结果都能对主调函数中的变量进行处理。
单个指针变量做函数参数
指向数组的指针变量做函数参数
指针与一维数组的关系
如何用数组名和指针变量引用每一个数组元素
数组做参数的四种写法
形式不同,本质相同
下节课讲二维数组以及二维数组做函数参数,请提前预习
33
作业 1
编程证明:哥德巴赫猜想 (所有的正偶数都可以分解为两个素数 )在 1000以内能够成立,并至少给出每个正偶数的一组分解。
要求:下述功能用函数实现,然后在主函数中调用:
/*函数 1:判断一个数是否为素数* /
/*函数 2:将一个正偶数分解为两个素数,m,n指向主函数中的两个变量,这样,*m,*n用来存储分解得到的两个素数。* /
void Goldbach (…,int *m,int *n)
{……
}
34
作业 2
读懂 202-203页例 6-20,例 6-21并用指针编写函数实现计算字符串长度和实现两字符串连接。
35
作业 3
1,写函数实现,删除字符串中的某一字符
x。并在主函数中调用。
要求:写四个程序,每一个程序中,函数的实参和形参格式分别对应下边的一种。
实参 形参数组名 数组名数组名 指针变量指针变量 数组名指针变量 指针变量