今天大家一起来学习C语言中的指针吧!
4. 指针
4.1 指针的概念及运算
4.1.1 指针的概念
程序中的每个变量在内存中占有一个或多个连续的存储单元,比如,存储一个char型数据需要一个存储单元,存储一个int型数据需要两个连续的存储单元。我们把连续的存储单元中的第一个存储单元的地址称为变量的地址,计算机通过地址访问存储单元。
显然,地址是以整数的形式表示的。但是,由于存储器容量有限,不同的存储器其地址范围可能不同,而且不同类型的整型数据其取值范围也不相同,所以地址的取值范围可能不同于整数的取值范围,不能用整型变量存储内存地址。为了解决这一问题,C语言引入了“指针”这一概念,我们可以定义一个变量为指针变量,用以存储内存的地址。
换句话说,指针就是地址,指针变量就是存储地址的变量。
4.1.2 指针变量的定义和初始化
指针变量与其他变量一样,必须在使用前声明。定义指针变量与定义普通变量基本一样,唯一不同的是在指针变量标识符前加星号“ * ”。定义指针变量的一般形式为:
数据类型 *指针变量名1,*指针变量名2,……;
说明:
- 一条定义语句可以定义一个或多个指针变量。
- 星号(*)表示其后的变量是一个指针变量。
- “数据类型” 称为指针变量的基类型,即指针所指向的基本变量的数据类型。
- 指针变量可以和其他变量一起出现在同一个说明语句中。
说明3中的举例:
int a, *pi; /*定义整型变量a和指向整型数据的指针变量pi*/
float x, *pf; /*定义实型变量x和指向实型数据的指针变量pf*/
char ch, *pc; /*定义字符型变量ch和指向字符型数据的指针变量pc*/
指针变量定义之后,指针变量可指向数据的类型就确定了。在一般情况下,一个指针变量只能指向同一类型的变量。例如,上述定义的指针变量pi只能指向整型数据,pf只能指向实型数据,pc只能指向字符型数据。特别注意,如果指针变量的基类型与它指向的变量的数据类型不一致,那么在通过指针访问它所指向的变量时,可能会得到错误的结果。
说明4中的举例:
int i, j, a[10], *p, *q;
引入指针的目的是为了通过指针来访问其他数据对象,因此,一个指针变量指向一个其他变量才有实用价值。可以将已经定义的指针变量与另一个变量建立联系,这种联系称为指向。指向的实质就是把一个数据对象的地址赋值给一个指针变量,比如,当将变量a的地址赋值给指针变量p时,则说指针变量p指向了变量a。这个过程可以通过如下语句实现:
int a, *p;
a = 3;
p = &a;
4.1.3 与指针变量有关的运算
1. 取地址运算和取内容运算
为了引入指针变量,C语言提供取地址运算符和取内容运算符。
(1)&:取地址运算符,运算对象是变量,得到变量在存储器中的首地址。
(2)*:取内容运算符,运算对象是指针,得到指针所指向的变量的值。
例如:
int a = 3, b, *p; /*定义整型变量a,b和指向整型变量的指针变量p*/
p = &a; /*将变量a的地址赋值给指针变量p,即p指向a*/
b = *p; /*将指针p指向变量的值赋值给变量b*/
指针变量p指向变量a时,可以用*p引用变量a,或者说*p与指针变量p所指向的变量a等价,注意下面说法是正确的:
(1)*p和a都引用a的内容。
(2)p和&a都引用a的地址。
2. 指针的赋值运算
C语言允许指针变量存储任一变量的地址,但要注意变量的类型。若有以下定义:
int i, j, arr[5];
int *p, *p1 = &i;
若要将变量j的地址赋给指针变量p,可用以下方式:
p = &j;
若要将数组arr的起始地址赋给指针变量p,可用以下方式:
p = arr; /*或 p = &arr[0],数组名就是元素arr[0]的地址*/
若要将数组arr的某个元素的地址赋给指针变量p,可用以下方式:
p = &arr[2];
也可以将一个指针变量的值赋值给另一个指针变量,此时,两个指针指向同一个变量:
p = p1;
需要注意的是,不能用一个常量(除0外)或一个非地址表达式来给指针变量赋值。下列指针赋值的表述方式都是非法的:
p = 67; /*不允许用一个常量来给指针变量赋值*/
p = i + 5; /*不允许用一个非地址表达式来给指针变量赋值*/
3. 指针的算术运算
指针变量指向数组的元素时,允许指针变量进行下面的算术运算:
(1)++(自增)、--(自减)。
(2)加、减整型数据。
(3)两个指针相减。
此处补充一个知识点:
i++ & ++i
i++:先引用,后增加
++i:先增加,后引用
换句话说就是:
i++:先在 i 所在的表达式中使用 i 的当前值,再让 i 加1
++i:先让 i 加1,再在 i 所在的表达式中使用 i 的新值
指针变量加、减整型数据实质上是一种地址运算。例如:
int a[6] = {0, 1, 2, 3, 4, 5};
int *p;
p = a; /* 指针变量p指向数组的第一个元素a[0],*p的值是0 */
p++; /* 指针变量加1,指针指向数组的下一个元素a[1],*p的值是1 */
p += 3; /* 指针变量加3,指针指向数组的第五个元素a[4],*p的值是4 */
p--; /* 指针变量减1,指针指向数组的上一个元素a[3],*p的值是3 */
注意,不同类型的指针,移动的字节数是不同的,char型移动一个元素的实际距离为1字节,int型为2字节,float型为4字节,double型为8字节。
两个指针变量只有在一定条件下才可以做减法运算。即,这两个指针变量指向同一个数组中的元素。当两个指针变量指向同一数组时,它们的差值即为两个指针变量相对移动的元素个数。
4. 指针变量比较运算
指向同一个数组的两个指针变量,可以进行关系运算。
假设p1、p2是指向同一数组的两个指针变量,p1指向的数组元素在p2指向的元素之前,则表达式 “p1<p2” 的值为1(真),否则为0(假)。当p1、p2指向数组的同一元素时,p1 == p2为真,否则为假。
特别需要注意,在一个不指向任何数组元素的指针上执行算术运算会导致未定义的行为,此外,只有两个指针指向同一个数组时,它们的相减运算和比较运算才有实际意义。
【例】分析下列程序的输出结果
#include<stdio.h>
void main()
{
char a[5] = "1234";
char *p;
p = a; /* 指针变量p指向数组的第一个元素a[0] */
printf("%d\n", *p); /* 输出第一个元素的ASCII值 */
p++; /* 指针变量p后移一位,指向数组的第二个元素a[1] */
printf("%d\n", *p); /* 输出第二个元素的ASCII值 */
}
运行结果:
49
50
【例】分析下列程序的输出结果
#include<stdio.h>
void main()
{
char a[5] = "1234";
int *p;
p = a;
printf("%d\n", *p);
p++;
printf("%d\n", *p);
}
运行结果:
12849
13363
该例子中,错误地将基类型是int型指针变量p指向了char型的数组,导致程序输出“异常”的数据。
未完待续……欢迎大家提问或提出建议!