指针与数组

我们的讨论以下面的定义为基础。

int a = 9;
int b[3] = { 0, 1, 2 };

每个变量都有一个确切的内存地址,&a可以取到变量a的地址。现在将这个规则应用到数组上思考一下:首先,数组中的任意元素都是可以存储数据的,这表示每个元素都有内存空间,有空间就能取得地址,所以,&b[0]操作肯定可以取得一个地址;再次从数量上思考,b是数组,存储的是同一类型的多个变量,那&b应该是哪个元素的地址呢?

想像你面前有一摞盘子,从顶上取出一个总比从中间或底部更便利。语言设计同样也需要考虑操作上的便利性,如果&b代表的是第二个元素b[1]的地址,那访问邻近的元素就需要加减某个偏移量,徒增加脑力负担。另外,当数组大小为1时,越界访问也是不确定性的行为,据此我们暂时可以推断出,数组名与数组第一个元素有关,这样能方便连续访问。

#include <stdio.h>

int main()
{
    int a[] = {1, 2, 3};

    // 使用%x以16进制输出。
    printf("sizeof(int)=%x\n\n", sizeof(int));

    // 数组名是一个地址,a与&a指向的是同一个地址。
    printf(" a    = 0x%p\n", a);
    printf("&a    = 0x%p\n\n", &a);

    printf("&a[0] = 0x%p\n", &a[0]);    // 数组名所持有的地址与第一个元素的地址相等
    printf("&a[1] = 0x%p\n", &a[1]);    // 第二个元素的址址,与第一个元素有4个地址的偏移。
    printf("&a[2] = 0x%p\n", &a[2]);    // 同上

    return 0;
}

glimix.com

从最后三行输出可以看到:数组元素在内存中是连续存储的,这也是数组支持随机访问(如a[1], a[5], a[9])的原因。数组名所持有的地址与第一个元素的地址相同,这代表数组名是整个数组元素的起始地址。但是请注意,&a与a的地址虽然一样,但它们在指针运算上的的意义是完全不一样的!

ZYF

指针+1

数组与数组之间不支持赋值操作,当需要拷贝一个数组时,通常需要使用循环逐元素复制。

/**
 * 拷贝数组src到dst中,这里假设源与目标的大小一样
 * 数组参数以 int *a 替代之前的 int a[]
 */
void copy_array(int *dst, int *src, int count)
{
    for (int i = 0; i < count; i++)
        dst[i] = src[i];
}

在函数copy_array中,我们使用下标运算符完成拷贝操作;数组传参到函数中时会退化为指针类型,指向第一个元素的地址,因此我们也可以使用间接访问的方式修改值。当需要移动到下一个元素指,只需将指针+1即可。

void copy_array(int *dst, int *src, int count)
{
    for (int i = 0; i < count; i++)
    {
        *dst = *src;    // 拷贝
        dst += 1;       // 目标位置移动到下一个位置
        src++;          // 源位置移动到下一个位置
    }
}

指针支持一些简单的算术运算,对于像数组这种连续存储元素的数据类型,假设p指向其中某个元素,+1指向下一个元素,+2指向下下一个元素,-1、-2则与之相反。下面的示例程序使用这种特性,对每个元素进行变换。

void transform(int *a, int count)
{
    for (int i = 0; i < count; ++i)
    {
        //   a+i :数组的第i个元素的址址。
        // *(a+i):由于(*)号优先级高,使用括号保证先偏移地址。

        *(a + i) = *(a + i) * 2;    // 每个元素扩大2倍
        *(a + i) *= 2;              // 使用复合赋值运算符再扩大2倍

        // 同等效果的下标实现
        // a[i] = a[i] * 2;
        // a[i] *= 2;
    }
}

接下来,我们使用指针运算,编写一个输出函数。

void print_array(int *arr, int count)
{
    for (int i = 0; i < count; i++)
    {
        printf("%d ", *arr);
        ++arr;
    }

    printf("\n");
}

最后是主程序,把这些功能驱动起来。

#define ARRAY_SIZE 5

int main()
{
    int a[] = { 1, 2, 3, 4, 5 };
    int b[ARRAY_SIZE];

    printf("copy array: ");
    copy_array(b, a, ARRAY_SIZE);
    print_array(b, ARRAY_SIZE);

    printf("transform: ");
    transform(b, ARRAY_SIZE);
    print_array(b, ARRAY_SIZE);

    return 0;
}

glimix.com

ZYF

练习

1 使用指针与下标两种方式,对一个数组每隔2个元素进行数值变换。

2 编写一些函数,尝试将-1操作应用到指针。

陕ICP备2025078817号-1 陕公网安备61011202001108号