参考资料:
《全国计算机等级考试二级教程——C语言程序设计》
《21天学通C语言》
变量的地址和指针
在程序中,一个变量实际上是代表了内存中的若干个存储单元。在C程序中声明一个变量时,编译器会预留一个内存位置来存储该变量,此位置有唯一的地址。一般情况下,我们在程序中只需要指出变量名,无须知道每个变量在内存中的具体地址。编译器把地址和变量名相关联,当程序使用了该变量名时,将自动访问正确的内存位置。
在C语言中,还可以定义一种特殊的变量,这种变量只是用来存放内存地址的。这种用来存放内存地址的变量称为“指针变量”。
指针变量的定义和指针变量的基类型
声明指针
定义指针变量的一般形式如下:
类型名 *指针变量名1, *指针变量名2……;
例如:
int *pi, *pj;
以上定义语句中,pi和pj都是用户标识符,在每个变量前的星号“*****”是一个说明符,用来说明该变量是指针变量。
注意:变量前的星号不可省略。
int是类型名,说明了pi和pj是两个指向整型(int类型)变量的指针,也就是说pi和pj中只能存放int类型变量的地址,这时称int是指针变量pi和pj的基类型。
初始化指针
必须在程序中使用取址运算符“&”获得变量的地址,然后将其存入指针。
因此,使用下面的形式初始化指针:
指针 = &变量;
指针未初始化之前,未指向任何内容。初始化之后,该指针是指向变量的指针。
给指针变量赋值
给指针变量赋地址值
通过求地址运算符(&)获得地址值
单目运算符&用来求出运算对象的地址。
若有如下定义:
int k = 1, *q, *p;
则赋值语句
q = &k;
把变量k的地址赋予了q。
这时可以说:p指向了变量k。
求地址运算符&只能应用于变量和数组元素,不可以用于表达式、常量或者被说明为register的变量。另外,&必须放在运算对象的左边,且运算对象的类型必须与指针变量的基类型相同。
通过指针变量获得地址值
可以通过赋值运算,把一个指针变量中的地址值赋给另一个指针变量,从而使这两个指针变量指向同一个地址。例如,若有以上定义,则语句
p = q;
使指针变量p中也存放了变量k的地址,也就是说p和q都指向了变量k。
注意:当进行赋值运算时,赋值号两边指针变量的基类型必须相同。
通过变量名访问变量的内容,称为直接访问,通过指向变量的指针访问变量的内容,称为间接访问或间接取值。
假设声明一个名为ptr的指针,已将其初始化为指向var变量,以下的说法都正确:
- *ptr和var都引用var的内容(程序储存在该位置的任何值)
- ptr和&var都引用var的地址
因此,不带间接运算符的指针名访问指针本身存储的值。
//指针的基本用法示例
#include <stdio.h>
int var = 1; //声明并初始化一个int类型的变量
int *ptr; //声明一个指向int类型变量的指针
int main(void)
{
ptr = &var; //让ptr指向var
//直接和间接访问var
printf("\nDirect access, var = %d", var);
printf("\nIndirect access, var = %d", *ptr);
//以两种方式显示var的地址
printf("\nThe address of var = %p", &var);
printf("\nThe address of var = %p\n", ptr);
return 0;
}
输出:
Direct access, var = 1
Indirect access, var = 1
The address of var = 0040A028
The address of var = 0040A028
通过标准函数获得地址值
可以通过调用库函数malloc和calloc在内存中开辟动态存储单元,并把开辟的动态存储单元的地址赋给指针变量。
给指针变量赋“空”值
可以给指针变量赋NULL值。例如:
p = NULL;
NULL的代码之为0,当执行了以上的赋值语句后,称p为空指针。
以上语句与以下语句等价:
p = '\0';
p = 0;
这时,指针p并不是指向地址为0的存储单元,而是具有一个确定的值——“空”。
对指针变量的操作
通过指针来引用一个存储单元
单目运算符“*”称为“间接访问运算符(也称间址运算符)”。
当指针变量中存放了一个确切的地址值时,就可以使用间接访问运算符通过指针来引用该地址的存储单元。
int *p, i = 10, j;
p = &i;
j = * p; //这条赋值语句把p所指的存储单元(i)的内容(整数10)赋予变量j,这里* p代表p所指的变量i
间址运算符是一个单目运算符,必须出现在运算对象的左边,其运算对象或是存放地址的指针变量,或是地址。
例如:
j = * (&i);
以上赋值语句表示取地址&i中的内容赋予j。
j = * p + 1;
以上语句取指针变量p所指存储单元中的内容加1后赋予变量j。
注意:用指针来引用存储单元时,当* p 出现在赋值号的左边时,代表的是指针所指的存储单元;当* p出现在赋值号的右边时,代表的是指针所指的存储单元的内容。
移动指针
移动指针就是对指针变量加上或减去一个整数,或通过赋值运算,使指针变量指向相邻的存储单元。
因此,只有当指针指向一串连续的存储单元时,指针的移动才有意义。
当指针指向一串连续的存储单元时,可以对指针变量进行加上或减去一个整数的运算,也可以对指向同一串连续存储单元的两个指针进行相减运算。除此之外,不能对指针进行任何其他的算术运算。
在对指针进行加、减运算时,数字“1”是指1个存储单元长度。1个长度占多少字节的存储空间视指针的基类型而定。增1表示指针向地址大(高地址)的方向移动一个存储单元,减1表示指针向地址值小(低地址)的方向移动一个存储单元。移动指针时,系统会根据指针的类型自动地来确定移动的字节数。
指针比较
在关系表达式中可以对两个指针进行比较。例如,p和q是两个指针变量,以下语句完全正确:
if (p < q)
printf("p points to lower memory than q.\n");
if (p == '0')
printf("p points to Null.\n");
通常,两个或多个指针指向同一目标(如一串连续的存储单元)时,比较才有意义。
指针运算
下表为可用于指针的所有操作:
运算 | 描述 |
---|---|
赋值 | 可以给指针赋值 |
间接取值 | 间接运算符返回存储在指针所指向位置上的值(通常称为解引用) |
取址 | 可以用取址运算符找到指针的地址,因此,有指向指针的指针 |
递增 | 给指针加上一个整数,使其指向不同的内存位置 |
递减 | 给指针减去一个整数,使其指向不同的内存位置 |
求差 | 将两个指针相减,得出两者的间距 |
比较 | 只有指向相同数组的两个指针才能进行比较 |
函数之间地址值的传递
形参为指针变量时实参和形参之间的数据传递
若函数的形参为指针类型,调用该函数时,对应的实参必须是基类型相同的地址值或者是已经指向某个存储单元的指针变量。
例1:
编写函数myadd (int *a, int *b),函数中把指针a和b所指的存储单元中的两个值相加,然后将和值作为函数值返回。在主函数中输入两个数给变量,把变量地址作为实参,传送给对应形参。
#include <stdio.h>
int myadd (int *a, int *b)
{
int sum;
sum = *a + *b;
return sum;
}
int main (void)
{
int x, y, z;
printf("Enter x, y:");
scanf("%d%d", &x, &y);
z = myadd(&x, &y);
printf("%d + %d = %d\n", x, y, z);
return 0;
}
在此程序中,main函数调用myadd函数时,系统为myadd函数的形参a和b开辟两个基类型为int类型的临时指针变量,并通过实参&x、&y把x和y的地址传给它们,使指针a指向变量x,指针b指向变量y。在myadd函数中,语句sum = *a + *b的含义是:分别取指针a和b所指存储单元中的内容,相加后存入变量sum中。
通过传送地址值在被调用函数中直接改变调用函数中的变量值
通过传送地址值,可以在被调用函数中对调用函数中的变量进行引用,这也就使得通过形参改变对应实参的值有了可能,利用此形式可以把两个或两个以上的数据从被调用函数返回到调用函数。
例2:
调用swap函数,交换主函数中变量x和y中的数据。
#include <stdio.h>
void swap (int *, int *);
int main (void)
{
int x = 30, y = 20;
printf("(1) x = %d, y = %d\n", x, y);
swap(&x, &y);
printf("(4) x = %d, y = %d\n", x, y);
return 0;
}
void swap (int *a, int *b)
{
int t;
printf("(2) a = %d, b = %d\n", *a, *b);
t = *a;
*a = *b;
*b = t;
printf("(3) a = %d, b = %d\n", *a, *b);
}
输出:
(1) x = 30, y = 20
(2) a = 30, b = 20
(3) a = 20, b = 30
(4) x = 20, y = 30
由于没有通过return语句返回函数值,所以定义swap函数为void类型。
由此例可见,C程序中可以通过传送地址的方式在被调用函数中直接改变调用函数中的变量的值,从而达到函数之间数据的传递。
函数返回地址值
函数值的类型不仅可以是简单的数据类型,也可以是指针类型。
例3:
以下函数把主函数中变量i和j中存放较大数的那个地址作为函数值传回。
#include <stdio.h>
int *fun (int *, int*);
int main (void)
{
int *p, i, j;
printf("Enter two number:");
scanf("%d%d", &i, &j);
p = fun(&i, &j);
printf("i = %d, j = %d, *p = %d\n", i, j, *p);
return 0;
}
int *fun (int *a, int *b)
{
if (*a > *b)
return a;
return b;
}