我正在尝试理解在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
时会发生什么?这些都是宣告矩阵的有效方法吗?
另外,为什么第一个选项不需要第二组花括号来定义“列”?
答案 0 :(得分:3)
在我写作的时候,你的答案看起来很多,但我还是可以发布我的答案,所以我觉得这不是一无所有......
(所有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);
}
是的,此示例有大量有符号/无符号的int不匹配,但该部分与该问题无关。此外,请不要忘记delete
使用new
和delete[]
创建的所有内容new[]
创建的所有内容A+1
编辑:
“当我P
时会发生什么?” - 我之前错过了。
如果我有someType
数组的指针P[n]
,则下标访问*(P + n)
与使用此语法*(P + n*sizeof(someType))
完全相同。编译器将考虑两种情况下指向的类型的大小。因此,生成的操作码实际上会为您*(P + n*sizeof(*P))
或等效A0
执行类似的操作,因为物理cpu不知道或不关心我们所有组成的“类型”。最后,所有指针偏移都必须是字节数。为了保持一致性,使用像指针这样的数组名称在这里工作相同。
回到上面的示例:A1
,A2
,A4_2
和A0[0]
的行为与指针算法相同。
*(A0+0)
与int[2]
相同,A0
引用A0[1]
的第一个*(A0+1)
类似地:
sizeof(A0[0])
与int[2]
相同,后者将“指针”偏移A0
(即8,见上文),最终引用A3
的{{1}} {1}}
A3
行为略有不同。这是因为A3
是唯一一个不连续存储2乘2阵列的所有4个整数的{0}。在我的示例中,A3[1]
指向一个包含2个int指针的数组,每个指针指向完全分离两个整数的数组。使用*(A3+1)
或{{1}}仍会最终指向两个int数组中的第二个,但是它会通过从A3的开头仅偏移4个字节来实现它(使用32位指针用于我的目的)它给你一个指针,告诉你在哪里找到第二个两个int数组。我希望这是有道理的。
答案 1 :(得分:2)
int A[2][2] = {0,1,2,3};
int A[2][2] = {{0,1},{2,3}};
这些将A
声明为array of size 2 of array of size 2 of int
。声明完全相同。
int **A = new int*[2];
这声明了一个用两个指针数组初始化的pointer to pointer to int
。如果要将其用作二维数组,则应为这两个指针分配内存。
int *A = new int[2][2];
这不会编译,因为右边部分的类型是pointer to array of size 2 of int
,无法转换为pointer to int
。
在所有有效情况下,A + 1
与&A[1]
相同,这意味着它指向数组的第二个元素,即,如果int A[2][2]
指向第二个数组两个整数,如果int **A
到数组中的第二个指针。
答案 2 :(得分:2)
对于数组声明,第一个指定的维度是最外面的维度,是包含其他数组的数组。
对于指针声明,每个*
都会添加另一个间接层。
为C语言设计的语法是让声明模仿使用。 C创建者和C ++创建者(Bjarne Stroustrup)都将语法描述为失败的实验。主要问题是它并没有遵循数学中通常的替代规则。
在C ++ 11中,您可以使用std::array
代替方括号声明。
您也可以定义类似的ptr
类型构建器,例如
template< class T >
using ptr = T*;
然后写
ptr<int> p;
ptr<ptr<int>> q;
答案 3 :(得分:1)
其他答案涵盖了其他声明,但我将解释为什么在前两次初始化中不需要大括号。这两个初始化的原因是相同的:
int A[2][2] = {0,1,2,3};
int A[2][2] = {{0,1},{2,3}};
是因为它被aggregate initialization覆盖。在这种情况下,允许“省略”(省略)大括号。
C ++标准提供了第8.5.1节中的示例:
[...]
float y[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 }, };
[...]
在以下示例中,初始化列表中的大括号被省略; 但初始化列表与。具有相同的效果 上面例子的完全支撑的初始化列表,
float y[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 };
y的初始值设定项以左括号开头,但是y [0]的初始化程序 没有,因此使用列表中的三个元素。同样 接下来的三个是y [1]和y [2]连续拍摄的。
答案 4 :(得分:0)
好的我会尝试向你解释一下:
答案 5 :(得分:0)
int A[2][2] = {0,1,2,3};
int A[2][2] = {{0,1},{2,3}};
这两个是等价的 两者都意味着:&#34;我声明了一个二维的整数数组。该阵列的大小为2乘2&#34;。
然而,记忆不是二维的,它不是在网格中布局,而是(概念上)在一条长线上。在多维数组中,每行只是在前一行中分配在内存中。 因此,我们可以转到A
指向的内存地址,并存储长度为2的两行或长度为4的一行,并且内存中的最终结果将是相同的。
int **A = new int*[2];
声明一个指向A的指针。
A存储指向包含int
s的大小为2的数组的指针的地址。该数组在堆上分配。
int *A = new int[2][2];
A是指向 int
的指针
该int是堆中分配的2x2 int
数组的开头。
一般来说这是无效的:
prog.cpp:5:23: error: cannot convert ‘int (*)[2]’ to ‘int*’ in initialization
int *A = new int[2][2];
但由于我们在前两个看到的内容,这将起作用(并且100%相当):
int *A new int[4];
答案 6 :(得分:-2)
int A[2][2] = {0,1,2,3};
A是4个整数的数组。为了编码器的方便,他决定将其声明为二维数组,因此编译器将允许编码器将其作为二维数组进行访问。编码器将所有元素线性初始化,因为它们存放在内存中。像往常一样,由于A是一个数组,A本身就是数组的地址,因此A + 1(在应用指针数学运算之后)将A偏移2个int指针的大小。由于数组的地址指向该数组的第一个元素,因此A将指向数组第二行的第一个元素,值为2.
编辑:使用单个数组运算符访问二维数组将沿第一维操作,将第二维视为0.因此A [1]等效于A [1] [0]。 A + 1导致等效指针添加。
int A[2][2] = {{0,1},{2,3}};
A是4个整数的数组。为了编码器的方便,他决定将其声明为二维数组,因此编译器将允许编码器将其作为二维数组进行访问。编码器按行初始化元素。出于同样的原因,A + 1指向值2.
int **A = new int*[2];
是指向int指针的指针,该指针已初始化为指向指向int指针的2个指针的数组。由于A是指针,A + 1取A的值,这是指针数组的地址(因此,数组的第一个元素)并加1(指针数学),其中现在将指向数组的第二个元素。由于数组没有被初始化,实际上用A + 1做一些事情(比如读它或写它)会很危险(谁知道那里有什么价值以及实际上指的是什么,如果它甚至是一个有效的地址)。 / p>
int *A = new int[2][2];
编辑:正如Jarod42指出的那样,这是无效的。我认为这可能更接近你的意思。如果没有,我们可以在评论中澄清。
int *A = new int[4];
A是指向int的指针,该指针已初始化为指向4个整数的匿名数组。由于A是指针,A + 1取A的值,这是指针数组的地址(因此,数组的第一个元素)并加1(指针数学),其中现在将指向数组的第二个元素。
一些要点:
也就是说,你需要注意如何使用带有数组元素的指针。请考虑以下事项:
int *a = new int(5);
int *b = new int(6);
int c[2] = {*a, *b};
int *d = a;
c+1
与d+1
不同。事实上,访问d+1
非常危险。为什么?因为c
是一个int数组,已通过解除引用a
和b
进行初始化。这意味着c
是a
内存块的地址,其中该内存位置是已设置为tovariable a
指向的值的值,并且在下一个内存位置,是变量b
固定的值。另一方面,d
只是a
的地址。因此,您可以看到,c != d
因此没有理由c + 1 == d + 1
。