第十章 指针本章重点:
指针变量和指针运算符。
指针用作函数参数。
指针与数组。
10.1指针的基本概念一、地址在计算机中,把内存区划分为一个一个的存储单元,每个单元为一个字节(8位),它们都有一个编号,这个编号就是内存地址。如下图所示:

注意:1、程序中定义的每个数据在编译后都占有各自的内存区。
2、数据所占有的存储单元个数是由其类型决定的。
3、首地址:第1个单元的地址,
4、表示地址的数与整数的区别:
5、系统通过变量名对内存单元进行访问
2.数据在内存中的存储
整型变量---分配2个字节单元
实型变量—分配4个字节单元
字符变量—分配1个字节单元

2.内存单元的地址与内存单元的内容。
printf(“%d”,i);
scanf(“%d”,&i);
的操作过程。
3.对变量值的访问方式直接访问:按变量地址存取变量值的方式。
间接访问方式:将变量的地址存放在另一个变量当中,通过对另一变量来访问变量。
4.指针:就是变量的地址。
指针变量:用于存放变量地址的变量。
10.2 指针变量和指针运算符
一.指针变量的定义
1,格式:类型名 *指针变量名;
2、举例
int *p1;
char *s1,*s2;
float *a1,*a2,f;
3、说明
a,类型名:指针变量所指向的变量类型。
b.,*” 是定义指针变量的标志,不可丢掉。
c,指针变量定义后,其值是不确定的。
4、赋值:可以进行初始化,也可以使用赋值语句
(1)、初始化,int a,*s=&a;
(2)、赋值语句
int a,*s;
s=&a;
3)、注意:只能用同类型变量的地址进行赋值!
如定义:int *s; float f; 则 s=&f;是非法的。
二,指针变量的引用
1、两个运算符:& 与 *
&:取地址,注意与作位运算符时的不同(双目)
*:取内容,注意与作乘运算符时的不同(双目)
2、说明
a,&既可作用于一般变量,也可作用于指针变量
b,* 只能作用于指针变量
c,定义指针变量时的 * 与该处的含义不同
3、指针变量可以进行的操作
(1)赋值:int a,*p1=&a,*p2; p2=p1;
下面的赋值是不合法的,
int *ip;
ip=100;
假设,
int i=200,x;
int *ip;
ip=&i;
x=*ip; 通过指针变量ip间接访问变量i,
(2)输出:printf(“%x”,p1);
(3)增减:p1++; p2--; p1+=4; (只能和一个整数)
p+n
不论指针变量指向何种数据类型,指针和整数进行加、减运算时,编译程序总根据所指对象的数据长度对n放大。
(4),两个指针变量在一定条件下,可进行减法运算。设p,q指向同一数组,则p-q的绝对值表示p所指对象与q所指对象之间的元素个数。
例1
#include <stdio.h>
main( )
{
int a1=11,a2=22;
int *p1,*p2;
p1=&a1;
p2=&a2;
printf(“%d,%d\n”,*p1,*p2);
p2=p1;
printf(“%d,%d\n”,*p1,*p2);
}
例2
#include <stdio.h>
main( )
{
int a1=11,a2=22;
int *p1,*p2,*p;
p1=&a1;
p2=&a2;
printf(“%d,%d\n”,*p1,*p2);
p=p1; p1=p2; p2=p;
printf(“%d,%d\n”,*p1,*p2);
}
例3
#include <stdio.h>
main( )
{
int a1=11,a2=22,t;
int *p1,*p2;
p1=&a1;
p2=&a2;
printf(“%d,%d\n”,a1,a2);
t=*p1; *p1=*p2; *p2=t;
printf(“%d,%d\n”,a1,a2);
}
三、指针作为函数的参数
1、形式:只需要形式参数名前加上一个*即可。
如:void test(int *pointer,char f,char *s)
2、说明指针作为参数可以改变主调函数中变量的值。
例4:对输入的两个整数按大小顺序输出
Void Swap( int *p1,int *p2)
{ int temp;
temp=*p1; *p1=*p2; *p2=temp; }
Void 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);
}
指针与一维数组 (Pointer and Arrays)
一、通过指针访问一维数组
1、数组结构的分析设有数组定义为:int a[5]; 则有:
(1) a表示数组在内存中的首地址,也就是数组中第1
个元素的首地址,它是一个地址常量,其值由系
统在编译时确定,程序运行期间不能改变。
(2) 数组中的各元素表示为:
a[0]、a[1]、a[2]、a[3]、a[4]
或者是:
*(a+0)(或*a)、*(a+1)、*(a+2)、*(a+3)、*(a+4)
(3) 数组中的各元素的地址表示为:
&a[0]、&a[1]、&a[2]、&a[3]、&a[4]
或者是:a+0(或a)、a+1、a+2、a+3、a+4
(4) 另一种解释:数组名是基地址,下标是偏移量,a[i]就表示以a为基地址,偏移i个元素的那个元素。
(5) 数组的逻辑结构如下:

