【问题标题】:C++ syntax difference: 2D- and 1D-arrays (pointer arithmetic)C++ 语法差异:2D 和 1D 数组(指针算术)
【发布时间】:2020-06-23 09:31:35
【问题描述】:

问题

我正在学习 C++,并且正在编写代码来转置二维数组反转一维数组

请查看调用。为什么我必须使用reverse(arr, 4) 进行反向,而我必须使用transpose(*in_matrix, *out_matrix) 进行转置?

每个函数签名有两种编写方式。两者似乎给出了相同的结果。

谢谢。

编辑:我知道如何用数组下标来解决它。我特意这样做是为了练习。现在我明白了尝试这个没有意义。但是,我添加了一些从下面的答案中总结出来的注释。

代码

#include <iostream>
using namespace std;

const int LENGTH = 2;
const int WIDTH = 3;

void printArray(const int arr[], const int N) {
    cout << arr[0]; 
    for (int i = 1; i < N; ++i) {
        cout << ", " << arr[i];
    }
    cout << "\n";
}

// void transpose(int* const input, int* const output) { // both these signatures
void transpose(const int input[], int output[]) {        // works (I find the top one clearer)
    for (int i = 0; i < WIDTH; ++i) {
        for (int j = 0; j < LENGTH; ++j) {
            *(output + j * WIDTH + i) = *(input + i * LENGTH + j);
        }
    }
}

// void reverse(int arr[], const int N) { // both these signatures
void reverse(int* arr, const int N) {     // works (I prefer this one)
    for (int i = 0; i < N / 2; ++i) { 
        int temp = *(arr + i);
        *(arr + i) = *(arr + N - 1 - i);
        *(arr + N - 1 - i) = temp;
    }
}

int main() {
    int arr[4] = {2,4,6,8};
    printArray(arr, 4);
    reverse(arr, 4); // this works
    // reverse(*arr, 4); // this doesn't work
    printArray(arr, 4);
     
    int in_matrix[WIDTH][LENGTH];
    in_matrix[0][0] = 1;
    in_matrix[0][1] = 2;
    in_matrix[1][0] = 3;
    in_matrix[1][1] = 4;
    in_matrix[2][0] = 5;
    in_matrix[2][1] = 6;

    int out_matrix[LENGTH][WIDTH];
    // transpose(in_matrix, out_matrix); // this doesn't work
    transpose(*in_matrix, *out_matrix); // this works

    cout << "in_matrix is:\n";
    for (int i = 0; i < WIDTH; ++i) {
        printArray(in_matrix[i], LENGTH);
    }

    cout << "out_matrix is:\n";
    for (int i = 0; i < LENGTH; ++i) {
        printArray(out_matrix[i], WIDTH);
    }
    return 0;
}

答案摘要

LESSON: DO NOT USE pointer-arithmetic for 2D-arrays

              decay
KEY IDEA: arr -----> &arr[0]        type int*
This is also the reason the two function signatures are equivalent.

        // transpose(int* const input, int* const output) // alt.
Signature: transpose(const int input[], int output[])
i.e. it expects an array of ints (or equiv., a pointer to an int)

             (id)
IDENTITY: a[i] = *(a + i)           ALWAYS TRUE

Reason transpose(in_matrix, out_matrix) doesn't work:
           decay
out_matrix -----> &out_matrix[0]    type int(*)[WIDTH]

Reason transpose(*in_matrix, *out_matrix) works:
           (id)             decay
*out_matrix = out_matrix[0] -----> &(out_matrix[0])[0]

【问题讨论】:

  • 数组可以衰减到指向其第一个元素的指针。例如,arr 将衰减为 &amp;arr[0],其类型为 int*。数组数组也是如此,例如out_matrixout_matrix 也将衰减为指向其第一个元素的指针,即&amp;out_matrix[0],但由于out_matrix 的每个元素都是一个数组,因此类型将是指向数组的指针(对于&amp;out_array[0],该类型具体为int (*)[WIDTH]) .指向数组的指针不能衰减。
  • 另请注意,对于任何数组或指针a和索引i,表达式a[i]完全等于@987654338 @。由此得出*out_array 等于out_array[0],这是一个可以衰减为指针的实际数组。这意味着*out_array 实际上与&amp;((out_array[0])[0]) 相同(添加括号以使其更清晰)。
  • 如果没有cout,我认为是C
  • @Someprogrammerdude 谢谢。这是完全有道理的。你解释得很好。如果你很好奇,问题来自ocw.mit.edu/courses/electrical-engineering-and-computer-science/…(作业2,最后一个问题)。事实上,我读错了这个问题。它没有特别要求使用指针偏移(仅数组下标)实现transpose。经历了这一切,难怪他们没有问这个问题。

