【问题标题】:When passing 2d array to function what is the difference between single pointer and double pointer?将二维数组传递给函数时,单指针和双指针有什么区别?
【发布时间】:2015-02-24 08:35:48
【问题描述】:

我们可以将二维数组作为单指针和双指针传递。但在第二种情况下,输出不如预期。那么第二个代码有什么问题呢?

方法一:

#include <stdio.h>
void print(int *arr, int m, int n)
{
    int i, j;
    for (i = 0; i < m; i++)
      for (j = 0; j < n; j++)
        printf("%d ", *((arr+i*n) + j));
}

int main()
{
    int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
    int m = 3, n = 3;
    print((int *)arr, m, n);
    return 0;
}

输出:

1 2 3 4 5 6 7 8 9

方法二:

#include <stdio.h>
void print(int *arr[], int m, int n)
{
    int i, j;
    for (i = 0; i < m; i++)
      for (j = 0; j < n; j++)
        printf("%d ", *((arr+i*n) + j));
}

int main()
{
    int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
    int m = 3;
    int n = 3;
    print((int **)arr, m, n);
    return 0;
}

输出:

1 3 5 7 9 3 0 -1990071075 0

【问题讨论】:

  • “我们可以将二维数组作为单指针和双指针传递” - 您可以将它作为四元指针传递给char。这并不意味着它是一个好主意,或定义的要利用的行为(它不是,也不是)。对int** 的硬性转换以及没有它的代码编译失败应该暗示你会以这种方式出现一些邪恶的事情。 int** 是指向 int 的指针的指针 arr 到作为表达式的指针的合法转换是 int (*)[3]。这些类型不等价。数组不是指针,不管你可能被引导相信什么。
  • C 中的一条经验法则是避免使用二维数组。只需使用一维数组并以a[i*width+j] 的身份访问它...
  • 但是@BasileStarynkevitch 在我的项目中我必须将二维数组传递给函数,所以我无法避免二维数组
  • 您可以始终避免使用 2D 数组:它们不存在于硬件中,而 C 是(或曾经是)一种旨在接近硬件的语言。跨度>
  • 我在第一条评论中已经给出了。

标签: c arrays function pointers


【解决方案1】:

第一个是未定义的行为:Accesing a 2D array using a single pointer

第二个完全错误,不能将二维数组(arr[][3])传递给int指针数组(*arr[]),看看Correct way of passing 2 dimensional array into a function

void print(int *arr[], int m, int n)

必须

void print(int arr[][3], int n) /* You don't need the last dimesion */

void print(int (*arr)[3], int n) /* A pointer to an array of integers */

但是这样 arr[][3] 中的列必须是全局定义的。不是吗 还有其他解决方法吗?

在 C99 下你可以使用 VLA(可变长度数组):

void print(int rows, int cols, int arr[rows][cols])

【讨论】:

  • 如果我错了,请纠正我,但在我看来,他将矩阵视为没有实际索引的线性化数组(计算索引),我相信这是完全合法的(第一个案例)。
  • @PankajMahato,在C99下可以使用VLA's
  • 第一个是正确的;您误解了您链接到的文章。 (int *)arr 表示我们正在遍历所有 arr;另一个问题以&amp;a[0][0] 为特征,这意味着我们只迭代a[0]
  • @PankajMahato 不,它没有阅读链接标准中的第一段。你可以摆脱它,但你不应该这样使用它。
  • @MattMcNabb, If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.
【解决方案2】:

Alter Mann 是对的,但是方法 2 的主要问题是这段代码:

*((arr+i*n) + j)

因为arr 现在是int *arr[] 的类型,所以元素大小是sizeof(int *) 而不是sizeof(int) 和第一种情况一样。所以当 f.e. arr = 0,然后arr + 1 等于0 + sizeof(int*),而不是第一种情况下的0 + sizeof(int)。如果将arr 转换为(int*) 就像这样:

*(((int *)arr+i*n) + j)

TL;DR 你是通过指针大小而不是整数大小来跳过数组。

我个人的建议是使用指针,但是像数组一样访问它:

int *arr[];
return arr[i][j];

这每次都有效,不像指针算术可能会影响步长,就像它对你的情况一样。

【讨论】:

  • 只是为了让事情更清楚一点:它是 UB,因为指针运算后的指针类型(来自 OP 的示例)的结果与 int* 不同,因此需要强制转换?
  • 不是UB,只是越界读取。在 X86_64 上,程序读取位置 [0, 8, 16, 24, 32 ...] 而不是 [0, 4, 8, ...] 上的内存,因为步长不同。为了说明这一点,请尝试打印:(int*)NULL + 1(int)0 + 1,看看它们有何不同。
  • 方法 1 中的 *((arr+i*n) + j) 没有任何问题(除了比 arr[i*n+j] 更难阅读。你说的是方法 2 还是什么?
  • 有,就像我对 Alter Mann 的回答(标准)的回应一样。我在说明为什么方法 2 是错误的。
  • 好的,我建议编辑你的答案,说你在谈论方法 2
猜你喜欢
  • 1970-01-01
  • 2021-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-13
  • 2016-02-03
相关资源
最近更新 更多