【问题标题】:Requires help in understanding 2d array and passing it in functions需要帮助理解二维数组并将其传递给函数
【发布时间】:2019-06-18 02:40:16
【问题描述】:

首先,我浏览了这些链接以更好地帮助我理解多维数组以及如何在函数中传递它们
https://stackoverflow.com/a/17569578/10452758
http://c-faq.com/aryptr/pass2dary.html
https://jameshfisher.com/2016/12/08/c-array-decaying/

关于这些事情我心里还是有一些不清楚的地方,想问问

假设我们有一个像这样的二维数组

int mat[][COLS] = {{...}}

i) 我们知道,如果mat 的大小是固定的,即在编译时已知为int (*mat)[COLS],我们可以将其传递给函数。 在这里,mat 被称为指向整数数组的单个指针,即它指向整个整数数组,那么上面定义的 main 函数中的 mat 将被称为相同,即指向整数数组的单个指针还是根本不是指针???但是将mat 打印到控制台会给出一个地址,所以基本上它是一个指针。
int arr[] = {}的情况,我们说arr是第一个元素的地址,mat可以说什么。

我理解 b/w matmat[0] 的区别,但是为什么指向整个数组的指针和指向数组第一个元素的指针具有相同的值。在取消引用指针的情况下,如何知道它是指向第一个元素或整个数组的指针???
我知道这个问题听起来有点含糊,但我只是想知道为什么matmat[0] 指向内存的相同地址,因为如果它们表示不同的东西,它们不会有不同的值吗???

如果有人可以分享他们对mat 所指的指针的了解,那将非常有帮助。 我已经阅读了关于这些主题的其他 SO 问题,但这些事情仍然没有引起我的注意。

ii) 我们知道 C 中的数组基本上不是指针,但是当我们访问数组的值时,它会衰减为指针类型的值。所以基本上我想问的是int arr[] = {...}arr 是否不是指针,我想用sizeof(arr)!=size_occupied_by_pointer 支持它,当我们定义mat 并且当我们在控制台中打印它们时也是如此衰减的指针值被引用。这是第一个问题的答案???

iii) 我的其他疑问是将矩阵作为指针传递给指针。 这是我倾向于遵循的代码,但会导致分段错误。如果有人能指出错误 -

void process_pointer_to_pointer(int **arr, size_t rows, size_t cols){
    for (size_t i = 0; i < rows; ++i){
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << arr[i][j] << '\t';
        std::cout << std::endl;
    }
}