标签: c++ arrays pointers reference transpose


【解决方案1】:

在 C 中,数组和指针有点复杂。一个数组可以被认为是一个带有一些“大小”信息的指针(它不存储在任何地方,但编译器知道)。因此,sizeof 在数组上使用时会给出整个数组内容的大小,而在指针上使用时会给出指针的大小。

当您将数组传递给函数时,大小信息会丢失 - 实际上,数组会衰减为指针。对于大多数实际目的,指向类型的指针可以完全像该类型的一维数组一样使用。数组下标符号 ([]) 也可用于使用指针访问连续元素。

但是,对于二维数组,这会变得更加复杂。二维数组和双指针可以使用a[i][j] 形式的相同访问语法,但它们不可互换。二维数组衰减为指向数组的指针,而双指针是指向指针的指针。

回到您的问题,编写函数签名的两种方式本质上是等效的,因为一维数组在传递给函数时会衰减为指针。所以void reverse(int* arr, const int N) void reverse(int arr[], const int N) 是一样的。

然而,在您的转置函数中,您传递的是一个二维数组。它会衰减为指向数组的指针。但是在您的函数声明中,您将这些参数作为数组(或实际上是指针)接受。由于 C 的怪癖,这仍然可以正常工作。2D 数组也可以被视为一个大的 1D 数组,其中行一个接一个地连续布置。然而,这不是最好的方法。这也反映了当您将数组名称传递给转置函数时必须取消引用它们,因为它需要一维数组(或指针)而不是二维数组(或指向数组的指针)。

此外,C/C++ 提供了一种比使用笨拙的指针算法更优雅的方式来访问数组。因此,我会推荐以下方法。它应该与您最初发布的代码完全一样,但会更干净、更易读。

#include <iostream>
using namespace std;

const int LENGTH = 2;
const int WIDTH = 3;

void printArray(const int arr[], const int N) {
    cout << arr[0]; 
    for (int i = 1; i < N; ++i) {
        cout << ", " << arr[i];
    }
    cout << "\n";
}

void transpose(const int input[][LENGTH], int output[][WIDTH]) {
    for (int i = 0; i < WIDTH; ++i) {
        for (int j = 0; j < LENGTH; ++j) {
            output[j][i] = input[i][j];
        }
    }
}

void reverse(int* arr, const int N) {
    for (int i = 0; i < N / 2; ++i) { 
        int temp = arr[i];
        arr[i] = arr[N - 1 - i];
        arr[N - 1 - i] = temp;
    }
}

int main() {
    int arr[4] = {2,4,6,8};
    printArray(arr, 4);
    reverse(arr, 4);
    printArray(arr, 4);
     
    int in_matrix[WIDTH][LENGTH];
    in_matrix[0][0] = 1;
    in_matrix[0][1] = 2;
    in_matrix[1][0] = 3;
    in_matrix[1][1] = 4;
    in_matrix[2][0] = 5;
    in_matrix[2][1] = 6;

    int out_matrix[LENGTH][WIDTH];
   
    transpose(in_matrix, out_matrix);

    cout << "in_matrix is:\n";
    for (int i = 0; i < WIDTH; ++i) {
        printArray(in_matrix[i], LENGTH);
    }

    cout << "out_matrix is:\n";
    for (int i = 0; i < LENGTH; ++i) {
        printArray(out_matrix[i], WIDTH);
    }
    return 0;
}

【讨论】:

  • 谢谢。这很有意义。也许我应该说我确切地知道如何使用数组下标(代码中的方法)来完成任务。我只是故意尝试使用指针算术练习。当涉及到二阶数组时,也许我不应该这样做。从字面上看是没有理由的。 (另外,为什么我的“被黑”代码有效的解释是有道理的)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多