2、指针与数组的关系现定义一个指针变量:int *p;
并进行赋值:p=a; 或 p=&a[0];
则,指针变量p指向了数组a的开始,二者产生了联系,这样就可以通过指针变量p访问数组a了。
注意:a是指针常量,p是指针变量。

(1),p+i和a+i均表示a[i]的地址,
(2),*(p+i)和*(a+i)都表示p+i和a+i所指对象的内容,即为a[i]。
(3),指向数组元素的指针,也可以表示成数组的形式,也就是说,它允许指针变量带下标,如p[i]与*(p+i)等价。
假若,p=a+5;
则p[2]就相当于*(p+2),由于p指向a[5],所以p[2]就相当于a[7]。而p[-3]就相当于*(p-3),它表示a[2]。
3、一维数组的访问例5 用多种方法访问一维数组各元素
#include <stdio.h>
main( )
{ int a[5]={1,3,5,7,9},i,*p=a;
for (i=0;i<5;i++) printf(“%d”,a[i]);
for (i=0;i<5;i++) printf(“%d”,*(a+i));
for (i=0;i<5;i++) printf(“%d”,p[i]);
for (i=0;i<5;i++) printf(“%d”,*(p+i));
for (;p<a+5;p++) printf(“%d”,*p);
p=a;
while (p<a+5) printf(“%d”,*p++);
}
4、几个表达式的分析 P191
设定义:int a[3]={1,2,3},*s=a;
s,*s a,*a
s++,*s++,a++,*a++
*(s++),(*s)++ *(a++),(*a)++
初始化时的*s=a; 与语句 *s=a; 的不同一个指针变量加/减一个整数后,指针变量值的变化情况
10.2指针与多维数组一,二维数组元素的地址
为了说明问题,我们定义以下二维数组,
int a[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};
┏━━━━┓ ┏━┳━┳━┳━┓
a─→ ┃ a[0] ┃─→┃0 ┃1 ┃2 ┃3 ┃
┣━━━━┫ ┣━╋━╋━╋━┫
┃ a[1] ┃─→┃4 ┃5 ┃6 ┃7 ┃
┣━━━━┫ ┣━╋━╋━╋━┫
┃ a[2] ┃─→┃8 ┃9 ┃10┃11┃
┗━━━━┛ ┗━┻━┻━┻━┛
图5,
但从二维数组的角度来看,a代表二维数组的首地址,则有:
a[3][4]
a ┏━┳━┳━┳━┓
(1000)─→ ┃0 ┃1 ┃2 ┃3 ┃
a+1 ┣━╋━╋━╋━┫
(1008)─→ ┃4 ┃5 ┃6 ┃7 ┃
a+2 ┣━╋━╋━╋━┫
(1016)─→ ┃8 ┃9 ┃10┃11┃
┗━┻━┻━┻━┛
图6,
二,指向多维数组的指针变量
1.在Turbo C中,可定义如下的指针变量,
int a[3][4],(*p)[4];
p=a;
2.含义:pa是指针变量,它指向一个数组,数组 含有4个元素,每个元素的类型是int。
3.说明:
a,与定义 int *pa; 以及 int *pa[4]; 含义不同。
b,如果执行pa++,则pa实际增加了多少呢?
开始时p指向二维数组第0行,当进行p+1运算时,根据地址运算规则,此时放大因子为4x2=8,所以此时正好指向二维数组的第1行。和二维数组元素地址计算的规则一样,*p+1指向a[0][1],*(p+i)+j则指向数组元素a[i][j]。
例6,有一个班,3个学生,各学4门课,计算总平均成绩,以及第n个学生的成绩
Void main()
{
Void average( float * p,int n);
Void search(float (*p)[4],int n);
Float score[3][4]={{65,67,70,60},{80,97,90,81},{90,99,100,98}};
Average(score,12);
Search(score,2);
}
Void average(float *p,int n)
{ float *p_end;
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)
{ int I;
For (i=0;i<4;I++)
printf(“%5.2f”,*(*(p+n)+i));
}// *(p+n)表示一维数组的开始
10.3 指针与字符串
1.字符串还可以定义为:char *s=“abcde”; 它在内存中占用6个字节,长度为5。其结构为:

