• 締切済み

関数のプロトタイプ宣言

#include <stdio.h> #define N 3 #define M 4 int sum(int [][M]); (関数のプロトタイプ宣言) int main(){   ・   ・   ・ return(0); } int sum(int x[][M]){    ・    ・ } 以上のプログラムより、関数のプロトタイプ宣言や、関数内の 行列の定義でint sum(int [][M]);や int sum(x[][M]){}となっていますが なぜ、列にMだけを代入することだけでよいのでしょうか? また、教科書に関数のプロトタイプ宣言ではint sum(int [][]);だけでもよいと書かれていたのですが 実際、コンパイルしてみたところ 'int[]' 型のサイズは未知あるいはゼロとエラーがでました。 これは、コンパイラによってできるものとできないものがあるのでしょうか?? よろしくお願いします。

みんなの回答

  • chie65536
  • ベストアンサー率41% (2512/6032)
回答No.6

以下、蛇足。 古いコンパイラで main(int argc, char argv[][]) が許されていたのには、理由があります。 charの配列に限り char str[]={ "abcd" }; と言う初期化が許されます。 これは char str[]={'a','b','c','d','\0'}; と同じ意味です。 そして、一部のコンパイラは *str と str[0] を同一視していました。どちらも「strが指すアドレス+0にある文字」を意味しますから、同じコードが生成されます。 つまり(実際のメモリ配置は異なるけども) char str[]={ "abcd" }; も char *str="abcd"; も同一とみなされていた訳です。 char str[]={'a','b','c','d','\0'}; と書くのが面倒なので char str[]={ "abcd" }; と書く事を許してしまったばっかりに「charのポインタと、charの配列は、同じモンだよ~」と言う、トンデモな処理系が出てきてしまったのです。 さて、上記2つを、行数が3行の配列型にしたら、どうなるでしょう? char str[3][]={ "abcd","defghij","klmnopqr" }; と char *str[3]={ "abcd","defghij","klmnopqr" }; になります。前述の例が同一視されているなら、これも同一視されます。 ではでは、3行ではなく、不定行で定義したら? char str[][]={ "abcd","defghij","klmnopqr" }; と char *str[]={ "abcd","defghij","klmnopqr" }; になります。前述の例が同一視されているなら、これも同一視されます。 「char *str[]」の書き方は、通常のmain関数の定義の main(int argc, char *argv[]) で良く見る形になりました。 「char *str[]」が「char str[][]」と同一視されるのなら main(int argc, char *argv[]) と main(int argc, char argv[][]) も同一視されてしまいます。 そういう訳で、古いコンパイラでは main(int argc, char argv[][]) は main(int argc, char *argv[]) と解釈されてコンパイルが通ってしまうという「トンデモ仕様」になっていました。

  • chie65536
  • ベストアンサー率41% (2512/6032)
回答No.5

「配列が、完全型」と「配列の要素が、完全型」は意味が違います。 int ary1[P][N][M]; // 配列が、完全型、紛らわしいので「O」の使用は避けること int ary2[][N][M]; // 配列の要素が、完全型(要素は「int[N][M]」) int ary3[N][M]; // 配列が、完全型 int ary4[][M]; // 配列の要素が、完全型(要素は「int[M]」) int ary5[M]; // 配列が、完全型 int ary6[]; // 配列の要素が、完全型(要素は「int」) ary1は「int[N][M]がP個ある」です。 ary2は「int[N][M]が何個かある」のさえ判れば、各要素にアクセス出来ます。「最大、何個あるか?」は気にしなくて良いのです。 ary3は「int[M]がN個ある」です。 ary4は「int[M]が何個かある」のさえ判れば、各要素にアクセス出来ます。「最大、何個あるか?」は気にしなくて良いのです。 ary5は「intがM個ある」です。 ary6は「intが何個かある」のさえ判れば、各要素にアクセス出来ます。「最大、何個あるか?」は気にしなくて良いのです。 一方 int ary7[][]; は「何個か数が不明の、複数のintが並んだ、int[]が何行かある。ary7[0][0]は先頭だから良いが、ary7[2][1]はどこか判らない」と言う状態になります。 メモリ中、この配列は int int int int int int int int ....(何個あるか不明) int int int int int int int int ....(何個あるか不明) int int int int int int int int ....(何個あるか不明) int int int int int int int int ....(何個あるか不明) | (何行あるか不明) となります。この状態で「3行目の左から2番目」って言われても、アドレスを特定出来ません。 なので「int[]のサイズが未知でコンパイル不能」とのエラーが出るのです。

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.4

> 完全型とはint [N][M]のように配列の各添え字をすべてうめることでしょうか? 完全型自体はそういう意味です。 ただし、ここでは、配列の要素が完全型でなければならないということですので、int [][M]でも大丈夫です(配列型int [][M]の要素はint[M]型です)。

  • tig33
  • ベストアンサー率50% (6/12)
回答No.3

jactaさんが述べているように、教科書が間違っています。 二次元配列が、メモリーにどう展開されるかを理解すれば簡単です。 #define N 3 #define M 4 int x[N][M]; 上記配列は、メモリーに以下のように展開されます。 1 : x[0][0] 2 : x[0][1] 3 : x[0][2] 4 : x[0][3] 5 : x[1][0] 6 : x[1][1] 7 : x[1][2] 8 : x[1][3] 9 : x[2][0] 10: x[2][1] 11: x[2][2] 12: x[2][3] これで、sum(int x[][])という関数内で、 例えばx[1][0]の値を取り出そうとした場合、 行列の列数Mが分からないと、x[1][0]が行列の先頭から 5番目を取り出すことができません。 したがって、関数の定義部分で、sum(int x[][M])とする 必要があるんですね。 蛇足ですが、K6R準拠の古いコンパイラーでは、列数を 定義しなくてもコンパイルは通ったように思います。 (確か、main(int argx, char argv[][] {...}のように定義できた!?)

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.2

> また、教科書に関数のプロトタイプ宣言ではint sum(int [][]);だけでもよいと書かれていたのですが 教科書が間違っています。 GCCなど一部の処理系ではコンパイルできますが、規格上は、配列の要素は完全型でなければなりません。

fixmania
質問者

お礼

返信ありがとうございます。 完全型とはint [N][M]のように配列の各添え字をすべてうめることでしょうか? また、列の添え字を埋めるだけで正常にコンパイルされるのはなぜでしょうか? 質問ぜめですみません。

  • Yanch
  • ベストアンサー率50% (114/225)
回答No.1

>また、教科書に関数のプロトタイプ宣言ではint sum(int [][]);だけでもよいと書かれていたのですが >実際、コンパイルしてみたところ 'int[]' 型のサイズは未知あるいはゼロとエラーがでました。 コンパイルエラーは、本当に、関数のプロトタイプ宣言のところで起きていますか? 別の行で、起きているエラーなのではないかと予想します。

fixmania
質問者

お礼

返信ありがとうございます。 >コンパイルエラーは、本当に、関数のプロトタイプ宣言のところで起きていますか? 恐らくそうだと思います。そこを修正すると正常にコンパイルされるので・・・