第 6章 指针本章要点
6.1 地址和指针的概念
6.2 指向变量的指针变量
6.3 指针与数组
6.4 指针与字符串本章要点:
理解地址和指针的概念,及指针变量的初始化方法;
掌握指针变量的引用方法和格式,特别注意本文对在引用指针变量时所强调的内容;
熟念掌握指针变量的运算规则。
6.1 地址和指针的概念
地址和指针的定义
指针有以下四方面的重要作用:
(1)指针为函数提供修改变量值的手段。
(2)指针为 C的动态内存分配系统提供支持。
(3)指针为动态数据结构 (如链表、队列、二叉树等 )提供支持。
(4)指针可以改善某些子程序的效率 。
6.2指向变量的指针变量
如上一节所述,指向变量的指针与指针变量是不同的概念,
指向变量的指针等价于变量的地址,而指针变量是存放变量地址的变量。下面详细介绍指向变量的指针变量,即存放变量地址的指针变量。
6.2.1指针变量的定义
指针变量的定义形式为:
[存储类型]数据类型 *指针变量名[=初始值];
说明:
(1)存储类型是指指针变量本身的存储类型,与前面介绍过的相同,可分为 register型,static型,extern型和 auto型,若缺省则为 auto型。
(2)数据类型是指该指针可以指向该数据类型的变量。
(3)*号表示后面的变量是指针变量。
(4)初始值通常为某个变量名或为 NULL,不要将内存中的某个地址值作为初始地址值。
6.2.2指针变量的引用
变量名其实是给变量数据存储区域所取的名字。计算机内存的每个存储位置都对应惟一的存储地址。在前几章的叙述中,
都是通过变量名访问变量。 C语言支持使用变量存储地址对变量进行存取操作。
引用指针变量时必须注意以下几点:
( 1)未经赋值的指针变量不能使用;
( 2)指针变量的赋值只能赋予地址。
下面介绍两个和指针有关的运算符。
(1)&取地址运算符
C语言中提供了取地址运算符 &,它是一个单目运算符,结合性自右向左,它的功能是取变量的地址。其一般形式为:
&变量名 ;
例如,&a表示变量 a的地址,&b表示变量 b的地址。变量本身必须预先说明。
注意,&在形式上虽然与位操作中的“按位与”运算符完全相同,但“按位与”运算符是双目运算符,而此处的取地址运算符为单目运算符,二者在使用上不会发生混淆。
下面是一个使用,&”运算符的例子。
用,&”运算符查看变量和数组地址示例
#include<stdio.h>
void main()
{
int a=2;float b=3;double c=3;
char d[5];
printf("address of a is,%p\n",&a); /*输出变量 a的地址 */
printf("address of b is,%p\n",&b); /*输出变量 b的地址 */
printf("address of c is,%p\n",&c); /*输出变量 c的地址 */
printf("address of array d is,%p\n",d); /*输出数组 d的首地址 */
}
运行结果为:
(2)*指针运算符
指针运算符,*” 是单目运算符,其结合性为自右至左,用来表示指针变量所指的变量。在,*” 运算符之后的变量必须是指针变量。
变量与指针的存储关系引用指针变量示例。
#include<stdio.h>
void main()
{
int a=50,*p; /*声明整型指针变量 p*/
p=&a;
printf("*p=%d\n",*p); /*输出指针变量 p所指向变量的值 */
*p=100;
printf("a=%d\n",a);
}
程序运行结果:
6.2.3 指针变量的运算
1.指针变量加减一个整数
(1)*p++和 (*p)++的区别
(2)*++p和 ++(*p)的区别了解指针变量的值的变化
#include<stdio.h>
void main()
{ int i=0,*pi=&i;
float f=0,*pf=&f;
long l=0,*pl=&l;
printf("pi=%lu,pi+1=%lu\n",pi,pi+1);
printf("pi=%lu,pi-1=%lu\n",pi,pi-1);
printf("pf=%lu,",pf);
printf("pf++=%lu\n",pf);
printf("pl=%lu,",pl);
pl--;
printf("pl++=%lu\n",pl);}
程序运行结果:
2.两个同类型的指针变量相减
#include<stdio.h>
void main()
{
float x[5],*p,*q;
p=&x[1];q=&x[4];
printf("q-p=%d\n",q-p);
}
程序运行结果为:
3.指针变量的关系运算
两个指向相同类型变量的指针变量还可以使用关系运算符进行比较运算,对两个指针变量中存放的地址进行比较。
指针运算规则很重要,它是指针运算必须遵循的原则。要指出如下两个错误的认识:
①认为指针与整型变量是一样的,这就混淆了指针变量与整型变量的本质区别。
②当两个指针指向的目标的数据类型相同时,就认为可以对这两个指针进行相减运算或关系运算,这是不确切的。因为 C语言规定,两指针进行相减或关系运算的充要条件是,两个指针不但要指向同一数据类型的目标,同时还要求该目标是惟一的。
6.3 指针与数组
6.3.1指向数组元素的指针变量采用指针法输出数组的全部元素。
#include<stdio.h>
void main()
{
int a[10],i;
int*p;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
for(p=a;p<a+10;p++) /*指针的后移 */
printf("%d ",*p); /*访问数组的各元素 */
printf("\n");
}
运行结果为:
6.3.2 通过指针引用数组元素
方法 1 用下标法引用数组元素。
#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]);
}
printf("\n");}
方法 2 用数组名法引用数组元素。
#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));
}
printf("\n");}
方法 3 用指针变量法引用数组元素
#include<stdio.h>
void main()
{ int a[10],*p=NULL,i;
p=a;
for(i=0;i<10;i++)
{
scanf("%d",p+i);
}
p=a; /*在循环开始前,确保指针 P指向数组首地址 */
for(i=0;i<10;i++)
{
printf("%d ",*(p+i));
}
printf("\n");}
6.3.3 指向多维数组的指针用指针法输入输出二维数组各元素
#include<stdio.h>
void main()
{ int a[3][4],*ptr;
int i,j;
ptr=a[0];
printf("Please input data:\n");
for(i=0;i<3;i++)
for(j=0;j<4;j++)
scanf("%d",ptr++);/*指针的表示方法 */
ptr=a[0];
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
printf("%-3d",*ptr++);
printf("\n");}}
程序运行结果:
6.3.4 指针数组
1.指针数组的定义
一个数组,它的各个元素都是指针,这种数组称为指针数组。其定义方式如下:
类型标识符 *数组名 [元素个数 ];
指针数组初始化
#include<stdio.h>
void main()
{
int a[2][5];int*pa[2]={a[0],a[1]};int i,j;
printf("这是一个指针数组初始化的程序例子 !\n");
for(i=0;i<2;i++)
for(j=0;j<5;j++) scanf("%d,t",&a[i][j]);
for(i=0;i<2;i++)
for(j=0;j<5;j++) printf("a[%d][%d]= %d\t",i,j,pa[i][j]);
}
程序运行结果为:
6.4 指针与字符串
6.4.1指向字符串的指针变量
sp与 cp的关系字符串指针作函数参数
#include<stdio.h>
void strcpy(char*,char*);
void main()
{
char *str1="Pascal";
char *str2="C++";
printf("%s\n%s\n",str1,str2);
strcpy(str1,str2);
printf("%s\n%s\n",str1,str2);}
void strcpy(char *t,char*s)
{ while(*s!='\0')
{*t=*s;
t++;
s++;}
t=s;}
运行结果:
6.4.2字符串指针与字符数组的区别
既然字符串指针和字符数组均可以对字符串进行操作,那么使用字符串指针变量与字符数组又有什么区别呢?实际上,
用字符数组和字符串指针变量都可以实现字符串的存储和运算,但是两者是有区别的。在使用时应注意以下几个问题。
( 1)字符串指针变量本身是一个变量,用于存放字符串的首地址。而字符串本身是存放在以该首地址为首的一块连续的内存空间中并以,′\ 0′”’作为串的结束。字符数组是由若干个数组元素组成的,它可用来存放整个字符串。
( 2)对字符数组作初始化赋值,必须采用外部类型或静态类型,如,char st[]={ "C Language"};”,而对字符串指针变量则无此限制,如,char *ps="C Language";”。
6.4.2字符串指针与字符数组的区别
( 3)对字符串指针方式,,char *ps="C Language";”
可以写为:
char *ps;
ps="C Language";
而对数组方式,,char st[]= {"C Language"};”不能写为:
char st[ 20];
st={"C Language");
而只能对字符数组的各元素逐个赋值。
( 4)字符串指针变量占用的内存要少于字符数组。字符串指针变量只是在程序运行中被临时赋予一字符串的首地址,
而字符数组在程序被编译时要为每个数组元素分配内存单元,
而且必须用字符数组可能存放字符的最大数目作为数组的大小,尽管有可能在大多数时候该数组只用到其占用内存中的一部分。
本章小结
指针是 C语言中非常重要的概念,使用指针可以有效地表示复杂的数据结构,灵活方便地实现机器语言所能完成的功能,而且可以使编写的程序清晰、简洁并可以生成紧凑、高效的代码。指针可以作为函数间传递的参数,
也可以作为函数的返回值类型,它为函数间各类数据的传递提供了简捷便利的方法。指针可用于动态分配存储空间,可更简单有效地处理数组。 C语言之所以成为优秀的系统程序设计语言,在很大程度上应归功于指针的成功应用。
6.1 地址和指针的概念
6.2 指向变量的指针变量
6.3 指针与数组
6.4 指针与字符串本章要点:
理解地址和指针的概念,及指针变量的初始化方法;
掌握指针变量的引用方法和格式,特别注意本文对在引用指针变量时所强调的内容;
熟念掌握指针变量的运算规则。
6.1 地址和指针的概念
地址和指针的定义
指针有以下四方面的重要作用:
(1)指针为函数提供修改变量值的手段。
(2)指针为 C的动态内存分配系统提供支持。
(3)指针为动态数据结构 (如链表、队列、二叉树等 )提供支持。
(4)指针可以改善某些子程序的效率 。
6.2指向变量的指针变量
如上一节所述,指向变量的指针与指针变量是不同的概念,
指向变量的指针等价于变量的地址,而指针变量是存放变量地址的变量。下面详细介绍指向变量的指针变量,即存放变量地址的指针变量。
6.2.1指针变量的定义
指针变量的定义形式为:
[存储类型]数据类型 *指针变量名[=初始值];
说明:
(1)存储类型是指指针变量本身的存储类型,与前面介绍过的相同,可分为 register型,static型,extern型和 auto型,若缺省则为 auto型。
(2)数据类型是指该指针可以指向该数据类型的变量。
(3)*号表示后面的变量是指针变量。
(4)初始值通常为某个变量名或为 NULL,不要将内存中的某个地址值作为初始地址值。
6.2.2指针变量的引用
变量名其实是给变量数据存储区域所取的名字。计算机内存的每个存储位置都对应惟一的存储地址。在前几章的叙述中,
都是通过变量名访问变量。 C语言支持使用变量存储地址对变量进行存取操作。
引用指针变量时必须注意以下几点:
( 1)未经赋值的指针变量不能使用;
( 2)指针变量的赋值只能赋予地址。
下面介绍两个和指针有关的运算符。
(1)&取地址运算符
C语言中提供了取地址运算符 &,它是一个单目运算符,结合性自右向左,它的功能是取变量的地址。其一般形式为:
&变量名 ;
例如,&a表示变量 a的地址,&b表示变量 b的地址。变量本身必须预先说明。
注意,&在形式上虽然与位操作中的“按位与”运算符完全相同,但“按位与”运算符是双目运算符,而此处的取地址运算符为单目运算符,二者在使用上不会发生混淆。
下面是一个使用,&”运算符的例子。
用,&”运算符查看变量和数组地址示例
#include<stdio.h>
void main()
{
int a=2;float b=3;double c=3;
char d[5];
printf("address of a is,%p\n",&a); /*输出变量 a的地址 */
printf("address of b is,%p\n",&b); /*输出变量 b的地址 */
printf("address of c is,%p\n",&c); /*输出变量 c的地址 */
printf("address of array d is,%p\n",d); /*输出数组 d的首地址 */
}
运行结果为:
(2)*指针运算符
指针运算符,*” 是单目运算符,其结合性为自右至左,用来表示指针变量所指的变量。在,*” 运算符之后的变量必须是指针变量。
变量与指针的存储关系引用指针变量示例。
#include<stdio.h>
void main()
{
int a=50,*p; /*声明整型指针变量 p*/
p=&a;
printf("*p=%d\n",*p); /*输出指针变量 p所指向变量的值 */
*p=100;
printf("a=%d\n",a);
}
程序运行结果:
6.2.3 指针变量的运算
1.指针变量加减一个整数
(1)*p++和 (*p)++的区别
(2)*++p和 ++(*p)的区别了解指针变量的值的变化
#include<stdio.h>
void main()
{ int i=0,*pi=&i;
float f=0,*pf=&f;
long l=0,*pl=&l;
printf("pi=%lu,pi+1=%lu\n",pi,pi+1);
printf("pi=%lu,pi-1=%lu\n",pi,pi-1);
printf("pf=%lu,",pf);
printf("pf++=%lu\n",pf);
printf("pl=%lu,",pl);
pl--;
printf("pl++=%lu\n",pl);}
程序运行结果:
2.两个同类型的指针变量相减
#include<stdio.h>
void main()
{
float x[5],*p,*q;
p=&x[1];q=&x[4];
printf("q-p=%d\n",q-p);
}
程序运行结果为:
3.指针变量的关系运算
两个指向相同类型变量的指针变量还可以使用关系运算符进行比较运算,对两个指针变量中存放的地址进行比较。
指针运算规则很重要,它是指针运算必须遵循的原则。要指出如下两个错误的认识:
①认为指针与整型变量是一样的,这就混淆了指针变量与整型变量的本质区别。
②当两个指针指向的目标的数据类型相同时,就认为可以对这两个指针进行相减运算或关系运算,这是不确切的。因为 C语言规定,两指针进行相减或关系运算的充要条件是,两个指针不但要指向同一数据类型的目标,同时还要求该目标是惟一的。
6.3 指针与数组
6.3.1指向数组元素的指针变量采用指针法输出数组的全部元素。
#include<stdio.h>
void main()
{
int a[10],i;
int*p;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
for(p=a;p<a+10;p++) /*指针的后移 */
printf("%d ",*p); /*访问数组的各元素 */
printf("\n");
}
运行结果为:
6.3.2 通过指针引用数组元素
方法 1 用下标法引用数组元素。
#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]);
}
printf("\n");}
方法 2 用数组名法引用数组元素。
#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));
}
printf("\n");}
方法 3 用指针变量法引用数组元素
#include<stdio.h>
void main()
{ int a[10],*p=NULL,i;
p=a;
for(i=0;i<10;i++)
{
scanf("%d",p+i);
}
p=a; /*在循环开始前,确保指针 P指向数组首地址 */
for(i=0;i<10;i++)
{
printf("%d ",*(p+i));
}
printf("\n");}
6.3.3 指向多维数组的指针用指针法输入输出二维数组各元素
#include<stdio.h>
void main()
{ int a[3][4],*ptr;
int i,j;
ptr=a[0];
printf("Please input data:\n");
for(i=0;i<3;i++)
for(j=0;j<4;j++)
scanf("%d",ptr++);/*指针的表示方法 */
ptr=a[0];
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
printf("%-3d",*ptr++);
printf("\n");}}
程序运行结果:
6.3.4 指针数组
1.指针数组的定义
一个数组,它的各个元素都是指针,这种数组称为指针数组。其定义方式如下:
类型标识符 *数组名 [元素个数 ];
指针数组初始化
#include<stdio.h>
void main()
{
int a[2][5];int*pa[2]={a[0],a[1]};int i,j;
printf("这是一个指针数组初始化的程序例子 !\n");
for(i=0;i<2;i++)
for(j=0;j<5;j++) scanf("%d,t",&a[i][j]);
for(i=0;i<2;i++)
for(j=0;j<5;j++) printf("a[%d][%d]= %d\t",i,j,pa[i][j]);
}
程序运行结果为:
6.4 指针与字符串
6.4.1指向字符串的指针变量
sp与 cp的关系字符串指针作函数参数
#include<stdio.h>
void strcpy(char*,char*);
void main()
{
char *str1="Pascal";
char *str2="C++";
printf("%s\n%s\n",str1,str2);
strcpy(str1,str2);
printf("%s\n%s\n",str1,str2);}
void strcpy(char *t,char*s)
{ while(*s!='\0')
{*t=*s;
t++;
s++;}
t=s;}
运行结果:
6.4.2字符串指针与字符数组的区别
既然字符串指针和字符数组均可以对字符串进行操作,那么使用字符串指针变量与字符数组又有什么区别呢?实际上,
用字符数组和字符串指针变量都可以实现字符串的存储和运算,但是两者是有区别的。在使用时应注意以下几个问题。
( 1)字符串指针变量本身是一个变量,用于存放字符串的首地址。而字符串本身是存放在以该首地址为首的一块连续的内存空间中并以,′\ 0′”’作为串的结束。字符数组是由若干个数组元素组成的,它可用来存放整个字符串。
( 2)对字符数组作初始化赋值,必须采用外部类型或静态类型,如,char st[]={ "C Language"};”,而对字符串指针变量则无此限制,如,char *ps="C Language";”。
6.4.2字符串指针与字符数组的区别
( 3)对字符串指针方式,,char *ps="C Language";”
可以写为:
char *ps;
ps="C Language";
而对数组方式,,char st[]= {"C Language"};”不能写为:
char st[ 20];
st={"C Language");
而只能对字符数组的各元素逐个赋值。
( 4)字符串指针变量占用的内存要少于字符数组。字符串指针变量只是在程序运行中被临时赋予一字符串的首地址,
而字符数组在程序被编译时要为每个数组元素分配内存单元,
而且必须用字符数组可能存放字符的最大数目作为数组的大小,尽管有可能在大多数时候该数组只用到其占用内存中的一部分。
本章小结
指针是 C语言中非常重要的概念,使用指针可以有效地表示复杂的数据结构,灵活方便地实现机器语言所能完成的功能,而且可以使编写的程序清晰、简洁并可以生成紧凑、高效的代码。指针可以作为函数间传递的参数,
也可以作为函数的返回值类型,它为函数间各类数据的传递提供了简捷便利的方法。指针可用于动态分配存储空间,可更简单有效地处理数组。 C语言之所以成为优秀的系统程序设计语言,在很大程度上应归功于指针的成功应用。