XMUT 2008
厦门理工学院高级语言程序设计教学课件第 7章 指针厦门理工学院计算机科学与技术系
2009-7-31
教 师,吴 芸
E-Mail,ywu@xmut.edu.cn
2008 00:562
7.0 目标
理解指针的概念
定义指针变量
掌握对指针的操作
理解指针和数组的关系
2008 00:563
…...
内存
2000
2001
2002
2005
0
2003
2004
…...
…..,…...
7.1 指针的基本概念
1,内存地址 ──内存中存储单元的编号
101 102
201 202
301 302
401 402
501 502
601 602
教学楼教室号码存储地址教室存储单元教室有容量存储单元有大小 (字节单元、字单元 )
50
存储数据注意,内存单元的地址与内存单元中的数据是两个完全不同的概念。
2008 00:564
内存
…...
…...
0
2000
2001
2002
2005
2003
2004
…...
…...
2、变量地址 ── 系统分配给变量的内存单元的起始地址程序中,int i;
float k;
内存中每个字节有一个编号 -----地址
i
k
编译或函数调用时为其分配内存单元变量 是对程序中数据存储空间的抽象
i的地址
k的地址注意,在 TC或 BC下,系统将给变量 i分配 2字节 的单元,而 VC下将是
4字节 的单元!
2008 00:565
main( )
{
int a=1;
float b=2;
int c[2]={5,6};
char d=’d’;
}
一个程序片段变量 a
变量 b
变量 d
数组 c[0]5
00000000H
00000001H
00000002H
00000003H
00000004H
00000005H
1
00000006H
00000007H
00000008H
00000009H
…...
6
0000000AH
00000009H
‘d’
2
数组 c[1]
2008 00:566
变量名变 量 的地址 存放变量的内存单元
00000000H
00000001H 1
变量的值 (a=1)
局部变量 a的存储示意图注:变量的地址是二进制的,为了便于书写而在这里写成对应的十六进制形式。
int a
2008 00:567
要访问变量首先就要知道变量的地址,可是通过数字形式的地址值访问变量,显然是不方便的
( 正如使用 URL网址,域名比 IP地址要方便 ),
不便于书写和记忆,而且数字本身没有什么具体的字面意义。
需要了解硬件细节。比如当前哪些内存空间是空闲的等等。
失去了高级语言容易使用,接近人类语言的优点 。
解决的办法:
C语言提供了变量名,程序员通过变量名来访问变量
2008 00:568
,指针就是地址” !!!
地址值 (也就是内存单元的编址 )。
是什么类型的数据的地址 。 (这就存在着一个跨度也就是存储空间大小的问题 )。
7.1.2 指针
2008 00:569
明白 指针就是地址,这一点十分重要。
多数情况下,这个地址是指内存中一个变量的起始位臵。
如果一个变量包含了另一个变量的地址,那么第 1个变量就是个指针变量,而且说它是“指向”
第 2个变量的,“指针”由此而得其名。
例如,如果在地址为 1000的变量指向地址为
1004的变量,那么也就是说地址为 1000的这个变量的值是 1004。
7.1.3 指针其名
2008 00:5610
为什么要表达为,指向,呢?下一节中将会看到如果变量 p的值是变量 a的地址,则可以 利用变量 p来访问和操作变量 a(其实这是很自然的事情,有了某变量的地址当然就可以访问该变量)。
所以这样的 变量 p和 a之间是有某种联系的,这种联系就被表达为“指向” 。
图 7-3解释了这一点,它仅仅用来对地址进行偏移。
7.1 指针的基本概念
2008 00:5611
10041000
1001
1002
1003
121004
图 7-3 一个变量指向另一个变量内存单元内存地址
2008 00:5612
内存
10
int x
ED53地址 (ptr_x)
变量数据
ED53
int *ptr_x
指针指针 ptr_x 指向变量 x
2008 00:5613
1,变量指针(变量的指针)
一个变量 x的地址就是该变量的指针,记作
&x,即在变量名前加上取地址运算符,&” 。
2,指针变量
专门用来存放地址的变量称为指针变量。
当指针变量中存放着某一个变量的地址时,
就称这个指针变量指向那一个变量。
由于 地址或指针是常量,因此当我们需要对地址进行操作的时候一般要用指针变量来保存该地址再做处理。
7.1.4 变量的指针与指针的变量
2008 00:5614
整型变量 i
变量 i_pointer
…...
…...
102000
2004
2006
2005
2001
2002
2003
变量指针与指针变量
变量指针,一个变量的地址
指针变量,专门存放变量地址的变量
2000
指针变量整型变量 i的内容指针变量 i_pointer的内容
(是地址 )
变量的地址指针指针变量变量变量地址 (指针 )
变量值指向 地址存入指针变量
2008 00:5615
指针变量也是一个变量,具有变量的特征,在内存中也占用一定的存储单元,也有“地址”和
“值”的概念。
只不过指针变量中存储的是另一个变量的内存地址;但指针变量的“值”不同于一般变量的
“值”,指针变量的“值”是另一实体(变量、
数组或函数等)的地址。
常量是没有地址的,所以指针变量只能指向变量
3、指针变量和一般变量的关系
2008 00:5616
由于指针值是数据,指针变量可以赋值,所以一个指针的指向在程序执行中可以改变。指针 p 在执行中某时刻指向变量 x,在另一时刻也可以指向变量 y。
指针变量 px与它所指向的整型变量 x的关系
用指针运算符,*”表示为,*px (等价于变量 x)
3
变量 x
2000
px
*px
x *px
&x px
x=3; *px=3;
2008 00:5617
下面四条语句的作用相同,都是将 100赋给变量 x:
1,x=100; /*将 100直接赋给变量 x*/
2,*px=100; /*将 100间接赋给变量 x*/
3,*(&x)=100; /*将 100间接赋给变量 x*/
4,*((int *)100h)=100 /*将 100间接赋给变量 x*/
(假设 x的地址为 100h)
2008 00:5618
指针变量的长度可以是 2个字节或 4个字节,
这取决于引用者和被引用者之间在内存的距离,
通常由系统自动决定,程序员不必理会。 (下述内容在学习汇编语言后更好理解,目前可暂不关注。)
编译系统根据设定的内存模式来安排代码段和数据段,由此确定指针变量的长度。内存模式取决于代码段和数据段的长度,早期的 C系统将内存模式分为六种,如 P202 表 7-1所示。
4、指针变量的长度
2008 00:5619
一般形式,[存储类型 ] 数据类型符 *变量名 [=初始值 ];
合法标识符表示定义指针变量不是 ‘ *’ 运算符指针的目标变量的数据类型指针变量本身的存储类型注意:
int *p1,*p2; 与 int *p1,p2;
指针变量名是 p1,p2,不是 *p1,*p2
指针变量只能指向定义时所规定类型的变量
指针变量定义后,变量值不确定,应用前必须先赋值例 int *p1,*p2;
float *q;
static char *name;
7.2 指针变量的定义与赋值
2008 00:5620
例如,
int a=1 /*定义整型变量 */
int *p; /*定义指向整型变量的指针变量 */
p=&a; /*把 int变量 a的首地址赋予该指针变量,
对指针的另一个含义“地址值”进行补充。 */
7.2.1 指针变量的定义上面的三个语句也可以简化为
int a=1;
int *p=&a;
2008 00:5621
图 7-4 变量在内存中的存储分配程序片段
int a=10;
……,.
……,.(定义别的变量 )
int * p;
p=&a;
········
00000000H
00000001H
········
00000006H
00000005H
········
00000065H
········
40000000H
00000064H
········
········
a
········
········
········
P
········
········
10
00000005H
········
········
········
········
变量名 变量地址 变量值
2008 00:5622
例 int i;
int *p = &i;
例 int *p = &i;
int i;
7.2.2 指针变量的赋值
初始化赋值
[存储类型 ] 数据类型 *指针名 = 初始地址值 ;
变量必须已说明过类型应一致例 int i;
int *p1 = &i;
int *p2 = p1;
用已初始化指针变量作初值例 int i,*pi = &i;
float *pf;
pf=pi;
2008 00:5623
赋值语句赋值例
int a = 20 ;
int *p,*q;
p = &a;
q = p;
整型变量 a
指针变量 p
指针变量 q

