c++ - 数组声明在 C++ 中如何工作?

标签 c++ arrays pointers multidimensional-array

<分区>

我试图了解在 C++ 中声明数组(一维或二维)的不同方式以及它们返回的内容(指针、指向指针的指针等)

这里有一些例子:

int A[2][2] = {0,1,2,3};
int A[2][2] = {{0,1},{2,3}};
int **A = new int*[2];
int *A = new int[2][2];

在每种情况下,A 到底是什么?是指针,双指针?当我执行 A+1 时会发生什么?这些都是声明矩阵的有效方法吗?

另外,为什么第一个选项不需要第二组花括号来定义“列”?

最佳答案

看起来你在我写我的时候得到了太多的答案,但我还是把我的答案贴出来,这样我才不会觉得这一切都是徒劳的......

(所有 sizeof 结果取自 VC2012 - 32 位构建,指针大小当然会在 64 位构建中加倍)

size_t f0(int* I);
size_t f1(int I[]);
size_t f2(int I[2]);

int main(int argc, char** argv)
{
    // A0, A1, and A2 are local (on the stack) two-by-two integer arrays
    // (they are technically not pointers)

    // nested braces not needed because the array dimensions are explicit [2][2]
    int A0[2][2] = {0,1,2,3};

    // nested braces needed because the array dimensions are not explicit,
    //so the braces let the compiler deduce that the missing dimension is 2
    int A1[][2] = {{0,1},{2,3}};

    // this still works, of course. Very explicit.
    int A2[2][2] = {{0,1},{2,3}};

    // A3 is a pointer to an integer pointer. New constructs an array of two
    // integer pointers (on the heap) and returns a pointer to the first one.
    int **A3 = new int*[2];
    // if you wanted to access A3 with a double subscript, you would have to
    // make the 2 int pointers in the array point to something valid as well
    A3[0] = new int[2];
    A3[1] = new int[2];
    A3[0][0] = 7;

    // this one doesn't compile because new doesn't return "pointer to int"
    // when it is called like this
    int *A4_1 = new int[2][2];

    // this edit of the above works but can be confusing
    int (*A4_2)[2] = new int[2][2];
    // it allocates a two-by-two array of integers and returns a pointer to
    // where the first integer is, however the type of the pointer that it
    // returns is "pointer to integer array"

    // now it works like the 2by2 arrays from earlier,
    // but A4_2 is a pointer to the **heap**
    A4_2[0][0] = 6;
    A4_2[0][1] = 7;
    A4_2[1][0] = 8;
    A4_2[1][1] = 9;


    // looking at the sizes can shed some light on subtle differences here
    // between pointers and arrays
    A0[0][0] = sizeof(A0);        // 16 // typeof(A0) is int[2][2] (2by2 int array, 4 ints total, 16 bytes)
    A0[0][1] = sizeof(A0[0]);     // 8  // typeof(A0[0]) is int[2] (array of 2 ints)

    A1[0][0] = sizeof(A1);        // 16 // typeof(A1) is int[2][2]
    A1[0][1] = sizeof(A1[0]);     // 8  // typeof(A1[0]) is int[2]

    A2[0][0] = sizeof(A2);        // 16 // typeof(A2) is int[2][2]
    A2[0][1] = sizeof(A2[0]);     // 8  // typeof(A1[0]) is int[2]

    A3[0][0] = sizeof(A3);        // 4 // typeof(A3) is int**
    A3[0][1] = sizeof(A3[0]);     // 4 // typeof(A3[0]) is int*

    A4_2[0][0] = sizeof(A4_2);    // 4 // typeof(A4_2) is int(*)[2] (pointer to array of 2 ints)
    A4_2[0][1] = sizeof(A4_2[0]); // 8 // typeof(A4_2[0]) is int[2] (the first array of 2 ints)
    A4_2[1][0] = sizeof(A4_2[1]); // 8 // typeof(A4_2[1]) is int[2] (the second array of 2 ints)
    A4_2[1][1] = sizeof(*A4_2);   // 8 // typeof(*A4_2) is int[2] (different way to reference the first array of 2 ints)

// confusion between pointers and arrays often arises from the common practice of
// allowing arrays to transparently decay (implicitly convert) to pointers

    A0[1][0] = f0(A0[0]); // f0 returns 4.
    // Not surprising because declaration of f0 demands int*

    A0[1][1] = f1(A0[0]); // f1 returns 4.
    // Still not too surprising because declaration of f1 doesn't
    // explicitly specify array size

    A2[1][0] = f2(A2[0]); // f2 returns 4.
    // Much more surprising because declaration of f2 explicitly says
    // it takes "int I[2]"

    int B0[25];
    B0[0] = sizeof(B0); // 100 == (sizeof(int)*25)
    B0[1] = f2(B0); // also compiles and returns 4.
    // Don't do this! just be aware that this kind of thing can
    // happen when arrays decay.

    return 0;
}

