int** 和 int[][] 类型有什么区别
What is difference between types int** and int[][]?
如果以下赋值有效:
int a[2] = {1,2};
int* b = a;
那么这有什么问题:
int a[2][2]={1,2,3,4};
int** b = a;
C++给出一个错误,指出它无法将int[][]
转换为int**
。如果int[]
与int*
相同,这两种类型有什么区别?
放轻松。这只是编译器错误。数组非常棘手。规则如下:
数组类型的变量的值衰减到该数组的元素零的地址
您的第一个代码段如下所示:
int a[2] = {1,2};
因此,根据规则,如果a
位于赋值的右侧,那么它会衰减到元素零的地址,这就是为什么它具有类型 int *
.这将您带到
int *b = a;
在第二个代码段中,您真正拥有的是一个数组数组。(顺便说一下,为了明确起见,我已经对您的代码进行了一些更改。
int a[2][2]={{1,2},{3,4}};
这次a
将衰减到指向两个整数数组的指针!因此,如果您想将a
分配给某些内容,则需要此内容具有相同的类型。
int (*b)[2] = a; //Huh!
(这种语法可能对你来说有点令人惊讶,但只要想一想我们已经写了int *b[2];
明白了吗? b
将是指向整数的指针数组!不是我们真正想要的...
你可以停止阅读这里,但你也可以继续前进,因为我还没有告诉你所有的真相。我提到的规则有三个例外...
数组的值不会衰减到元素零的地址,如果
- 数组是
sizeof
的操作数 - 数组是
&
的操作数
数组 - 是字符数组的文本字符串初始值设定项
让我们更详细地解释这些异常,并举例说明:
int a[2];
int *pi = a ; /* the same as pi = &a[0]; */
printf("%dn", sizeof(a)); /* size of the array, not of a pointer is printed! */
int (*pi2)[2] = &a; /* address of the array itself is taken (not the address of a pointer) */
最后
char a[] = "Hello world ";
这里没有复制指向"Hello world"的指针,而是复制了整个字符串并指向此副本。
信息真的很多,一次理解所有内容真的很难,所以慢慢来。我建议你阅读K&R关于这个主题,然后阅读这本优秀的书。
出现的事情,所以我会尽量清楚地解释它。
当你创建一个数组时,它会将元素连续地存储在内存中,因此:
int arr[2] = { 1, 2 };
翻译为:
arr:
+---+---+
| 1 | 2 |
+---+---+
指针指向内存中的对象,当取消引用时,通过一元*
或通过[]
,它访问该连续内存。所以之后
int *ptr = arr;
ptr
(或&ptr[0]
,如果您愿意(指向1
所在的框,ptr + 1
(或&ptr[1]
(指向2
所在的框。这是有道理的。
连续的,则数组的数组在内存中是连续的。所以:
int arr[2][2] = {{ 1, 2 }, { 3, 4 }};
在内存中看起来像这样:
arr:
+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+
这看起来很像我们的平面阵列。
现在,让我们考虑指向int
指针的指针如何在内存中布局:
ptr:
+-------+-------+
| &sub1 | &sub2 |
+-------+-------+
sub1:
+---+---+
| 1 | 2 |
+---+---+
sub2:
+---+---+
| 3 | 4 |
+---+---+
ptr
(或&ptr[0]
(指向sub1
,ptr + 1
(或&ptr[1]
(指向sub2
。 sub1
和 sub2
彼此之间没有实际关系,并且可以位于内存中的任何位置,但由于它是指向指针的指针,因此保留了 2D 数组的双重取消引用,即使内存结构不兼容也是如此。
T
类型的数组衰减为指向 T
类型的指针,但 T
类型的数组数组不会衰减到指向类型 T
的指针,它们衰减到指向类型 T
数组的指针。因此,当我们的 2D arr
衰减到指针时,它不是指向int
指针的指针,而是指向int [2]
的指针。这种类型的全名是 int (*)[2]
,为了使您的代码行正常工作,您需要使用
int (*ptr)[2] = arr;
哪种类型是正确的。 ptr
期望指向一个连续的内存数组,就像arr
所做的那样 - ptr
(或&ptr[0]
(指向arr
,ptr + 1
(或&ptr[1]
(指向&arr[1]
。 ptr[0]
指向容纳1
的盒子,ptr[1]
指向容纳3
的盒子,因此ptr[0][0]
产生 1,ptr[0][1]
产生 2,依此类推。
你为什么需要知道这个? 2D 指针似乎比它们的价值更复杂 - 如果您使用malloc
则必须在循环中反复调用malloc
,并对free
执行相同的操作。或者,您可以使用一些邪恶的*诡计来使扁平的一维内存分配像 2D 数组一样运行:
// x and y are the first and second dimensions of your array
// so it would be declared T arr[x][y] if x and y were static
int (*arr)[y] = malloc(x * y * sizeof(arr[0][0]));
if(!arr) /* error */;
现在,arr
指向大小y
int
对象的连续数组块。由于它指向的对象是一个数组,因此我们不需要int **
对象的双指针间接寻址,完成后,您可以通过一次调用来释放它:
free(arr);
将其与使用 int **
的版本进行比较:
int **arr = malloc(x * sizeof(*arr));
if(!arr) /* error */;
for(size_t ii = 0; ii < x; ii++)
{
arr[ii] = malloc(y * sizeof(**arr));
if(!arr[ii])
{
free(arr[ii]);
free(arr);
}
}
// do work
for(size_t ii = 0; ii < x; ii++)
free(arr[ii]);
free(arr);
上面的代码有内存泄漏。看看你能不能找到它。(或者只是将版本与那些看似棘手的指向数组的指针一起使用。
著名的衰减约定:数组被视为指向数组第一个元素的指针。
int a[2] = {1,2};
int* b = a; //decay
但是衰减约定不应该多次应用于同一个对象。
int a[2][2]={1,2,3,4};
int** b = a; //decay more than once
int[]
与 int*
不同。 在某些情况下,int[]
会衰变成int*
。
您可能应该阅读 comp.lang.c 常见问题解答,特别是:
- Q6.2 但我听说
char a[]
和char *a
一模一样。 - Q6.3 那么C语言中"指针和数组的等价性"是什么意思呢?
也许是数组和指针部分的其余部分。
- int(c) 和 c-'0' 之间的区别。C++
- 向量 <int> a {N, 0} 和 int arr a[N] = {0} 的时间复杂度有什么区别
- 结构体 S { int align; } 之间的区别;(struct 关键字后的名称)和 struct { int al
- 在 C 和 C++ 中作为函数参数,int **a 和 int a[][] 之间有什么确切的区别
- 在 C/C++ 中将数组作为形式参数作为 int arr[] 和 int arr[N] 传递的区别
- int x_ 和 int x 在C++中有什么区别
- 无符号长整型和无符号 int 之间有什么区别,这 2 种类型应该如何在 c# 中封送?
- int (%d) 和 %.0lf 之间有什么区别?
- 打印一个带有静态 int 的函数,有一个 std::cout 和多个 std::cout 有什么区别?
- 有什么区别 - 常量 int x = 5000;和常量整数 x = 50'00;在C++?
- 杀死幻数:"const int" vs "constexpr int"(或者最后没有区别)
- uniform_int_distribution <>和 <int>有什么区别?
- “int*[1]”和“int(*)[1]”有什么区别
- std::vector<int> 和 std::vector* 有什么区别<int>?
- "auto x = vector<int>()"和"vector<int> x"有什么区别?
- int '*p = new int (5);' 和 'int *p = new int[5];' 有什么区别?
- C++ 中的 "int a, b =0" 和 "int a =0; int b = 0" 有什么区别?
- const int const&和const int&in C++有什么区别?
- "long int a=2"和"int a=2L"有什么区别?
- "long"和"long int"以及"long long"与"long long int"有什么区别