"数组地址"有什么实际用途?

8

免责声明:C++指针是一个非常流行的话题,因此我相信之前已经有人提出了这个问题。然而,我没有找到其他参考资料。如果我错了,请纠正我,并随时关闭这个线程。

我看到很多例子区分指向数组第一个元素的指针和指向整个数组本身的指针。下面是一个程序及其输出:

//pointers to arrays
#include <iostream>
using namespace std;

int main() {
    int arr[10]  = {};
    int *p_start = arr;
    int (*p_whole)[10] = &arr;

    cout << "p_start is " << p_start <<endl;
    cout << "P_whole is " << p_whole <<endl;

    cout << "Adding 1 to both . . . " <<endl;

    p_start += 1;
    p_whole += 1;

    cout << "p_start is " << p_start <<endl;
    cout << "P_whole is " << p_whole <<endl;

    return 0;
}

输出:

p_start is 0x7ffc5b5c5470
P_whole is 0x7ffc5b5c5470
Adding 1 to both . . . 
p_start is 0x7ffc5b5c5474
P_whole is 0x7ffc5b5c5498

所以,不出所料,对两个数都加1会得到不同的结果。但我不知道像 p_whole 这样的东西有什么实际用途。一旦我拥有整个数组块的地址,这可以使用 arr 来获取,那么我可以用这样的指针做什么呢?


在C++中,没有必要使用指针,因为你可以使用数组的引用。数组的引用非常有用(例如,根据其名称获取数组维度的规范实现)。 - M.M
2个回答

11

对于单个数组,我认为没有太多意义。但在多维数组中,指针变得非常有用,多维数组是由数组组成的数组。指向其中一个子数组的指针是行的指针,将其递增可获取到下一行的指针。相反,指向内部数组的第一个元素的指针是指向单个元素的指针,将其递增可获取到下一个元素。


1
@dostlash 和分配。如果您看到有人称呼 int ** 为“二维数组”,请纠正他们,并告诉他们改用 malloc(COLS * sizeof(int[ROWS]))(其中基本指针类型将为 int (*)[ROWS])。使用连续内存,节省缓存! - The Paramagnetic Croissant
@TheParamagneticCroissant 感谢您的提示!不过我没明白“保存缓存”的部分,请详细解释一下。 :) - ankush981
1
使用@dotslash分配一个连续的内存块(可以被视为真正的2D数组),而不是使用指向指针的指针,有多个好处。第一个好处是缓存局部性:连续的内存片段是连续的。系统将能够识别这一事实并轻松缓存它,因此访问速度更快(而访问独立的内存块可能会导致缓存未命中)。其次,访问数组元素将需要较少的间接引用(一个指针解除引用而不是两个)。第三,它将需要较少的分配和释放(1与1 + COLS相比)。 - The Paramagnetic Croissant
@TheParamagneticCroissant,它减少了更少的间接引用,但它用乘法替换了它们,因为必须将行索引乘以列数才能获得行偏移量。但是你关于局部性的想法是对的,尽管如果你在循环中分配所有行,大部分行可能会成为相邻的。因此,最好根据应用程序设计来决定2-D vs指针数组,否则这是过早的优化。 - Barmar
@Barmar 因为一个二维数组也比一堆指针简单(创建和管理),而且更能传达意图,所以我认为从一开始就使用它是没有理由的。在开始阶段,这不是关于优化,而是关于防御性编程。(当然,如果需要不同的语义,那就是另外一个问题了。)- 但是乘法真的是一个大问题吗?在现代CPU上,未缓存的内存访问比乘法慢得多(特别是如果乘数是2的幂,因为它经常是这样)。 - The Paramagnetic Croissant
显示剩余3条评论

2

int (*)[10] 是比 int* 更"强壮"的类型,因为它保留了数组的大小, 所以您可以将其传递给函数而无需传递额外的大小参数:

void display(const int(*a)[10]) // const int (&a)[10] seems better here
{
    for (int e : *a) {
        std::cout << " " << e;
    }
}

对比

void display(const int* a, std::size_t size) // or const int* end/last
{
    for (std::size_t i = 0; i != size; ++i) {
        std::cout << " " << a[i];
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接