ページ 11

二次元配列とconst修飾子

Posted: 2016年9月03日(土) 10:42
by Ohagi

コード:

#include <stdio.h>

print_array(const int n[10][10])
{
  /* n[][]の中身を表示 */
}

int main(void)
{
  int n[10][10];
  
  print_array(n);

  return 0;
}
print_array()がint n[][]の値を変更しないことを明示するためにconstを付けて定義すると
: warning: passing argument 1 of ‘print_array’ from incompatible pointer type print_array(n);
と表示されます。

clコンパイラだと問題なく通るのですが、gccですと警告されます。

何が駄目だったのでしょうか?

参考書の説明ではconstはこういう修飾子だったようなきがするのですが...

回答の方をお願いします。

Re: 二次元配列とconst修飾子

Posted: 2016年9月03日(土) 20:45
by hoge
これは残念ながら、Cの仕様です。
通常、関数に配列を渡すとき、関数内で配列を変更しないとき、constを付けることは望ましいことなのですが、
void func(type n[]);
↑このような関数でconstを付けられるのはtypeに対してだけです。
print_arrayの場合にはconstを付けてもいいのはintではなくint [10]になるのですが、
不幸なことにint [10]にconstを付ける方法がCには存在しません。

Re: 二次元配列とconst修飾子

Posted: 2016年9月04日(日) 03:39
by かずま
kazuki2655 さんが書きました:clコンパイラだと問題なく通るのですが、gccですと警告されます。
gcc --version でバージョンの確認をお願いします。

gcc 4.8.4 では、その警告が出ましたが、
gcc 5.3.0 では、その警告が出ませんでした。

print_array(const int n[10][10]) という宣言は、
print_array(const int (*n)[10]) という宣言と同じです。

main で宣言されている n の型は int[10][10] です。
print_array を呼び出すときの引数 n は、int (*)[10] に型変換されます。
int (*)[10] と const int (*)[10] の型が異なるので、gcc 4.8.4 では、
incompatible pointer type だという警告を出したのでしょう。

一次元配列の場合を考えてみましょう。

コード:

void print_array(const int n[10])
{
  /* n[]の中身を表示 */
}
 
int main(void)
{
  int n[10];
  print_array(n);
  return 0;
}
この場合、呼び出し側の n の型は int *。
関数の宣言の引数 n の型は const int *。
型は異なりますが、型 T へのポインタは、型 const T へのポインタに変換できるので、問題ありません。

二次元配列の場合の int (*)[10] は、型 T が int [10] であり、その型へのポインタです。

int [10] に const をつける記法がありません。
const (int [10]) や int [10]const はダメです。
const int [10] や int const [10] とすると、それは、int を const で修飾したのであって、
配列 [10] を const で修飾したことになりません。

gcc 5.3.0 で、なぜ警告を出さなくなったのかは知りませんが、
警告なしでも問題にならないからでしょうか。

Re: 二次元配列とconst修飾子

Posted: 2016年9月07日(水) 19:55
by Ohagi
型の変換や修飾などの知識が甘くて完全には理解できませんしたが
C言語の仕様の問題で2次元配列のconst修飾ができないことはわかりました。

今後Cを使っていくうえで2次元配列でconstが使えない理由が気になった時
もう一度読み返させて頂こうと思います。

回答してくださった御二方、ありがとうございました。