关于警告:“注意:应为'const int **',但参数类型为'int **'”

时间:2019-08-08 19:37:50

标签: c

我用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 应该为指针

但是编译器向我显示此警告使我感到困惑。

2 个答案:

答案 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.

假设我们做到了。那么cppconst char **,所以*cppconst char *。这意味着我们可以为其分配一个const char的地址,如下所示:

*cpp = &c;

现在*cpp是指向c的指针。由于cpp指向p,所以*cppp,这意味着p指向c。现在我们可以这样做:

*p = 0;

这会更改c,但是cconst 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