int main(int argc, char const *argv[]){
    const int m=4, n=4;

    int mat[][n]= {
        {1, 3, 1, 5},
        {2, 2, 4, 1},
        {5, 0, 2, 3},
        {0, 6, 1, 2}
    };
        int *ip = &mat[0][0];
    process_pointer_to_pointer(&ip, m, n);

我知道通过使用指针数组并用 mat[i] 填充指针数组来做同样事情的更简单方法,但我遵循此方法的限制并从此处尝试此方法
http://c-faq.com/aryptr/pass2dary.html
但没有得到想要的结果...

PS:这个问题可能完全无处不在,所以如果不能理解特定的东西,请问我。我很高兴进入辩论模式。另外,我是编码新手,所以请让我放松一下。 :p

【问题讨论】:

  • geeksforgeeks.org/pointer-array-array-pointer。这对我帮助很大,但仍然不明白为什么 mat 和 mat[0] 具有相同的地址值??
  • int **arr 在传递二维数组时不正确。数组不会衰减为 2D 指针。我的解决方案是不使用原始二维数组。你可以使用std::array吗?另一种选择是提供一维数组和一个将二维索引转换为一维索引的函数 (row*numberColumns + column)。
  • @user4581301 我知道将指针传递给指针是不正确的,但你可以参考stackoverflow.com/a/17569578/10452758的第四点
  • mat 是数组开头的地址。 mat[0] 也是数组开头的地址,因为第一个元素从数组的开头开始。
  • matmat[0]mat[0][0]的地址相同。它们是矩阵中第一个元素的地址。但是,每种类型的类型不同。

标签: c++ arrays pointers multidimensional-array


【解决方案1】:

你有几个选择。实际上创建一个指向每一行的指针数组,或者计算矩阵的偏移量并将其视为一维整数数组。前者是 30 多年前的做法,当时乘法比额外的内存访问要慢。如果您将arr 声明为int **arr,则必须这样做

#include <iostream>
using std::cout;

void process_pointer_to_pointer(int** arr, size_t rows, size_t cols) {
    for (size_t i = 0; i < rows; ++i) {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << arr[i][j] << '\t';
        std::cout << std::endl;
    }
}

int main(int argc, char const* argv[]) {
    const int m = 4, n = 4;

    int mat[][n] = {
        {1, 3, 1, 5},
        {2, 2, 4, 1},
        {5, 0, 2, 3},
        {0, 6, 1, 2}
    };

    int* pRows[m];
    for (int i = 0; i < m; i++)
        pRows[i] = mat[i];

    // pRows decays to a pointer to a pointer to an int
    process_pointer_to_pointer(pRows, m, n);
}

现在对可变大小数组所做的通常是计算线性数组的偏移量,通常实例化为vector,但这里我们使用您的基数。

#include <iostream>
using std::cout;

void process_pointer_to_pointer(int* arr, size_t rows, size_t cols) {
    for (size_t i = 0; i < rows; ++i) {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << arr[i*cols+j] << '\t';
        std::cout << std::endl;
    }
}

int main(int argc, char const* argv[]) {
    const int m = 4, n = 4;

    int mat[][n] = {
        {1, 3, 1, 5},
        {2, 2, 4, 1},
        {5, 0, 2, 3},
        {0, 6, 1, 2}
    };
    // mat[0] decays to a pointer to int and points to the first int in mat
    process_pointer_to_pointer(mat[0], m, n);
}

对于具有恒定行/列的二维数组,首选方法是使用std::array。例如:

std::array<std::array<int,4>,4> mat;
mat[2][3]=42;

这允许访问很像传统的 C 类型数组并且同样高效。

另请参阅这个将二维数组包装在类中的示例,该类仍允许使用括号进行常规访问。

Statically declared 2-D array C++ as data member of a class

【讨论】:

    【解决方案2】:

    让我们尝试一些小程序:

    #include <stdio.h>
    #include <stdint.h>
    int a1[3];
    int a2[3][3];
    int a3[3][3][3];
    
    void showptr(void *a, void *b) {
        printf("%p, %p, %zd\n", a, b, (intptr_t)b - (intptr_t)a);
    }
    
    int main() {
            showptr(a1, a1+1);
            showptr(a2, a2+1);
            showptr(a2[0], a2[0]+1);
            showptr(a3, a3+1);
            showptr(a3[0], a3[0]+1);
            showptr(a3[0][0], a3[0][0]+1);
            return 0;
    }
    

    上面定义了三个数组,如果增加维数,并打印出一些关于它们的值。第一个,(a1, a1+1),表明将 1 加到“a1”会使地址增加 4,这是一个 int 的大小。接下来的两个对 a2 和 a2[0] 执行相同的操作。 A2,结果增加了 12(3 个整数),这是数组的一行。 A2[0],以 4 (1 int) 递增。当我们到达 a3 时,它增加 36(3 行),a3[0] 增加 12(3 个整数,一行),a3[0][0] 增加 4。

    现在,让我们在 main 结尾处再添加一些:

    ... 诠释*t; t = a1;显示点(t,t+1); t = a2;显示点(t,t+1); t = a2[0];显示点(t,t+1); t = a3;显示点(t,t+1); t = a3[0];显示点(t,t+1); t = a3[0][0];显示点(t,t+1); ...

    这里发生的事情很有趣,编译器抱怨:

    a.c:20:4: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
    

    这在第 22 和 23 行重复:

    t = a2;
    t = a3;
    t = a3[0];
    

    右侧的表达式都被认为与指针类型不兼容。 C(以及在某种程度上 c++)允许地址表达式,您可以在其中对地址类型(指针)执行有限的算术运算。撇开这些优点不谈,这就是为什么一些数组表达式和指针表达式重叠的原因,它们都在处理相同的基本数据类型——地址。因为地址表达式通常是无情的,所以您应该仔细检查编译器警告;他们随时为您提供帮助,无论如何都会生成代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-01
      相关资源
      最近更新 更多