2、用字符数组与用指针使用字符串的比较定义及初始化
char s[]=“abcde”; char *p=“abcde”;
赋值
char s[6]; char *p;
s=“abcde”; /* 不对,应为 */ p=“abcde”;/*把字符串首地址赋给p */
strcpy(s,“abcde”); strcpy(p,“abcde”);
使用
s不能加/减 p可以加/减
注意:char s[]=“abc”;与char s[]={‘a’,’b’,’c’};的区别
例7,将字符数组a中的字符串拷贝到字符数组b中

3、通过指针在函数间传递字符串
①、函数的定义形式
例如:
char func(char a[],char b[])
{ 函数体;}
也可以写为:
char func(char *a,char *b)
{ 函数体;}
例8 设计一函数,实现与标准函数strcpy类似的功能。
#include <stdio.h>
void mystrcpy(char *to,char *from);
main( )
{ char ca[20],*cp=“Hello,world!”;
mystrcpy(ca,cp);
printf(“%s”,ca);
}
void mystrcpy(char *to,char *from)
{
while (*from!=‘\0’){ *to=*from; to++; from++; }
*to=‘\0’;
}
思考:调用 mystrcpy(ca,cp+2),结果如何?
10.4指针数组与指向指针的指针
一、指针数组
1、指针数组的概念指针数组是一种特殊的数组,它每个元素的类型都是指针类型(即地址),其它与一般数组相同。
当把某个量的地址放入某元素中后,即可通过该元素对相应的量进行间接访问。
2、指针数组的定义类型名 *指针数组名[常量表达式];//其中[]比*优先级高如:int *ap[3]; char *s[10]; 等等。
!! 请回忆:二维数组:int (*p)[4],区别异同?
3、指针数组的初始化
(1)int a1[5],a2[5],a3[5],*ap[3]={a1,a2,a3};
(2)char *sp[]={“abc”,“123”,“Hello”};
4、指针数组的赋值
上面的情况中,可以先定义变量,在进行赋值,即
(1)int a1[5],a2[5],a3[5],*ap[3];
ap[0]=a1; ap[1]=a2; ap[2]=a3;
(2)char *sp[]={“abc”,“123”,“Hello”};
sp[0]=“abc”; sp[1]=“123”; sp[2]=“Hello”;
例9,打印1月至12月的月名,
char *month_name(int n)
{
static char *name[]={
"Illegal month",
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};
return((n<1||n>12)?name[0]:name[n]);
}
main()
{
int i;
for(i=0; i<13; i++)
printf("%s\n",month_name(i));
二、指向指针的指针
1、多级指针的概念所谓多级指针,是指指针有多级指向关系。
2、多级指针的定义类型标识符 **指针变量名;
注:*的个数代表级数,*运算符的结合性是从右到左,因此,相当于*(*p),表示p指向一个指针变量。
如:char **pp,*p,ch=‘A’; 又p=&ch; pp=&p; 则:

10.5指针与函数一,指向函数的指针
1、函数名的含义
函数名代表一个地址,它是函数的入口,即函数代码在内存中的起始位置,类似于数组名。
2、概念
如果一个指针指向一个函数,则称该指针为函数指针。(注意与指针函数的区别)可以用一个指针变量指向函数,然后通过该指针变量调用此函数。
3、定义
类型名 (*指针变量名)(形参表);
如:int (*f ) (int a,int b);
例.10 函数指针举例
#include <stdio.h>
int max(int a,int b);
int min (int a,int b);
main( )
{ int x=3,y=4,z;
int (*pf) (int,int );
pf=max; z=(*pf) (x,y); printf(max is %d\n”,z);
pf=min; printf(“min is %d\n,(*pf) (x,y));
}
int max(int a,int b)
{ return(a>b?a:b); }
int min(int a,int b)
{ return(a<b?a:b); }
二.返回指针的函数
返回指针值的函数称为指针型函数.
定义:
类型说明符 *函数名(形参表)
{…../*函数体*/
}