我用C编码“将动态2D传递给功能”。
以下代码可以成功编译并正常运行。
void iter_2d(const int** arr,
const size_t row,
const size_t column) {
// ..some code
}
int main(){
const size_t column = 3, row = 4;
int** arr = (int**)malloc(sizeof(int*) * row);
for (size_t i = 0; i < row; i++)
arr[i] = (int*)malloc(sizeof(int) * column);
// init val for array
iter_2d(arr,row,column);
// clear array
}
但我得到警告:
t.c:24:11: warning: passing argument 1 of 'iter_2d' from incompatible pointer type [-Wincompatible-pointer-types]
iter_2d(arr,row,column);
^~~
t.c:4:26: note: expected 'const int **' but argument is of type 'int **'
void iter_2d(const int** arr,
~~~~~~~~~~~~^~~
我认为函数 iter_2d 只是迭代数组的值,而不能在函数 iter_2d 中修改
因此输入参数 arr 应该为指针。
但是编译器向我显示此警告使我感到困惑。
答案 0 :(得分:8)
在C 2018标准的6.5.16.1 6示例3中给出了从char **
到const char **
的转换违反约束的原因。假设我们有:
const char **cpp;
char *p;
const char c = 'A';
接下来,考虑&p
。这是char **
。如果我们允许转换为const char **
,则可以将其分配给cpp
:
cpp = &p; // Violation of C constraints for assignment.
假设我们做到了。那么cpp
是const char **
,所以*cpp
是const char *
。这意味着我们可以为其分配一个const char
的地址,如下所示:
*cpp = &c;
现在*cpp
是指向c
的指针。由于cpp
指向p
,所以*cpp
是p
,这意味着p
指向c
。现在我们可以这样做:
*p = 0;
这会更改c
,但是c
是const char
,我们不应该更改。
因此,允许从char **
到const char **
的转换违反了常量对象所需的行为。
此示例使用赋值表达式,但是将参数传递给函数的行为被定义为类似于将参数赋给参数。约束是相同的。
答案 1 :(得分:1)
将参数传递给函数时,它们“就像通过赋值一样”被复制,这意味着参数副本遵循与=
运算符相同的规则,正式称为简单赋值。因此,您的代码在行之间实际执行的操作基本上与此相同:
int** arr1 = ... ;
const int** arr2 = arr1;
如果您尝试编译该代码段,则会收到几乎相同的错误消息,例如“从不兼容类型初始化”。
进行简单分配时,复制指针的规则为(简化):
=
运算符的左操作数)可以具有合格或不合格的指针类型。限定含义,例如const
。int x; const int y = x;
可以,但反之则不行。)对于您拥有int* x; const int* y = x;
的情况,编译器不会抱怨。 y
是指向int类型的合格指针,x
是指向int类型的不合格指针。 y
至少具有x
的所有限定词。满足了上述所有规则,所以很好。
这里的问题是限定符与指针到指针如何一起表现。 const int**
实际上意味着(从右到左读取)“指向const int的指针”。它不是不是的意思是“指向int指针的const指针”。
如果我们回到第一个示例const int** arr2 = arr1;
,则arr2是指向类型const int*
的非限定指针。而且arr1是指向int*
类型的不合格指针。它们不是兼容类型-arr2
恰好指向 一种类型,该类型是arr1
所指向内容的限定版本。规则只关心“最外层”指针类型本身。
要解决此问题并保持“常量正确性”,我们必须常量限定指针到指针本身。那将是int**const arr
。再次,从右到左阅读:“指向int的const指针”。
但是,我的建议是完全摆脱指向指针的指针,因为您的代码因此而不必要地变慢。多个malloc调用将导致分散的分配和不良的数据缓存利用率。相反,您可以像这样使用指向VLA的指针:
#include <stdlib.h>
void iter_2d (size_t row,
size_t column,
int arr[row][column]);
int main (void){
const size_t row = 4;
const size_t column = 3;
int (*arr)[column] = malloc( sizeof(int[row][column]) );
iter_2d(row, column, arr);
free(arr);
}
此代码更快,更简单,更易于阅读。
更多信息:Correctly allocating multi-dimensional arrays。