// these are always returning 4 above because, when compiled,
// all of these functions actually take int* as an argument
size_t f0(int* I)
{
    return sizeof(I);
}

size_t f1(int I[])
{
    return sizeof(I);
}

size_t f2(int I[2])
{
    return sizeof(I);
}

// indeed, if I try to overload f0 like this, it will not compile.
// it will complain that, "function 'size_t f0(int *)' already has a body"
size_t f0(int I[2])
{
    return sizeof(I);
}

是的,这个样本有大量有符号/无符号整数不匹配,但那部分与问题无关。另外,不要忘记 delete 使用 new 创建的所有内容和 delete[] 使用 new[] 创建的所有内容

编辑:

“当我执行 A+1 时会发生什么?” -- 我之前错过了这个。

像这样的操作将被称为“指针算术”(尽管我在回答的顶部大声说其中一些不是指针,但它们可以变成指针)。

如果我有一个指向 someType 数组的指针 P,那么下标访问 P[n] 与使用它完全相同语法 *(P + n)。在这两种情况下,编译器都会考虑指向的类型的大小。因此,生成的操作码实际上会为您做这样的事情 *(P + n*sizeof(someType)) 或等效的 *(P + n*sizeof(*P)) 因为物理 cpu 不知道也不关心我们所有的“类型”。最后,所有指针偏移量都必须是字节数。为了保持一致性,在这里使用像指针这样的数组名称也是一样的。

回到上面的示例:A0A1A2A4_2 都表现相同用指针运算。

A0[0]*(A0+0)相同,引用了的第一个int[2] >A0

类似的:

A0[1]*(A0+1) 相同,后者将“指针”偏移 sizeof(A0[0])(即 8,见上文),它最终引用 A0

的第二个 int[2]

A3 的行为略有不同。这是因为 A3 是唯一一个不连续存储 2 x 2 数组的所有 4 个整数的数组。在我的示例中,A3 指向一个包含 2 个 int 指针的数组,每个指针都指向包含两个 int 的完全独立数组。使用 A3[1]*(A3+1) 最终仍会将您定向到两个 int 数组中的第二个,但它只会通过偏移 4 个字节来实现从 A3 的开头(出于我的目的使用 32 位指针),它为您提供了一个指针,告诉您在哪里可以找到第二个双整数数组。我希望这是有道理的。

关于c++ - 数组声明在 C++ 中如何工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26557231/

相关文章:

c - Memmove in same pointer for delete multiple whitespaces C

javascript - parse.com 使用 Javascript 多对多获取指针 ID 列表

c++ - CMake将C++参数传递给nvcc

c++ - 忽略 "taking address of temporary"错误

arrays - swift 4 - 获取数组的内存大小

java - 我无法在 Java 中正确地乘以数组

c++ - luabind 如何隐式转换对象?

c++ - begin() 与 rbegin() 它们是如何实现的?

java - 不调用继承类的方法

c - 初始化函数指针数组