为什么 A[i][j] 和 *((int*)A + i * n + j) 给出不同的输出?

Sta*_*ght 2 c++ pointers

我正在学习C++指针。我的老师提到这*((int*)A + i * n + j)是线性化符号的另一种方法A[i][j]。我尝试在这个 main 函数中使用 2x4 2D 数组来测试它。

int main()
{
    int** A = new int* [100];
    for (int i = 0; i < 2; ++i)
    {
        A[i] = new int[100];
    }

    //assign value
    for (int i = 0; i < 2; ++i)
        for (int j = 0; j < 4; ++j)
            cin >> *((int*)A + i * 4 + j);
            //cin >> A[i][j]; //if I do this instead of the line above, I will get trash values in the output

    for (int i = 0; i < 2; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            cout << *((int*)A + i * 4 + j) << " ";
        }
        cout << "\n";
    }
    
    for (int i = 0; i < 2; ++i)
       delete[] A[i];
    delete [] A;
}
Run Code Online (Sandbox Code Playgroud)

输入示例:

1 2 3 4
5 6 7 8
Run Code Online (Sandbox Code Playgroud)

我不明白为什么当我这样做时

cin >> A[i][j];
Run Code Online (Sandbox Code Playgroud)

然后

cout << *((int*)A + i * 4 + j) << " ";
Run Code Online (Sandbox Code Playgroud)

它给了我垃圾值。这是否意味着我必须cin >> *((int*)A + i * 4 + j);这样做cout << *((int*)A + i * 4 + j) << " ";

我的另一个问题是:为什么我必须显式强制转换(int*)?为什么不能呢(A + i * 4 + j)

Who*_*aig 5

你完全误解了老师想要告诉你的内容。他们的描述中的关键字是“符号”,但他们遗漏了一些重要的东西(我稍后会讲到)

首先,如果你的教练告诉你这样做:

*((int*)A + i * n + j)
Run Code Online (Sandbox Code Playgroud)

质疑他们所说的一切。这种强制转换既没有必要,也不建议这样做,而且除了隐藏不良代码之外什么也做不了。如果A是正确的类型,那么执行此操作就足够了:

*(A + i*n + j)
Run Code Online (Sandbox Code Playgroud)

如果它不是正确的类型,您可能一开始就不应该这样做(您刚刚发现)。

其次,您的讲师没有告诉您的是,这对于使用创造性索引在维数组的线性空间中建立人造多维数组非常有用。关键是维数组。作案手法是这样的:

如果希望将 M 行 N 列的 2D 空间映射到 M*N 元素的 1D 空间,您可以这样做:

constexpr size_t M = 10;
constexpr size_t N = 5;

int A[M*N];
A[row * N + col] = value;

// equivalent to...
*(A + row * N + col) = value;
Run Code Online (Sandbox Code Playgroud)

注意,row应在 0...(M-1) 范围内,并且col应在 0..(N-1) 范围内(包括 0...(M-1))。

该模型可以扩展到更多维度。例如,“3D”映射,L表示板、M表示行、N表示列:

constexpr size_t L = 10;
constexpr size_t M = 8;
constexpr size_t N = 5;

int A[L*M*N];

A[slab * (M*N) + row * N + col] = value;

// equivalent to...
*(A + slab * (M*N) + row * N + col) = value;

Run Code Online (Sandbox Code Playgroud)

其中slab的范围为 0...(L-1),row的范围为 0...(M-1),最后的col范围为 0...(N-1)。

您应该看到一个图案正在形成。只要您知道每个维度的限制,任何本机单维数组都可以使用多维表示制造的下标进行索引,并且生成的索引不会违反单维数组床。

大多数时候你不需要这个,但有时它会派上用场,特别是在 C++ 中,因为它缺乏 C 提供的运行时 VLA 支持。

因此,正确使用你的老师试图告诉你的内容应该是这样的:

#include <iostream>

int main()
{
    static constexpr size_t M = 2;
    static constexpr size_t N = 4;
    
    int *A = new int[M*N];
    
    //assign value
    for (size_t i = 0; i < M; ++i)
    {
        for (size_t j = 0; j < N; ++j)
            std::cin >> *(A + i * N + j);
    }

    for (size_t i = 0; i < M; ++i)
    {
        for (size_t j = 0; j < N; ++j)
            std::cout << *(A + i * N + j) << " ";
        std::cout << "\n";
    }
    
    delete [] A;
}
Run Code Online (Sandbox Code Playgroud)