…...
2000
…...
…..,20
2000
2000
2008 00:5624
例 int *p = &a;
int a;
指针变量赋值的几种错误方法,
变量 a的定义在后,对 a
的引用超出了 a的作用域例 int a;
int *pi = &a;
char *pc = &a;
pc不能指向非字符型变量例 int a;
int *p;
*p = &a;
赋值语句中,被赋值的指针变量 p的前面不能再加,*” 说明符例 int *p;
p = 2000;
不允许直接把一个数赋值给指针变量例 int a;
static int *p = &a;
不能用 auto变量的地址去初始化 static型指针注意,一个指针变量只能指向同类型的变量如果给指针赋值时,=号右边的指针类型与左边的指针类型不同,则需要进行类型强制转换。
int a;
int *pi;
char *pc;
pi = &a; //pi指向 a
pc = (char *)pi; //pc指向 a,即 pi和 pc的值都是 a
的地址
2008 00:5625
零指针与空类型指针
零指针:
定义,指针变量值为零
表示,int * p = 0;
p指向地址为 0的单元,
系统保证该单元不作它用表示指针变量值 没有意义
#define NULL 0
int *p = NULL;
p = NULL与未对 p赋值不同
用途,
避免指针变量的非法引用
在程序中常作为 状态 比较例 int *p;
......
while (p != NULL)
{,..…
}
void *类型指针
表示,void *p;
使用时要进行 强制类型转换表示不指定 p是指向哪一种类型数据的指针变 量例 char *p1;
void *p2;
p1=(char *)p2;
p2=(void *)p1;
2008 00:5626
int x=100,*ptr_x,*ptr_y;
ptr_x=&x;
ptr_y = ptr_x;
指针赋值运算
100
x
FF7C
ptr_x
ptr_y
FF7C
FF7C
实例注意,严格区分指针值和指针所指向变量的值
2008 00:5627
int a,*p;
p = &a;
内存
100
a
FF7C
p
指针FF7C
7.3 指针变量的使用
2008 00:5628
int a,*p;
p = &a;
*p=10;
内存
100
a
FF7C
p
指针FF7C10
7.3 指针变量的使用
printf(“a=%d”,a);
printf(“a=%d”,*p);
分别直接和间接方式输出变量 a的值,
因此,输出结果都是 10。
2008 00:5629
ptr_x的值,即 x
的地址
&x,取 x的地址赋给 ptr_x
ptr_x所指向变量,即 x的值
*ptr_x,ptr_x所指向变量的值
&与 *的区别
2008 00:5630
直接访问:按变量名来存取变量值
间接访问:通过存放变量地址的变量去访问变量例 i = 3; -----直接访问指针变量
…...
…...
2000
2004
2006
2005
整型变量 i
10
变量 i_pointer
2001
2002
2003
2000
3
例 *i_pointer = 20; -----间接访问
20
7.3.2 变量值的存取方法
2008 00:5631
指针变量
…...
…...
2000
2004
2006
2005
整型变量 i
10
变量 i_pointer
2001
2002
2003
2000
整型变量 k
10
例 k = i;
k = *i_pointer;
--直接访问
--间接访问
2008 00:5632
指针变量的命名规则和其他变量的命名规则一样 ;
指针变量不能与现有变量同名 ;
指针变量可存放 C 语言中的任何基本数据类型、数组和其他所有高级数据结构的地址 ;
若指针变量已声明为指向某种类型数据的地址,则它不能用于存储其他类型数据的地址 ;
应为指针变量指定一个地址后,才能在语句中使用指针,
指针变量的特点
2008 00:5633
【 例 1】 不同类型的指针操作同一内存变量
#include <stdio.h>
void main ( )
{
unsigned short a;
unsigned short *pi = &a;
char *pc = (char *)&a;
*pi = 0XF0F0;
*pc = 0;
printf ("a = %X",a);
}

