内容正文:
第 8 章 指针
2024年8月11日
内容概述
1.指针变量的概念、特点、定义和使用方法。
2.数组与指针的关系,包括一维数组和指针、字符串与指针等。
3.指针变量作函数参数、指向数组的指针作函数的参数。
2024年8月11日
教学目标
1.掌握指针和指针变量的概念,了解指针变量的特点以及直接访问数据和间接访问数据的原理。
2.掌握指针变量的定义、赋值方法及指针运算符的使用,熟练运用指针访问简单变量 。
3.熟悉指针和一维数组的关系,掌握指向一维数组的指针变量的定义方法,熟练使用指针变量访问一维数组元素。
4. 了解指针与字符串的关系,能熟练使用指针处理字符串。
5.熟练掌握用指针变量作函数的参数时函数的定义和调用方法、数组名作函数的参数用法。
2024年8月11日
C语言中,变量是有类型的,而且占据一定长度的内存单元。
通过前面的学习,我们已知道:
例如:
char ch; 变量ch占用1个字节的存储单元
int n; 变量n占用4个字节的存储单元
float x; 变量x占用4个字节的存储单元
double f; 变量f占用8个字节的存储单元
2024年8月11日
既然变量占据一定的内存空间,那么每一个变量都对应一个内存地址,这个地址为无符号整数,它不同于一般的整数。
问题: 能否对地址运算?
能否用一个变量保存地址?
2024年8月11日
8.2 指针运算
8.2.1 赋值运算
1.指针变量的初始化赋值
指针变量在使用前必须赋初值。
可以在定义指针变量的同时对其进行初始化赋值:
int x, *p=&x;
也可以在程序执行时对其进行初始化赋值:
int x,*p;
p=&x;
2024年8月11日
注意:在没赋初值之前,指针变量的内容将是不确定的,即指针没有确定的指向。如果此时引用指针指向的变量,将会产生不可预料的后果。
例如,int *ptr;
*ptr=100; ×
一定要注意哦!
2024年8月11日
2.指针变量间的赋值
在C程序中,可以将一个指针赋给另一个基类型相同的指针,其含义是两个指针在同一时刻指向同一个变量。
例如:
int a,b,*p=&a,*p1=&b;
…
p1=p;
改变了p1的指向
2024年8月11日
【例8.2】演示指针变量赋值的例子。
#include <stdio.h>
main()
{
int a=100,b=200;
int *pa=&a,*pb=&b; /*定义指针变量并赋初值*/
*pa=*pb; /*指针所指变量的赋值*/
printf("*pa=%d, *pb=%d
",*pa,*pb);
b=300;
pa=pb; /*指针变量赋值*/
printf("*pa=%d,*pb=%d
",*pa,*pb);
}
课堂练习:分析下面程序的功能.
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
”,a,b);
printf(“max=%d,min=%d
”,*p1,*p2);
}
&a
7
&b
8
p2
p1
&b
7
&a
8
p2
p1
a
b
b
a
p
2024年8月11日
8.2.2 指针的加减运算
含义:对于地址的运算,只能进行整型数据的加、减运算。
规则:指针变量+n 意味着将指针指向的当前变量向前或后的第n个变量单元。
指针变量算术运算的过程;
p新 =p原值 +n*sizeof(类型)
2024年8月11日
2002
2004
p
(a) P指向2000单元
2012
2002
2004
p
2008
2016
(c)执行p=p+3之后
2002
2004
2008
p
(b) 执行p++之后
2008
2002
2004
2012
2016
p
(d) 执行p--之后
假设有定义“int *p;”,且已知p指向2000单元,则顺序执行“p++;p=p+3;p--;”之后指针的指向如下图所示。
指针变量的算术运算结果是改变指针目标的指向;
2024年8月11日
8.2.3 指针的比较
作用:
用于识别目标变量在内存中的前后位置.
(1)若p和q是指向同一数组的指针,那么,p==q表示p和q指向同一元素;p>q表示p处于高地址位置;p<q表示p处于低地址位置。
(2)不同类型指针之间或指针与一般的整型数据之间的比较是没有意义的。
2024年8月11日
8.3 指针变量作为函数参数
函数的参数不仅可以是基本数据类型,还可以是指针类型。指针做函数参数的实质是将一个变量的地址传送到另一个函数中。相对于值传递方式,这是一种地址传递 方式。
2024年8月11日
8.3.1 在函数中处理主调函数中的变量
【例8.3】调用函数swap()实现两个整型量的内容交换。
swap(int *p1, int *p2)
{
int temp;
temp=*p1;
*p1=*p2;
*p2=temp;
}
2024年8月11日
main()
{ int a=25, b=48;
printf("before swapping:");
printf(“a=%d, b=%d
”, a, b);
swap(&a,&b);
printf("after swapping:");
printf("a=%d, b=%d
", a, b);
}
2024年8月11日
地址传递内存布局
swap
main
y
temp
b
x
a
交换
48
&b
返回地址
&a
25
程序中,形参通过修改地址所指的值来交换数据:
2024年8月11日
(1) 注意函数中temp为普通变量,并非地址量;
(2) 不用地址量作参数,不能实现交换,即:
swap(int x, int y)
{ int temp;
temp=x;
x=y;
y=temp;
}
2024年8月11日
swap
main
y
temp
b
x
a
交换
48
48
返回地址
25
25
栈
值传递内存布局
仅将形参x和y的内容交换,函数返回后,形参x、y释放, 实参a、b中的内容依然未改变。
2024年8月11日
下面的swap函数,虽然用指针做函数参数,也不能达到交换的目的。
swap(int *x, int *y)
{ int *temp;
temp=x;
x=y;
y=temp;
}
2024年8月11日
仅将x、y的指向改变,函数返回后, x、y释放, a、b中的内容依然未改变。
swap
main
y
temp
b
x
a
交换
48
&b
返回地址
&a
25
形参指针变量交换示意
2024年8月11日
如果写成这样就有问题了:
void swap(int *x, int *y)
{
int *temp;
*temp =*x;
*x =*y;
*y =*temp;
}
*temp是指针变量temp所指向的变量,而指针变量temp没有初始赋值,内部是一个随机地址,这种赋值是很危险的,有可能造成系统瘫痪。
2024年8月11日
结论:
1) 指针变量作为参数,从调用函数到被调用函数传递的不是一个变量而是变量的地址;
2)指针变量作为函数的参数,从实参到形参的数据传递仍然遵循“单向值传递”的原则,只是此时传递的是地址。
3)通过函数的调用可以得到n个需改变值的方法:
用指针变量;
用数组名;
用全局变量;
2024年8月11日
8.3.2 利用指针将函数处理结果带回主调函数
【例8.4】编写函数求n!,并在主函数中调用它。
#include "stdio.h"
void fac(int n,long *s)
{ int i;
*s=1;
for(i=1;i<=n;i++)
(*s)*=i; /* *s内存储n! */
}
main()
{ int n;
long m;
scanf("%d",&n);
fac(n,&m); /*调用函数fac(),求n!,结果存入m中*/
printf("%d!=%ld
",n,m);
}
2024年8月11日
综上所述,指针变量作为函数参数的作用有两个:
指针变量作为函数参数可以对主调函数中的变量进行操作,用起来非常灵活。但也要看到,指针的灵活性是以破坏函数的黑盒为代价的。它使函数可以访问本函数栈空间以外的内存区域,以至于使得程序可读性差,跟踪错误的区域从函数的局部栈空间扩大到整个内存空间,增加了调试的复杂性。
在函数中处理主调函数中的变量;
将函数处理结果带回主调函数。
$$