c++ - C++ 语法差异:2D 和 1D 数组(指针算术)
问题描述
问题
我正在学习 C++,并且正在编写代码来转置 2D 数组并反转 1D-array。
请查看调用。为什么我必须使用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]
解决方案
在 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;
}
推荐阅读
- javascript - 使用以编程方式添加的验证器将超链接添加到 ValidationSummary
- vb.net - 结构 intptr 无法被索引,因为它没有默认属性
- sails.js - 如何在 Helper 类中添加更多功能?
- azure-ad-b2c - 有没有办法在本地测试 Azure B2C 自定义策略而不将 XML 文件上传到 Azure 门户?
- node.js - 如何将浏览器配置为在 MEAN 应用程序中发送另一个请求之前等待响应
- apache-spark - Apache Spark Stats 集合..
- sql-server - DataSet SSRS 中的 STRING_SPLIT 函数替代方案是什么
- php - Stripe,从事件中检索卡片信息
- git - GitHub Desktop:为什么我创建的两个分支不会将它们的内容从主分支同步到彼此?
- amazon-web-services - 过滤代码构建推送事件