…...
2000
…...
…...
整型变量 a
指针变量 pi
2000
pi可操作单元指针变量 pc
2000
pc可操作单元
F0
F0
00
输出结果:
a = F000
2008 00:5634
【 例 2】 输入两个数,并使其从大到小输出
#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",a,b);
printf ("max = %d,min = %d\n",
*p1,*p2);
}
运行结果:
a=5,b=9
max=9,min=5
…...
…...
指针变量 p1
指针变量 p
2000
2008
2002
2004
2006
指针变量 p2
整型变量 b
整型变量 a5
2006
9
2008
2006
2008
2006
重点强调:
指针变量必须 先定义,后赋值,最后才能使用 !
没有赋值的指针变量是没有任何意义的,也绝对是不允许使用的。
指针变量只能指向定义时所规定类型的变量。
2008 00:5635
1、指针变量的算术运算(加、减)
指针可以参与加法和减法运算,但其加、减的含义绝对不同于一般数值的加减运算。如果指针 p是这样定义的:
ptype *p;,并且 p当前的值是 ADDR,那么:
p ± n 的值 = ADDR ± n * sizeof( ptype)
int *pi;
char *pc;
long *pl;
pi = (int *) 1000;
pc = (char *) 1000;
pl = (long *) 1000;
pi++; //pi的值将是 1002 (假设 int型占 2byte)
pi -= 2; // pi的值将是 998
pc++; // pc的值将是 1001
pc -= 2; // pc的值将是 999
pl++; // pl的值将是 1004
pl -= 2; // pi的值将是 996
注意,两个指针相加没有任何意义,但两个指针相减则有一定的意义,可表示两指针之间所相差的内存单元数或元素的个数,在后面的学习中就会体会到。
指针的算术运算
2008 00:5636
int *ptrnum,arr_num[8];
ptrnum = &arr_num[0];
ptrnum++;
使用递增 /递减运算符( ++ 和 --)将指针递增或递减 内存
arr_num[0]
arr_num[1]
arr_num[2]
arr_num[3]
arr_num[4]
arr_num[5]
arr_num[6]
arr_num[7]
10
23
15
60
41
49
13
39一个类型为 T 的指针的移动,以 sizeof(T)为移动单位。
应用(一)
2008 00:5637
将指针加上或者减去某个整数值
ptrnum = &arr_num[0];
ptrnum = ptrnum + 4;
printf(“%d”,*ptrnum);
ptrnum = &arr_num[5];
ptrnum = ptrnum - 2 ;
printf(“%d”,*ptrnum);
41
60
内存
arr_num[0]
arr_num[1]
arr_num[2]
arr_num[3]
arr_num[4]
arr_num[5]
arr_num[6]
arr_num[7]
10
23
15
60
41
49
13
39
应用(二)
2008 00:5638
2、指针变量的关系运算
若 p1和 p2指向同一数组,则
p1<p2 表示 p1指的元素在前
p1>p2 表示 p1指的元素在后
p1==p2 表示 p1与 p2指向同一元素
若 p1与 p2不指向同一数组,比较无意义
p==NULL或 p!=NULL
2008 00:5639
比较两个指针
#include<stdio.h>
void main ()
{
int *ptrnum1,*ptrnum2;
int value = 1;
ptrnum1 = &value;
value = value +10;
ptrnum2 = &value;
if (ptrnum1 == ptrnum2)
printf("\n 两个指针指向同一个地址 \n");
else
printf("\n 两个指针指向不同的地址 \n");
}
应用 思考,如何比较指针所指向的两个值?
2008 00:5640
C语言中函数参数的传递方式是,值传递,。 指针变量作为函数的实参时,由于指针变量的值实际上是它所指向的变量的地址,所以传递的是指针所指向的变量的地址,与此相对应函数的形参也应是指针变量 。
前面的程序例 6-18中的 change函数并不能交换 a和
b的值,下面我们先通过一个程序看看使用指针变量来间接访问变量能带来什么好处 。
7.3.4 指针变量作为函数参数
2008 00:5641
指针变量作为函数的参数
参数传递方式,值传递 和 地址传递
值传递,将参数值传递给形参。实参和形参占用各自的内存单元,互不干扰,函数中对形参值得改变不会改变实参的值,属于 单向数据传递方式 。
地址传递,将实参的地址传递给形参。形参和实参占用同样的内存单元,对形参值得改变也会改变实参的值,属于双向数据传递方式 。
void func (int a)
{
a = 5;
}
void main ( )
{
int b = 0;
func (b);
printf ("b = %d\n",b);
}
值传递
void func (int *p)
{
*p = 5;
}
void main ( )
{
int b = 0;
func (&b);
printf ("b = %d\n",b);
}
地址传递运行结果,b = 0 运行结果,b = 5
为什么结果不一样呢?
0
变量 b 形参 a
50
变量 b 指针 p
5
&b 0 &b
2008 00:5642
例 7-1 编制函数 change,交换两个变量的值。
void change(int *m,int *n)
{
int temp;
temp=*m;
*m=*n;
*n=temp;
}
void main()
{
int a=1,b=2;
change(&a,&b);
printf("a=%d,b=%d",a,b);
}
运行结果:
a=2,b=1
2008 00:5643
图 7-6程序的内存示意图
100 102
100 102
2 1
释放
m
a
a b1 2
100 102
n
204 206
temp 随机值
208
b
100 102
m n100 102
204 206
temp 1
208指向地址值
temp=*m;
*m=*n;
*n=temp;
a b2
100 102
m n
204 206
temp 2
208
释放 释放
change返回调用 change(&a,&b);
int a=1,b=2;
1
2008 00:5644
1、数组的指针、数组元素的指针
7.4 指针与数组数组的指针 其实就是 数组在内存中的起始地址 。
而数组在内存中的起始地址就是数组变量名,也就是数组第一个元素在内存中的地址。
a
2000
2002
2004
2018
…..,…...
a[0]
a[1]
a[2]
a[9]
a
a+1
a+2
a+9
……
例,int a[10]; int a[10];
int k;
for (k = 0; k < 10; k++)
a[k] = k; //利用数组下标
int a[10];
int k;
for (k = 0; k < 10; k++)
*(a+k) = k; //利用数组的指针注意,a+k &a[k] *(a+k) a[k]
2008 00:5645
2、指向数组元素的指针变量如果将数组的起始地址赋给某个指针变量,那么该指针变量就是 指向数组的指针变量 。
例,short int a[10],*p = a;*p=&a[0]
p
2000
2002
2004
2018
…...
a
a+1
a+2
a+9
………...
a[0]
a[1]
a[2]
a[9]
2000
p
p+1
p+2
p+9
……
*a
*(a+1)
*(a+2)
*(a+9)
……
*p
*(p+1)
*(p+2)
*(p+9)
……
p[0]
p[1]
p[2]
p[9]
……
地址 元素
2008 00:5646
char str[10];
int k;
for (k = 0; k < 10; k++)
str[k] = 'A' + k; //也可写成 *(str+k) = 'A' + k
char str[10];
int k;
char *p;
p = str;
for (k = 0; k < 10; k++)
p[k] = 'A' + k; //也可写成 *(p+k) = 'A' + k
char str[10];
int k;
char *p;
p = str;
for (k = 0; k < 10; k++)
*p++ = 'A' + k; //相当于 *p = 'A' + k; p++;
下面是对数组元素赋值的几种方法,它们从功能上是等价的执行完后,p仍然指向数组 str的首地址执行完后,p指向数组元素 str[9]的下一内存单元注意,数组名是地址常量,切不可对其赋值,也不可做 ++或 --运算。例如:
int a[10];如果在程序中出现 a++或 a--则是错误的。
2008 00:5647
a[0]
a[1]
a[2]
a[3]
a[4]
【 例 】 数组元素的引用方法
void main( )
{
int a[5],*pa,i;
for (i = 0; i < 5; i++)
a[i]=i+1;
pa = a;
for (i = 0; i < 5; i++)
printf ("*(pa+%d):%d\n",i,*(pa+i));
for (i = 0; i < 5; i++)
printf ("*(a+%d):%d\n",i,*(a+i));
for (i = 0; i < 5; i++)
printf ("pa[%d]:%d\n",i,pa[i]);
for (i = 0; i < 5; i++)
printf("a[%d]:%d\n",i,a[i]);
}
1
2
3
4
5
pa
2008 00:5648
例,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) (?)
例,注意指针变量的运算
void main()
{
int a[ ] = {5,8,7,6,2,7,3};
int y,*p = &a[1];
y = (*--p)++;
printf (“%d,,y);
printf (“%d”,a[0]);
}
5
8
7
6
2
7
3
0
1
2
3
4
5
6
a
p
p 6
输出结果:
5 6
2008 00:5649
void 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);
}
例 注意指针的当前值
p=a;
p
p 5
8
7
6
2
7
3
0
1
2
3
4
5
6
a
p
p
p
p
p
p
指针变量可以指到 数组后 的内存单元
2008 00:5650
(1)指针变量可以通过本身值的改变 (如 p++)
来指向数组中的不同元素。但是数组名是地址常量不能改变本身的值。如:
int data[5],*p;
p=data;
如果写 data++则是错误的;
3、指向数组元素的指针变量时应注意
2008 00:5651
*p++等价 于 *(p++),作用是先得到 p所指向的变量的值(即 *p),然后再使 p
加 1。
*(p++)与 *(++p)不同,前者是先取得 *p
的值,后使 p加 1;后者是先使 p加 1,再取 *p的值。若 p初值为 &data[0],输出
*(p++)时,得 data[0]的值,而输出
*(++p),则得到 data[1]的值。
(2) 应注意下面的几种指针运算形式:
2008 00:5652
小结如果 p当前指向 data数组中第 i个元素,则:
*(p++)相当于 data[i++],先对 p进行,*”运算,再使 p自增。
*(p--)相当于 data[i--],先对 p进行,*”运算,再使 p自减。
*(++p)相当于 data[++i],先使 p自增,再对 p进行,*”运算。
*(--p)相当于 data[--i],先使 p自减,再对 p进行,*”运算。
2008 00:5653
(3) (*p)++ 表示 p所指向的元素值加 1,注意是元素值加 1,而不是指针值加 1。
比如,如果 p所指向的元素为 data[3],且 data[3]
的值为 9,则 (*p)++表示将 data[3]单元中的值加 1,
变成 10,而 p仍指向元素 data[3],也就是说,p
中的地址值并没有改变。
(4) p+n 和 p-n:将指针从当前位臵前进或回退
n个元素,而不是 n个字节。显然,p++,p--(或 +
+p,--p)是 p+n或 p-n 的特例(当 n=1)。
2008 00:5654
(5) p2-p1表示两指针之间的数组元素个数,
而不是指针的地址之差,即图 7-8(课本上)中,
p2-p1为 4。
(6)两指针之间可以进行关系运算,如果 p1指向 data[i],p2指向 data[j],并且 i<j,则 p1<p2为
“真”,反之亦然,即图 7-8中,p1<p2为真。
2008 00:5655
图 7-8 指针算术、关系运算
data[0]p1
data[1]
data[2]
data[3]
data[4]
data[5]
p2
data数组
1002
1010
2008 00:5656
例 7-2 用下标法和指针法引用数组元素 。
#include <stdio.h>
main()
{
int data[6]={0,3,6,9,12,15};
int *p=data,i;
for(i=0; i<6; i++)
printf(i==5?"%d\n":"%d ",data[i]); /*数组名下标法 */
for(i=0;i<6;i++)
printf(i==5?"%d\n":"%d ",*(data+i)); /*数组名指针法 */
for(i=0;i<6;i++)
printf(i==5?"%d\n":"%d ",p[i]); /*指针变量下标法 */
for(i=0;i<6;i++)
printf(i==5?"%d\n":"%d ",*(p+i)); /*指针变量指针法 */
}
运行结果为:
0 3 6 9 12 15
0 3 6 9 12 15
0 3 6 9 12 15
0 3 6 9 12 15
2008 00:5657
当数组名作为函数参数时,在函数调用时,
实际传递给函数的是该数组的起始地址,即指针值。
调用函数的实参可以是数组名或指向数组的指针变量。
被调用函数的形参可以说明为数组也可以说明为指针。
7.4.4 数组名作为函数参数
2008 00:5658
(1)实参和形参都用数组名
void data_put( int str[ ],int n)
{
int i;
for(i=0; i<n; i++)
printf("\n %d",str[i]);
}
main()
{
int a[6]={1,2,3,4,5,6};
data_put( a,6);
}
(2)实参用数组名,形参用指针
void data_put( int *str,int n)
{
int i;
for(i=0; i<n; i++)
printf("\n %d",*(str+i));
}
main()
{
int a[6]={1,2,3,4,5,6};
data_put( a,6);
}
7.4 指针与数组
2008 00:5659
(3)实参用指针,形参用数组名 利用下标引用指针变量所指数组元素
void data_put( int str[ ],int n)
{
int i;
for(i=0; i<n; i++)
printf("\n %d",str[i]);
}
main()
{
int a[6]={1,2,3,4,5,6};
data_put( p,6);
}
(4)实参和形参都用指针
void data_put( int *str,int n)
{
int i;
for(i=0; i<n; i++)
printf("\n %d",*(str+i));
}
main()
{
int a[6]={1,2,3,4,5,6};
int *p=a;
data_put( p,6);
}
7.4 指针与数组
2008 00:5660
7.4.5 指向字符串的指针变量
1,字符串表示形式和引用
用字符数组实现例,
void main ( )
{
char string[] =,I love China!”;
printf (“%s \n”,string);
printf (“%s \n”,string + 7);
}
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
运行结果:
I love China!
China!
2008 00:5661
例题:用字符数组存放一个字符串
#include <stdio.h>
void main()
{
char string[ ]="This is a string"; /*字符数组存放字符串 */
printf(“\n %s”,string); /*整体引用输出 */
printf("\n");
for(i=0; *(string+i)!=?\0?; i++) /*逐个引用 */
printf("%c",*(string+i));
}
程序的执行结果如下:
This is a string
This is a string
2008 00:5662
用字符指针实现例:
void main ( )
{
char *string =,I love China!”;
printf (“%s \n”,string);
string += 7;
while (*string)
{
putchar (string[0]);
string++;
}
}
I
l
o
v
e
C
h
i
string
n
!
a
\0
字符指针 初始化,把字符串 首地址 赋给 string
char *string;
string =,I love China!”;
string
*string != 0
运行结果:
I love China!
China!
整体引用逐个字符引用
2008 00:5663
例 7-3:用字符指针指向一个字符串
#include <stdio.h>
void main()
{
int i;
char *p="This is a string"; /*字符数组存放字符串 */
printf("%s \n",p); /*整体引用输出 */
for(i=0;p[i]!='\0'; i++) /*下标法,逐个引用 */
printf(“%c”,p[i]); printf(" \n");
for(; *p!=?\0?; p++) /*指针移动法,逐个引用 */
printf("%c",*p);
}
程序的执行结果如下:
This is a string
This is a string
This is a string
说明:
语句,char *p=”This is a string”;
等价于下面两行:
char *p;
p=,This is a string”;
2008 00:5664
C语言对字符串常量是按字符数组处理的,在定义字符串常量,This is a string” 时,在内存开辟了一个字符数组来存放它,并把首地址赋给字符指针 p,如图 7-9所示。
T h i s i s a s t r i n g \0
图 7-9字符串常量在内存中的存放
p
2008 00:5665
例 7_4用指针方法,求字符串长度。
main()
{
char *p,str[80];
int n;
printf("输入字符串,\n");
gets(str);
p=str;
while(*p!='\0')p++;
n=p-str;
printf("字符串,%s的长度 = %d \n",str,n);
}
运行情况:
输入字符串:
abcd ↙
字符串 abcd的长度 =4
2008 00:5666
字符指针变量使用注意事项当字符指针指向字符串时,除了可以被赋值之外,与包含字符串的字符数组没有什么区别。
char str[10],*pstr;
pstr = "12345"; //pstr指向 "12345"
strcpy (str,pstr); //将 pstr所指向的字符串复制到数组 str中
pstr = str;
printf ("The Length of str is,%d\n",strlen(pstr)); //输出字符串的长度 5
注意“野指针”操作:
如果一个指针没有指向一个有效内存就被引用,则被称为
,野指针,操作或 空指针赋值 。野指针操作尽管编译时不会出错,但很容易引起程序运行时表现异常,甚至导致系统崩溃。
char *pstr;
char str[8];
scanf ("%s",pstr); //野指针操作,pstr没有指向有效内存
strcpy (pstr,"hello"); //野指针操作
pstr = str; //pstr指向数组 str所对应内存单元的首地址
strcpy (pstr,"0123456789"); //不是野指针,但会造成数组越界为什么“野指针”
操作会给程序运行带来极大的不确定性,甚至造成系统崩溃呢?
2008 00:5667
系统程序区
0001
pstr 0001
指针 pstr所占内存
char *pstr;
pstr =?a?;
随机值,有可能指向系统程序区假设首地址为 0001
此时对 pstr指向的内存单元赋值极其危险!
为什么,野指针,赋值会给程序运行带来极大的危险?
再次提醒:
指针变量只有与内存建立联系以后才可使用,否则将造成程序运行异常,甚至导致系统死机!
2008 00:5668
2.字符串指针作函数参数当数组名作为函数参数时,在函数调用时,实际传递给函数的是该数组的起始地址,即指针值。这样,在函数形参说明中,就可以将数组形参说明为指针,并可以在函数体中通过指针存取或改变数组中的元素。
例 7_5 编写一个合并两个字符串的函数下面给出三种方法。
2008 00:5669
(1)将形参说明为数组,利用下标引用数组元素
void mystrcat ( char str1[ ],char str2[ ] )
{
int i=0,j=0;
while(str1[i]!='\0')i++; /*找到字符串 str1的结束符 */
while(str2[j]!='\0')
{str1[i]=str2[j]; i++; j++;} /*把字符串 str2的内容复制到字符串 str1中 */
str1[i]='\0'; /*添加字符串 str1的结束符 */
}
2008 00:5670
(2)将形参说明为指针,利用指针引用数组元素
void mystrcat(char *str1,char *str2)
{
while(*str1!='\0')str1++;
while(*str2!='\0')
{*str1=*str2; str1++; str2++;}
*str1='\0';
}
2008 00:5671
(3)将形参说明为指针,利用下标引用指针变量所指数组元素
void mystrcat(char *str1,char *str2)
{ int i=0,j=0;
while(str1[i]!='\0')i++; /*找到字符串 str1的结束符 */
while(str2[j]!='\0')
{ /*把字符串 str2的内容复制到字符串 str1中 */
str1[i]=str2[j];
i++;
j++;
}
str1[i]='\0'; /*添加字符串 str1的结束符 */
}
2008 00:5672
#include "stdio.h"
main()
{
char source[80]="String One",object[20]="string two";
mystrcat(source,object);
printf("str1+str2=%s",s ource);
}
程序运行结果是:
str1+str2=String One string two
2008 00:5673
7.4.6 指针数组
1,指针数组的定义与应用如果一个数组的每个元素都是指针类型的数据,则这种数组称为指针数组。指针数组定义的一般形式为:
类型标识符 *数组名 [常量表达式 ];
例如:
char *p[10];
表示 p是一个指针数组,包括 10个元素,每个元素都是字符型指针。
2008 00:5674
如:
char *name[ ]={“zhang shan”,“Li shi,,
“Wangwu”};
由初始表中的初值个数可以看出,name[ ]指针数组中共有 3个元素,每个元素都是一个指针,如图 7-10所示。
Zhang shan
Li shi
Wnagsu
图 7-10 指针数组
name[0]
name[1]
name[2]
在定义指针数组的同时也可以为其初始化
2008 00:5675
其中 name[0]指向字符串,Zhan shan”,name[1]指向字符串,Li shi”,name[2]指向字符串,Wangwu” 。
因此,语句:
printf(“%s,%s,%s \n”,name[0],name[1],name[2]);
将显示字符串:
Zhan shan,Li shi,Wangwu
在程序设计中,经常使用指针数组显示菜单信息。下面的例子就是这方面的实际应用。
2008 00:5676
例 7-6 利用指针数组显示菜单信息 File Edit Search Option
/*7_6.c*/
#include "stdio.h"
main()
{
char *menu[]={"File","Edit","Search","Option"};
int i;
for(i=0; i<4; i++) printf("%s ",menu[i]);
}
说明:如果利用 C语言库函数中的光标定位函数,
则上述菜单信息可以显示在屏幕的任意位置上,
有关这方面的内容,请查阅图形和用户界面方面的技术 。
运行结果:
File Edit Search Option
2008 00:5677
void main()
{
char *names[] = { "Apple",
"Banana",
"Pineapple",
"Peach",
"Strawberry",
"Grapes" };
char *temp;
printf(“\n %s %s",names[2],names[3]);
temp = names[2];
names[2] = names[3];
names[3] = temp;
printf("\n %s %s",names[2],names[3]);
printf("\n");
}
Apple\0
Banana\0
Peach\0
Pineapple\0
names
Strawberry\0
Grapes\0
temp
Pineapple Peach
Peach Pineapple
5548
5562
5583
55A8
55B3
55C9
5583
55A8
5583
例题
2008 00:5678
指针数组的一个重要应用就是作为 main( )函数的形参,在前面的程序中,main( )函数是无参函数。实际上,main( )可带两个参数,其一般形式为:
main( int argc,char *argv[ ] )
/* 这里是形参罢了,名字是可以随便取的,比如
int x,char *y[ ] */
2.指针数组作为 main( )函数的形参
2008 00:5679
第一个参数是整型,第二个参数是作为指向字符的指针数组来处理的,那么这两个参数如何得到具体的值呢? 我们知道,在 DOS命令提示符下,可以键入一个可执行文件名,
还可以带参数 。 如 DOS命令,copy oldfile newfile,将实现
oldfile复制成 newfile的功能 。 假如 copy是用 C语言实现的,则相应的 argc表示是参数的个数 ( 含可执行程序名 ),故 argc
的值为 3,而指针数组 argv中的元素 argv[0],argv[1],argv[2]分别指向三个字符串,copy”,,oldfile” 和,newfile”,如图 7-11所示 。
2008 00:5680
copy\0
wldfile\0
newfile\0
图 7-11 指针数组的应用:命令行参数
argv[0]
argv[1]
argv[2]
argv
2008 00:5681
/*echo.c*/
#include <stdio.h>
void main(int argc,char *argv[ ])
{
int i;
for(i=1; i<argc; i++) printf("%s",argv[i]);
printf("\n");
}
例如,下面程序 echo.c将其所带的参数显示出来,如在
DOS状态下键入 echo oldfile newfile,将回显 oldfile newfile。
注意,若参数(含执行文件名)共有 n个,则最后一个参数由指针 argv[ ]指向。
2008 00:5682
#include <stdio.h>
void User(void)
{
·
·
· /*提示用户正确的操作信息语句 */
}
例 7-7 编写带有帮助说明的程序,也就是要求当输入执行文件名,后跟,/?” 时,将提示命令行的操作方法。
void main(int argc,char *argv[])
{
int i;
if(argc==2)
if(strcmp(argv[1],"/?")==0)
{user(); return;}
·
·
· /*其他代码 */
}
2008 00:5683
1,二维数组和数组元素的地址
C语言的二维数组由若干个一维数组构成,
即二维数组的每一个元素是一个一维数组。
例如定义以下二维数组:
int a[3][4]={{1,3,5,7,},{ 9,10,11,13,15},
{17,19,21,23,}};
7.4.7指针与二维数组
2008 00:5684
a是一个数组名。 a数组包含 3行,即 3个元素,a[0],a[1]、
a[2]。而每一个元素又是一个一维数组,每个一维数组又包含
4个元素 (即 4个列元素 ),例如,a[0]所代表的一维数组又包含 4
个元素,a[0][0],a[0][1],a[0][2],a[0][3],见图 7-12。可以认为二维数组是“数组的数组”,即二维数组 a是由 3个一维数组所组成的。
a[0]
a[1]
a[2]
1
9
17
3
11
19
5
13
21
7
15
23
图 7-12 二维数组是,数组的数组,
7.4 指针与数组
2008 00:5685
从二维数组的角度来看,a代表二维数组首元素的地址,
现在的首元素不是一个简单的整型元素,而是由 4个整型元素所组成的一维数组,因此 a代表的是首行 (即第 0行 )的首地址。
a+1代表第 1行的首地址。如果二维数组的首行的首地址为
2000,则在 Turbo C中,a+1为 2008,因为第 0行有 4个整型数据,
因此 a+1的含义是 a[1]的地址,即 a+4× 2=2008。 a+2代表 a[2]的首地址,它的值是 2016,见图 7-13。
a数组a
(2000)
a+1
a+2
(2008)
(2016)
a[0]
a[1]
a[2]
图 7-13 二维数组各行
2008 00:5686
a
a+1
a+2
图 7-14 二维数组各元素
2000
1
2002
3
2004
5
2008
9
2010
11
2012
13
2016
17
2018
19
2020
21
2006
7
2014
15
2022
23
a[0] a[0]+1 a[0]+2 a[0]+3
2008 00:5687
2.指向二维数组及其元素的指针变量在了解上面的概念后,可以用指针变量指向二维数组或二维数组的元素 。
2008 00:5688
例 7-8用指针变量输出二维数组元素的值 。
/*7-8.c*/
#include <stdio.h>
void main()
{
int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int *p=a;
int i;
for(i=0;i<=11;i++)
{
if(i%4==0) printf(,\n”);
printf("%4d",*(p+i));
}
printf("\n");
}
程序运行结果是:
1 3 5 7
9 11 13 15
17 19 21 23
说明:这个 p是指向数组元素的指针变量,
数组元素是的类型是 int,因此 p的定义形式是 int *p=a;而且 p+1是向后移动 2个字节。
2008 00:5689
指向数组的指针变量的两个属性分别是:
其值是数组的首地址,亦即下标为 0的数组元素的地址。
它是指向数组的,而不是数组元素。因此它的跨度是整个数组而不是一个数组元素。
定义格式指向一维数组的指针变量
[存储类型 ] 数据类型 ( * 指针名) [常量表达式 ];
2008 00:5690
int (*p)[6];
/*定义 p为指向具有 6个元素的整型数组的指针变量 */
int (*data)[5][6];
/*定义 data为包含 5*6个元素的整型数组的指针变量 */
p=data[2];
p++; /*思考:执行这个语句后 p的指向? */
p是指向一个包含 6个元素的一维数组。 p的值就是该一维数组的起始地址(亦即下标为 0的数组元素的地址),p不能指向一维数组中的某一元素。指向一维数组的指针变量一般用于对二维数组或多维数组的操作,请务必主意指针变量的类型。
[存储类型 ] 数据类型 ( * 指针名) [常量表达式 ];
2008 00:5691
例 7-9用指向一维数组的指针变量输出二维数组元素的值 。
#include <stdio.h>
void main()
{
int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int i=0,j=0;
int (*p)[4]=a;
for(i=0;i<3;i++)
for(j=0;j<4;j++)
{
printf("%4d",*(*(p+i)+j)); /*这里用 p[i][j]也可以 */
}
printf("\n");
}
程序运行结果是:
1 3 5 7
9 11 13 15
17 19 21 23说明,请注意这里变量 p的定义,int (*p)[4]=a;”,由于 p
先和运算符,*” 结合,因此 p是指向数组的指针变量 。 而如果是,int *p[4]” 则由于 p先与方括号结合,则 p是个包含 4个某种元素的数组,然后再与 *结合,表示 p是由两个指针构成的数组,而且指针是指向 int类型的数据的,这样的 p就成了一个指针数组了 。 要区分这两种形式,避免混淆 。
2008 00:5692
指向指针变量的指针变量 又简称为 指向指针的指针变量 。
指向指针的指针变量定义的形式为:
类型名 **指针变量明;
此处,指针变量名是“指向指针的指针变量”的变量名,
类型名是该指针变量经过二级间址后所存取变量的数据类型。
由于运算符,*”的结合性是“从右到左”,因此,**指针变量名”等价于,*(*指针变量名 )”,表示该指针变量的值存放的是另一个指针变量的地址,要经过两次间接存取后才能存取到变量的值。例如语句:
char **p;
定义 p为指向指针的指针变量,它要经过两次间接存取后才能存取到变量的值,该变量的数据类型为 char。
7.5 指向指针的指针
2008 00:5693
通过指针变量存取变量的值地址 值指针变量 变量
(a)一级间址地址 1 地址 2
指针变量 1 指针变量 2
(b) 二级间址值变量地址 n 值变量指针变量 n…指针变量 指针变量 2
地址 1 地址 2
(c) n级间址
7,5 指向指针的指针
2008 00:5694
1.指向一个指针变量,间接存取变量的值可以把一个指针变量的地址赋给指向指针的指针变量,然后通过二级间址方法存取变量的值。
7.5.3 指向指针的指针变量应用
2008 00:5695
例 7-10 通过二级间址方法存取变量的值。
main()
{
double d=123.456,*p,**pp;
pp=&p;
p=&d;
printf("d=%8.3f,",**pp);
**pp+=543.21;
printf("d=%8.3f\n",d);
}
运行结果为
d=123.456,d=666.666
2008 00:5696
上述指针变量 pp指向指针变量 p,而指针变量 p又指向双精度实型变量 d,如图 7-17所示,图中假设指针变量 p的地址是 1500,
变量 d的地址是 3500。此时 *pp表示指针变量 p的值(即变量 d的地址),因此表达式 **pp与变量 d等价。
1500
指向指针的指针变量 pp
3500
指针变量 p
123.45
变量 d
1500(地址 ) 3500(地址 )
图 7-17 指向指针指针指向指针变量
2008 00:5697
2.指向指针数组,存取指针数组元素所指内容可以把一个指针数组的首地址赋给指向指针的指针变量,例如:
例 7-11 有三个等级分,由键盘输入 1,屏幕显示,pas
s”,输入 2显示,good”,输入 3显示,excellent” 。
main()
{
int grade;
char *ps[ ]={"pass","good","excellent"},**pp;
pp=ps;
printf("请输入等级分 (1~3),");
scanf("%d",&grade);
printf("%s\n",*(pp+grade-1));
} 运行结果:
请输入等级 (1~ 3),2↙
good
2008 00:5698
上述程序中 pp指向数组 ps的第一个元素 ps[0],pp+1
则指向 ps的下一个元素 ps[1],pp+2指向 ps[2],如图 7-18所示 。 因此 *pp就是字符串,pass” 的首地址,*(pp+1)则是字符串,good” 的首地址,*(pp+2)是字符串,excellent”
的首地址 。
p a s s \0ps[0]
g 0 0 d \0ps[1]
ps[2]
pp
pp+1
pp+2
图 7-18 指向指针的指针指向指针数组
e x c e l l e n t \n
2008 00:5699
3.思考很多人会提出这样的问题:二维数组是一维数组的数组,而数组名是个地址,因此二维数组本质上应该和二级指针相同,所以可以写出如下程序:
2008 00:56100
例 7-12一个输出结果出乎意料的程序 。
void main()
{
int b[2][2]={1,2,3,4};
int **a;
a=b;
printf("%d",*(*(a+1)+1));
}
说明,
上机实验证明这个程序输出随机值,不能准确地输出我们预期的数组元素 b[1][1]的值。
要想得到正确的输出结果,则要把 a定义成 int (*a)[2]。
运行结果,
30036
2008 00:56101
原因:
1.如果把 a定义成 int **a,则 a是一个指向指针的指针变量,
他指向的仍然是一个指针变量,后者是一个指向 int变量的指针变量 。 大家已经知道,指向 int变量的指针变量占 2个字节,int变量也占 2个字节,根据指针的基本运算容易知道:
① a+1表示在 a的基础上后移了 2个字节,a+1便指向了 b[0][1],因此 *(a+1)即为 b[0][1]的值,
也就是 2;
② *(a+1)+1表示在 2的基础上后移 2个字节,得到 4。 *(*(a+1)+1)表示起始地址地址为 4的连续两个内存单元中存储的变量值,显然这个值并不是 b[1][1]。
2008 00:56102
2.如果把 a定义成 int (*a)[2],则 a是指向具有两个
int元素的一维数组的指针变量,则 a+1表示跨越 2个
int 元素,即 a+1指向二维数组 b[2][2]的第 1行(注意是从 0开始记数),而 *(a+1)+1很显然是指向了
b[1][1]。
下面以存储示意图 7-19来解释这个程序,以便读者更容易理解,(假设数组 b的起始地址是 100h,
变量 a的地址是 200h)
2008 00:56103
注,a=b,则 a指向了 b[0][0],而 a+1
则指向了 b[0][1],因此 *(a+1)等于
b[0][1]也就是 2。而 *(a+1)+1则为 4,即
*(a+1)+1指向地址为 4的单元格
a
a+1
*(a+1)+1
*(*(a+1)+1)的值显然不是
b[1][1]的值,而应该是由地址为 0004h和 0005h的两个内存单元中的值所决定的。
0000h
0001h
0002h
0003h
0004h?
0005h?
……… ………
b的起始地址即
0100h
1
2
3
4
0109h?
……… ………
a的地址即 0200H
……… ………
图 7-19 程序的存储示意图
2008 00:56104
一个结构体类型变量在内存中占有一段连续存储单元,这段内存单元的首地址,就是该 结构体变量的指针 。
可以用一个指针变量指向一个结构体变量,或指向结构体数组中的元素。这样的指针变量称为结构体的指针变量。
7.6 指针与结构
2008 00:56105
struct stu *pstu;
(*pstu).num 或者,pstu->num
7.6指针与结构体
一个指针当用来指向一个结构时,称之为结构指针变量。
结构指针变量中的值是所指向的结构变量的首地址。
结构指针变量声明的一般形式为:
struct 结构名 *结构指针变量名
通过结构指针可以访问该结构变量的成员,一般形式为:
(*结构指针变量 ).成员名 或者结构指针变量 ->成员名
#include<stdio.h>
struct student
{
int num;
char *name;
char sex;
float score;
}stu={1,“张三 ",'F',55},*pstu;
void main()
{
pstu=&stu;
printf("学号,%d 姓名,%s\n",stu.num,stu.name);
printf("性别,%c 成绩,%5.2f\n\n",stu.sex,stu.score);
printf("学号,%d 姓名,%s\n",(*pstu).num,(*pstu).name);
printf("性别,%c 成绩,%5.2f\n\n",(*pstu).sex,(*pstu).score);
printf("学号,%d 姓名,%s\n",pstu->num,pstu->name);
printf(“性别,%c 成绩,%5.2f\n\n",pstu->sex,pstu->score);
}
学号,1 姓名:张三性别,F 成绩,55.00
学号,1 姓名:张三性别,F 成绩,55.00
学号,1 姓名:张三性别,F 成绩,55.00
Press any key to continue
2008 00:56106
指向结构体数组的指针
struct student
{
char name[13];
char sex;
float score;
};
struct student stu[3]=
{
{"zhan",'M',110},
{"Li",'F',67},
{"wang",'M',911}
};
main()
{
struct student *p;
for(p=stu; p<stu+3; p++)
printf("\n%-10s%2c,%4d",
p->name,p->sex,p->score);
}
运行结果如下:
zhan M 110
Li F 67
Wang M 911
p-> MZhan 110 Stu[0]
FLi 67 Stu[1]
MWang 911 Stu[2]
指向结构体数组的指针
2008 00:56107
7.6.3指向结构体的指针作为函数参数类似于普通指针变量作为函数参数一样,
用指向结构体的指针变量作实参时,是属于,地址传递,方式 。
同其他变量一样,结构变量的首地址就是该结构变量的指针 。 用地址运算符 &,
就可以获得结构变量的指针 。
2008 00:56108
void printf_data(struct student *p)
{
printf("\n%-10s%2c,%4d",p->name,p->sex,p->score);
}
void main(void)
{
struct student *p;
for(p=stu; p<stu+3; p++) print_data(p);
}
例 7-15用函数调用方式,改写例 7-14,编写一个专门的显示结构成员函数,主函数调用时,用指向结构的指针变量作实参。
运行结果如下:
zhan M 110
Li F 67
Wang M 911
2008 00:56109
指针与函数的关系主要包括两方面的内容:
函数的返回值可以是指针类型;
函数指针和指向函数的指针变量。
7.7 指针与函数
2008 00:56110
7.7.1指针变量作为函数返回值一个函数不仅可以返回 int型,float型,
char型和结构类型等数据类型,也可以返回指针类型的数据 。 返回指针类型的函数定义格式为:
2008 00:56111
类型名 *函数名( [参数表 ])
{
函数体;
}
如:
int *func()
{
int *p;
………..
return ( p) ;
}
2008 00:56112
int *getaddress(int x)
{
return &x;
}
void main()
{
int a=10;
printf("%d",getaddress(a));
}
例 7-16 输出整型变量的地址。
2008 00:56113
7.7.2函数的指针和指向函数的指针变量函数在内存中也占据一定的存储空间并有一个入口地址 ( 函数开始运行的地址,但不一定是存储区的首地址 ),这个地址就称为该 函数的指针 。 可以用一个指针变量来存放函数的入口地址,这时称该指针指向这个函数,并成该指针变量为,指向函数的指针变量,,简称为,函数的指针变量,或,函数指针,,可以通过函数指针来调用函数,这是函数指针的主要用途 。 函数指针定义的一般形式如下:
类型标识符 ( *指针变量名 ) ( ) ;
2008 00:56114
例 7-17 用函数的指针调用函数求 a,b中的最大者 。
int max(int x,int y)
{
if(x>=y)
return x;
else
return y;
}
void main()
{
int a,b,c;
int (*p)(); /*定义 p是一个函数指针 */
p=max; /*使 p指向函数 max*/
scanf("%d%d",&a,&b);
c=(*p)(a,b); /*等价于 c=max(a,b)*/
printf("\n a=%d,b=%d,max=%d",a,b,c);
}
运行结果,
2 7↙
a=2,b=7,max=7
2008 00:56115
总结
指针是一个变量,它存储另一个对象的内存地址
指针的声明由基本类型、星号 (*) 和变量名组成
为指针赋值,赋值运算符右侧必须是一个地址。
如果是普通变量需要在前面加一个取地址运算符
&;如果是另一个指针变量或者是一个数组,不需要加 &运算符
运算符 * 用于返回指针指向的内存地址中存储的值
2008 00:56116
总结
指针的算术运算的含义是指针的移动,将指针执行加上或者减去一个整数值 n的运算相当于指针向前或向后移动 n个数据单元
指针可以执行比较相等的运算,用来判断两个指针是否指向同一个变量
指向数组的指针,存储的是数组中元素的地址。
数组 data的第 (i + 1) 个元素的地址可表示为
&data[i] 或 (data+i)