Skip to content

第六部分:指针初步


一、指针概述

要讲述指针,首先要讲到数据在内存中的存储方式。在 C 编译系统中,系统为整形数据分配 4 字节内存空间,为字符型数据分配1字节内存空间。而内存区每个字节都有一个编号,这个编号就是这个字节的地址。由于通过地址可以找到存放这个变量的位置,所以说这个地址指向这个变量,通常我们称这个地址是变量的指针。

例如我们有如下代码:

c
int a = 5;

变量 a 在内存中的存放情况示意图如下:

alt text

其中,这个变量占用 4 个字节的内存空间,如果有下一个变量被声明,地址号则是从 2004 开始。地址不一定是从 2000 开始,这里只是示意。如果我们声明了一个整型数组,则可以更好地理解整形数据占用 4 个字节的问题:

c
int a[4] = {1, 2, 3, 4};
地址变量名变量内容
2000a[0]1
2004a[1]2
2008a[2]3
2012a[3]4

当我们用 scanf 输入数据的时候需要用到 '&' 取地址符,例如:

c
int a;
scanf("%d", &a);

这个时候,系统将键盘输入的值送入 a 的地址指向的内存单元。如果有语句通过变量名直接调用这个变量,这叫做变量的“直接访问”。如果通过地址来访问变量的内容,这就叫做“间接访问”,这个时候就需要用到指针变量。

我们在 C 语言中定义过很多类型的变量,例如整形、浮点型、字符型,还有一种特殊的变量类型可以用来存放其他变量的地址,叫做指针变量,标识符是一个星号(*)。例如有指针变量 p,我们通过如下语句将变量 a 的地址存放给 p

c
p = &a;

这个时候变量 ap 在内存中的关系如下图:

alt text

可以看到,p 本身是一个变量,它存放的内容是 a 的地址,p 的内容指向变量 a。这个时候称 p 是指向 a 的指针变量。这样看来,指针变量就是可以存放其他变量地址的变量。

二、定义与引用指针

定义指针变量的一般形式为:

类型名 *指针变量名;

例如:

c
int *p;

通过下面这个例子可以更加详细理解指针变量:

c
#include<stdio.h>
int main()
{
    int a = 2, b = 3;
    int *p1, *p2;
    p1 = &a;
    p2 = &b;
    printf("a = %d , b = %d\n" , a , b);
    printf("*p1 = %d , *p2 = %d\n", *p1, *p2);
    return  0;
}

运行结果是:

a = 2 , b = 3
*p1 = 2 , *p2 = 3

定义指针后,要告诉系统这个指针指向的变量是谁,例如上例中的 6、7 行。给指针赋值后就可以通过指针来访问指针指向的变量了,在第 9 行,通过 * 号和指针变量名来代表这个指针变量指向的内容。

指针变量同时可以作为函数的参数,例如下面的例程,通过函数交换两个变量的值:

c
#include<stdio.h>
void swap(int *p1, int *p2)
{
    int temp;
    temp = *p1;
    *p1 = *p2;
    *p2 = temp;
}

int main()
{
    void swap(int *p1, int *p2);
    int *point1, *point2;
    int a = 1 , b = 2;
    point1 = &a;
    point2 = &b;
    printf("a = %d , b = %d\n", *point1, *point2);
    swap(point1, point2);
    printf("a = %d , b = %d\n", *point1, *point2);
    return 0;
}

可以思考一个问题,main 函数中两个指针变量的指向改变了没有?

三、指针与数组

我们已经知道,每个变量都有它自己的地址,由于数组是多个同类型的变量,所以每个数组元素都有它自己的地址,且在一个数组内的所有元素地址是连续的。数组元素的指针就是数组元素的地址。当我们想引用数组元素,可以用下标引用,这是数组元素的直接引用。通过指针引用数组是数组元素的间接引用。

特别地,C 语言中数组名代表数组首元素的地址。例如下面第 3、4 两行代码等价:

c
int *p;
int a[10];
p = a;
p = &a[0];

当指针指向数组元素的时候,对指针的运算概念与我们之前提到的四则运算有所不同。指针加一个整数 n 时,代表指针向前移动 n 个元素,减则为向后移动。同样也可以进行自加和自减运算。当两个指针都指向同一个数组中的元素时,两个指针相减则表示两个指针相隔的距离。例如:

c
#include<stdio.h>
int main()
{
    int *p;
    int a[10];
    int i;
    p = a;    //这行可以等价为  p = &a[0] 或*p = a[0]
    for(i = 0; i < 10; i ++)
        a[i] = i;
    for(i = 0; i < 10; i ++, p ++)
        printf("%d", *p);
    return  0;
}

特别地,当指针指向数组元素进行加减时,指针加一不是将 p 的值(地址)简单地加一,而是加上一个指针类型所占用内存大小的字节数。例如我们定义指针和数组如下:

c
char a[] = "WELCOME";
int *p;
p = a;

注意到数组类型和指针类型不同,但我们将指针指向数组首元素。这个时候如果:

c
printf("%c" , *p);
p ++;
printf("%c" , *p);

第 1 行会输出字符数组的首字符 'W',但经过第 2 行指针移动后,第 3 行不会输出第二个字符而会输出第 5 个字符 O。这就是因为整形元素占用 4 个字节,而字符型元素占用 1 个字节。移动指针时按照指针类型移动了 4 个字节,相当于字符数组的 4 个元素的大小。

同时,指针还可以与之前学过的函数结合,例如数组首元素可以当成函数的参数,可以声明指针类型的数组,感兴趣的可以自己查阅参考文献 1 的教材。

四、练习题

  1. 结合之前提到的例程:
c
#include<stdio.h>
void swap(int *p1, int *p2)
{
    int temp;
    temp = *p1;
    *p1 = *p2;
    *p2 = temp;
}
int  main()
{
    void swap(int *p1, int *p2);
    int *point1, *point2;
    int a = 1, b = 2;
    point1 = &a;
    point2 = &b;
    printf("a = %d , b = %d\n", *point1, *point2);
    swap(point1, point2);
    printf("a = %d , b = %d\n", *point1, *point2);
    return 0;
}

思考,如果将 swap 函数改成如下:

c
swap(int x1, int x2)
{
    int temp;
    temp = x1;
    x1 = x2;
    x2 = temp;
}

这个时候为什么不能完成值的交换?

本部分更多的练习将结合下一部分出现。

© thebestxt.cc
辽ICP备16009524号-8
本站所有文章版权所有,转载请注明出处