【问题标题】:Pass a two dimensional array to a function of constant parameter将二维数组传递给常量参数的函数
【发布时间】:2015-03-19 16:04:52
【问题描述】:

我从C Primer Plus中了解到,如果你想保护一个数组不被函数意外修改,你应该在函数定义的头部中的指针声明之前添加const修饰符。

遵循这个明智的建议,在以下最小示例中,我尝试将非常量二维数组 array 传递给函数 Sum2D,其中一个参数是 pointer-to-const-int[2]

#include <stdio.h>
#define ROWS 2
#define COLS 2
int Sum2D(const int ar[][COLS], int rows); //use `const` to protect input array
int main(void)
{
    int array[ROWS][COLS]={{1,2},{3,4}}; //the non-constant array

    printf( "%d\n", Sum2D(array,ROWS) );

    return 0;
}

int Sum2D(const int ar[][COLS], int rows)
{
    int total=0;
    int i,j;
    for( i=0 ; i<rows ; i++ )
    {
        for( j=0 ; j<COLS ; j++ )
        {
            total+=ar[i][j];
        }
    }
    return total;
}

但是,gcc 在不发出以下警告的情况下无法成功编译此代码:

$gcc -ggdb3 -Wall -Wextra -o test test.c

test.c: In function ‘main’:
test.c:16:2: warning: passing argument 1 of ‘Sum2D’ from incompatible pointer type [enabled by default]
  printf( "%d\n", Sum2D(array,4) );
  ^
test.c:4:5: note: expected ‘const int (*)[4]’ but argument is of type ‘int (*)[4]’
 int Sum2D(const int ar[][COLS], int rows);
     ^

1) 为什么会出现警告?

2)如何消除“噪音”?(除了在array声明中添加const。)

(如果array和函数都使用一维数组,则没有警告。)

系统信息:

Ubuntu 14.04LTS

编译器:gcc 4.8.2

【问题讨论】:

  • 非常直接。函数Sum2D 期望接收一个 const 2d 数组,但您给它一个非 const 数组。这可能很危险,但不一定,这就是警告而不是错误的原因。
  • @inneedofhelp 实际上,该函数需要一个 指针 指向 const int 大小的 COLS 数组。在函数参数中,const int ar[][COLS]const int (*ar)[COLS] 相同
  • 你的参数rows有什么原因,而不仅仅是使用定义的维度?
  • 显然 gcc 有他们的警告。 4.9.2 shows no such warnings
  • 允许编译器将不符合标准的程序编译为扩展,因此这不会是一个严重的错误,但是如果有人以这种方式编写代码,然后将代码移植到编译器,这可能会很烦人没有扩展名

标签: c arrays pointers gcc-warning


【解决方案1】:

这是 C 设计中的一个不幸的“错误”; T (*p)[N] 不会隐式转换为 T const (*p)[N]。您将不得不使用丑陋的演员表,或者让函数参数不接受const


乍一看,这种转换应该是合法的。 C11 6.3.2.3/2:

对于任何限定符q,指向非q 限定类型的指针可以转换为指向q 限定类型的指针类型的版本;

但也请查看 C11 6.7.3/9(在 C99 中为 /8):

如果数组类型的规范包括任何类型限定符,则元素类型是如此限定的,而不是数组类型。

最后这句话说int const[4] 被认为是int[4]const 限定版本。实际上它是 4 个const ints 的非const 限定数组。 int[4]int const[4] 是不同元素类型的数组。

所以 6.3.2.3/2 实际上不允许将int (*)[4] 转换为int const (*)[4]


const 和数组出现此问题的另一个奇怪情况是在使用 typedef 时;例如:

typedef int X[5];
void func1( X const x );
void func1( int const x[5] );

这会导致编译器错误:X const x 表示 x 是 const,但它指向一个非 const 数组 ints;而 int const x[5] 表示 x 不是 const,但它指向一个 const ints 数组!

进一步阅读here,感谢@JensGustedt

【讨论】:

  • 你有这个参考吗?我想知道这是否意味着 clang 正在“修复”它作为扩展。我尝试在严格模式下编译(-Wall -Wextra -Wconversion -pedantic-errors -std=cxx 其中xx 是 89、99、11。)
  • 丑陋的演员表是指像(const int (*)[])这样的东西吗?我通过在函数调用中添加它来“修复”它。
  • 我认为我的知识仍然太有限,无法完全理解您的意思。但是当我尝试的时候,我想问:const int (*)[4]int const (*)[4]是同一个东西吗?因为前者是我在警告消息中看到的。
  • @Naitree const int (*)[4]int const (*)[4] 是同一个东西;指向四个 const int 的数组的指针。 Bothint (*const)[4] 不同,后者是一个指向由四个非常量 int 组成的数组的 const 指针。为了避免我们忽略它,int const (*const)[4],一个指向四个 const int 的数组的 const 指针。
  • 在 c23 的最新草案中,文本被更改为“如果数组类型的规范包括任何类型限定符,both 数组和元素类型都是如此限定的."。所以这个问题也许终于解决了
【解决方案2】:

您可以在调用函数时对数组进行类型转换。它不会自动将非 const 转换为 const。 你可以用这个。

Sum2D( (const int (*)[])array, ROWS );

【讨论】:

  • 我实际上仍然使用您的类型转换收到警告。我想你的意思是(const int (*)[])。然后警告就消失了。
猜你喜欢
  • 2012-01-22
  • 2020-11-01
相关资源
最近更新 更多