需要帮助来理解二维数组并将其传递给函数

时间:2019-06-18 02:40:16

标签: c++ arrays pointers multidimensional-array

首先,我通过这些链接来更好地帮助我理解多维数组以及如何在函数中传递它们
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。 在这里,mat被称为指向整数数组的单个指针,即它指向整个整数数组,然后将如上定义的主函数中的mat被称为相同的指针,即单个指针到整数数组还是根本不是指针???但是将int arr[] = {}打印到控制台会给出一个地址,因此基本上,它是一个指针。
arr一样,我们说mat是第一个元素的地址,关于mat可以这么说。

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

如果某人可以分享他们对int arr[] = {...}所指的内容的了解,这将非常有帮助。 我已经阅读了关于这些主题的其他 SO问题,但这些东西仍然没有被我点击。

ii)我们知道C语言中的数组基本上不是指针,但是当我们访问数组的值时,它会衰减为指针类型的值。因此,基本上我想问的是,在arr中,sizeof(arr)!=size_occupied_by_pointer是否不是指针,而我想用mat支持它,在定义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); 时也是如此当我们在控制台中打印它们时,将引用衰减的指针值。这是第一个问题的答案吗?

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

mat[i]

我知道通过使用指针数组并用message填充指针来完成相同操作的更简单方法,但是我遵循了这种方法的局限性,并从这里尝试了这种方法
http://c-faq.com/aryptr/pass2dary.html
但是没有得到想要的结果...

PS:问题可能完全存在,因此,如果无法理解特定内容,请问我。我很高兴进入辩论模式。另外,我是编码的新手,所以请给我一些时间。 :p

2 个答案:

答案 0 :(得分:0)

您有两种选择。实际上创建一个指向每一行的指针数组,或计算矩阵的偏移量并将其视为整数的一维数组。前者是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);
}

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

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

这使得访问非常类似于传统的C类型数组,并且同样有效。

另请参见将2D数组包装在仍允许使用括号进行常规访问的类中的示例。

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

答案 1 :(得分:0)

让我们尝试一些小程序:

#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)表示,在'a1'中添加一个将使地址增加4,即int的大小。接下来的两个对a2和a2 [0]执行相同的操作。 A2,结果是以12(3 ints)的增量递增,这是数组的一行。 A2 [0],递增4(1个整数)。当我们到达a3时,它递增36(3行),a3 [0]递增12(3 ints,一行),而a3 [0] [0]递增4。

现在,让我们在main的末尾添加一些:

...     int * t;     t = a1; showptr(t,t + 1);     t = a2; showptr(t,t + 1);     t = a2 [0]; showptr(t,t + 1);     t = a3; showptr(t,t + 1);     t = a3 [0]; showptr(t,t + 1);     t = a3 [0] [0]; showptr(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 ++)允许使用地址表达式,您可以在其中对地址类型(指针)执行有限的算术运算。除了优点之外,这就是为什么一些数组表达式和指针表达式重叠的原因,它们都在相同的基本数据类型(地址)上工作。由于地址表达式通常是令人难以容忍的,因此您应仔细检查编译器警告。他们在那里可以帮助您,无论如何都会生成代码。