プロが教えるわが家の防犯対策術!

↓が自分の作ったマージソートのプログラムなのですが、コンパイルするとエラーが起きてしまいます。
mergesort()にポインタを引数として渡してる、引数の数が足りない、ということが書いてありますが…。
ちゃんとint型を渡してるし、引数の数も合ってるように思います。
どこがおかしいのでしょう?

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define Max 255

int A[Max];

main(){
int n,k;
n=inputdata();
int w=1;
mergesort(w,n);
printdata(n);
return(0);
}

inputdata(){ //配列に乱数を要素として入れていく
int n,i;
printf("n= ");
scanf("%d",&n); //使用者にいくつの要素を入れるか指定してもらう
srand(time(NULL));
for(i=1; i<=n; i++){
A[i]=1+rand()%30;
}
printf("A[%d]={%d,",n,A[1]);
for(i=2;i<n;i++) printf("%d,",A[i]);
printf("%d}\n",A[n]);
return(n);
}

void mergesort(int p, int r){
int q;
if(p<r){
q=(p+r)/2;
mergesort(p,q);
mergesort(q+1,r);
merge(p,q,r);
}
}

void merge(int p, int q, int r){
int i,j,k,B[Max];
i=p; j=q+1;
for(k=p;k<=r;k++){
if((j>r) || ((i<=q)&&(A[i]<=A[j]))){
B[k]=A[i]; i++;
}else{
B[k]=A[j]; j++;
}
}
for(k=p; k<=r; k++) A[k]=B[k];
}

printdata(int n){
int i;
printf("A[%d]={%d,",n,A[1]);
for(i=2; i<n; i++) printf("%d,",A[i]);
printf("%d}\n",A[n]);
}


・エラーメッセージ
merge1.c: In function ‘main’:
merge1.c:12: warning: passing argument 1 of ‘mergesort’ makes pointer from integer without a cast
merge1.c:12: error: too few arguments to function ‘mergesort’
merge1.c: At top level:
merge1.c:31: error: conflicting types for ‘mergesort’
/usr/include/stdlib.h:294: error: previous declaration of ‘mergesort’ was here
merge1.c:41: warning: conflicting types for ‘merge’
merge1.c:37: warning: previous implicit declaration of ‘merge’ was here

A 回答 (5件)

>どこがおかしいのでしょう?



書いている順番がおかしい。

まず、mainが一番上にあり、呼ばれる関数本体が定義される前に呼び出しをしているのが悪い。

この場合、Cコンパイラは「関数が定義されていないので、引数と返り値を勝手に予想」してコンパイルしてしまう。

で、関数の本体が現れた段階で「予想したのと違う」って言って、エラーになったりワーニングになったりする。

それと、運悪く、stdlib.hに「既にmergesortというのが定義されてて、名前が衝突している」ので、mergesortと言う名前の関数は使えない。

もう一つは、mainで
main(){
int n,k;
n=inputdata();
int w=1;
とやってて、実効コードが現れた後で、変数の宣言をしようとしているので、ここでエラーになる。

他にも「intを返す関数に、戻り値の型を書いてない」」とか「引数無しの関数に、引数が無い事を示すvoidと書かれてない」とか、色々問題点が山盛り。

で、それらを全て修正し、ワーニングが出ないようにしたのが、以下のソースコード。

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define Max 255

int A[Max];

/*関数は呼ばれる前に実体を定義する事*/
/*inputdataはmainから呼ばれるので、mainより先に関数本体を定義する事*/
/*intを返す関数には、ちゃんと「intを返す」と書く事*/
/*引数が無い場合は引数無しを明示するため、引数に「void」と書く事*/
int inputdata(void){ //配列に乱数を要素として入れていく
int n,i;
printf("n= ");
scanf("%d",&n); //使用者にいくつの要素を入れるか指定してもらう
srand(time(NULL));
for(i=1; i<=n; i++){
A[i]=1+rand()%30;
}
printf("A[%d]={%d,",n,A[1]);
for(i=2;i<n;i++) printf("%d,",A[i]);
printf("%d}\n",A[n]);
return(n);
}

/*関数は呼ばれる前に実体を定義する事*/
/*mergeはmerge_sortから呼ばれるので、merge_sortより先に関数本体を定義する事*/
void merge(int p, int q, int r){
int i,j,k,B[Max];
i=p; j=q+1;
for(k=p;k<=r;k++){
if((j>r) || ((i<=q)&&(A[i]<=A[j]))){
B[k]=A[i]; i++;
}else{
B[k]=A[j]; j++;
}
}
for(k=p; k<=r; k++) A[k]=B[k];
}

/*関数は呼ばれる前に実体を定義する事*/
/*merge_sortはmainから呼ばれるので、mainより先に関数本体を定義する事*/
/*mergesortと言う関数名は使えないので変更する事*/
void merge_sort(int p, int r){
int q;
if(p<r){
q=(p+r)/2;
merge_sort(p,q);
merge_sort(q+1,r);
merge(p,q,r);
}
}

/*関数は呼ばれる前に実体を定義する事*/
/*printdataはmainから呼ばれるので、mainより先に関数本体を定義する事*/
/*返り値が無いのなら、関数の戻り値の型をvoidにする事*/
void printdata(int n){
int i;
printf("A[%d]={%d,",n,A[1]);
for(i=2; i<n; i++) printf("%d,",A[i]);
printf("%d}\n",A[n]);
}

/*mainを呼ぶ人は居ないので、必然的にmainの実体は一番最後に書く事になる*/
/*intを返す関数には、ちゃんと「intを返す」と書く事*/
/*引数が無い場合は引数無しを明示するため、引数に「void」と書く事*/
int main(void){
int n,k;
int w=1; /*変数の宣言は実効コードより前に行う事*/
n=inputdata(); /*これは実効コード。これ以降は変数の宣言は出来ない*/
/*int w=1; 実効コードの後に変数は宣言できない*/
merge_sort(w,n);
printdata(n);
return(0);
}

どうだろうか?これでエラーもワーニングも出ない筈。

質問者さんは「どのプログラムを見ても、mainが一番最後にあるな。なんでだろう?」って思った事は無いだろうか?

これは「関数を参照(呼び出し)する前に、関数の実体を定義しておく」と言う書き方をしているから。

普通、変数は
int i;
と定義を済ませた後で
i=0;
と言うように参照や代入を行う。

関数もこれと同じで
int inputdata(void){
中身
}
と定義を済ませた後で
int main(void){
(略)
n=inputdata();
というように参照(呼び出し)を行う。

Cのお約束の「定義してから使う」って言うのは、関数にも当てはまる話なのだ。
--------------------------------
ここから蛇足。読み飛ばしても可。

もし「mainが最初に無いと、なんか気持ち悪い」と言うなら、実体が定義される前の関数を正しく呼べるように、関数のプロトタイプ宣言を行ってから、実体が後から定義される関数を呼べば良い。

すると、以下のようになる。

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define Max 255

int A[Max];

/*使う関数をプロトタイプ宣言する*/
int inputdata(void);
void merge_sort(int p, int r);
void merge(int p, int q, int r);
void printdata(int n);

main(){
int n,k;
int w=1; /*変数の宣言は実効コードより前に行う事*/
n=inputdata(); /*これは実効コード。これ以降は変数の宣言は出来ない*/
/*int w=1; 実効コードの後に変数は宣言できない*/
merge_sort(w,n);
printdata(n);
return(0);
}

int inputdata(void){ //配列に乱数を要素として入れていく
int n,i;
printf("n= ");
scanf("%d",&n); //使用者にいくつの要素を入れるか指定してもらう
srand(time(NULL));
for(i=1; i<=n; i++){
A[i]=1+rand()%30;
}
printf("A[%d]={%d,",n,A[1]);
for(i=2;i<n;i++) printf("%d,",A[i]);
printf("%d}\n",A[n]);
return(n);
}

void merge_sort(int p, int r){
int q;
if(p<r){
q=(p+r)/2;
merge_sort(p,q);
merge_sort(q+1,r);
merge(p,q,r);
}
}

void merge(int p, int q, int r){
int i,j,k,B[Max];
i=p; j=q+1;
for(k=p;k<=r;k++){
if((j>r) || ((i<=q)&&(A[i]<=A[j]))){
B[k]=A[i]; i++;
}else{
B[k]=A[j]; j++;
}
}
for(k=p; k<=r; k++) A[k]=B[k];
}

void printdata(int n){
int i;
printf("A[%d]={%d,",n,A[1]);
for(i=2; i<n; i++) printf("%d,",A[i]);
printf("%d}\n",A[n]);
}

--------------------------------
ここから蛇足の蛇足。

1番目の修正ソースのように「呼ぶ前に関数本体を定義する」と言う書き方をすると、関数プロトタイプ宣言は不要な筈。

しかし「2つの関数が、お互いを相互に呼び出す」と言う場合、どうしてもどちらか1つの関数は「呼んだ後に関数本体を定義する」と言う事になる。

そういう場合は「関数プロトタイプ宣言」が必須となる。

例:
int func2(int a):/*プロトタイプ宣言*/

int func1(int a){
(略)
if (a > 1) return func2(a - 1);
return 0;
}

int func2(int a){
(略)
if (a > 1) return func1(a - 1);
return 0;
}

例を見て判る通り、func1とfunc2の関数の場所を逆にしても、やはり「呼んだ後に関数本体を定義する」のが避けられない。

なので、こういう場合には「関数プロトタイプ宣言」が欠かせない。
    • good
    • 0
この回答へのお礼

詳しい解説ありがとうございます。
プロトタイプ宣言でコンパイラにちゃんと教えてあげないと駄目なんですね。

お礼日時:2009/07/03 13:47

本質は既に書かれている通り「ISO C にも POSIX にも定義されていない mergesort という関数が stdlib.h で宣言されている」 (とエラーメッセージにもちゃんと書いてある) ことなので, 関数名を変えるか (可能なら) 適切なコンパイラオプションでつぶすかすることになる.


以下余談:
ちと調べてみると BSD系では mergesort が定義されている>#4.
ついでにいうと今の ISO C をまじめに取り入れているコンパイラなら「実効コードの後に変数は宣言できない」という制限はない>#2. なぜか知らんがいまだに ISO C に対応しようとしない (ということは事実上 C++0x にも準拠しないことになる) MS なコンパイラではそのような制限があるが, どっちかといえばこれは 「MS なコンパイラ」が腐っていると思うべき.
    • good
    • 0

追記。



>それと、運悪く、stdlib.hに「既にmergesortというのが定義されてて
>名前が衝突している」ので、mergesortと言う名前の関数は使えない。

他の一般的なC、C++では「mergesortと言う関数は標準関数として定義されていない筈」で、普通は、自前でmergesortと言う関数を作っても何も問題は起きない筈。

質問者さんが使っているコンパイラには、なぜか「mergesortと言う識別子がstdlib.hに定義済み」で、標準から逸脱した変なコンパイラらしいので、使用する場合は注意が必要だと思われます。

出来れば「変なものが定義されてない、もっと標準的なコンパイラ」を使った方が良いでしょう。
    • good
    • 0

#define Max 255の次の行に、以下の行を追加して下さい。


void mergesort(int p, int r); //これを追加
void merge(int p, int q, int r); //これを追加
    • good
    • 0

通りすがりです。




>↓が自分の作ったマージソートのプログラムなのですが、
>コンパイルするとエラーが起きてしまいます。
>mergesort()にポインタを引数として渡してる、
>引数の数が足りない、ということが書いてありますが…。
>ちゃんとint型を渡してるし、引数の数も合ってるように思います。
>どこがおかしいのでしょう

正直、引数が違うとかの問題ではないかと思いますが。

.netでコンパイルしてみたので確認してください。
ちなみ、ソースは質問者様の書いたものをそのままコンパイルしました。
#include<stdio.h> が 1行目になります。

==============================================================

mergesort.cpp(8) : error C4430: 型指定子がありません - int と仮定しました。メモ: C++ は int を既定値としてサポートしていません
mergesort.cpp(10) : error C3861: 'inputdata': 識別子が見つかりませんでした
mergesort.cpp(12) : error C3861: 'mergesort': 識別子が見つかりませんでした
mergesort.cpp(13) : error C3861: 'printdata': 識別子が見つかりませんでした
mergesort.cpp(17) : error C4430: 型指定子がありません - int と仮定しました。メモ: C++ は int を既定値としてサポートしていません
mergesort.cpp(17) : error C2365: 'inputdata' : 再定義; 以前の定義は '以前は不明な識別子' でした。
mergesort.cpp(21) : warning C4244: '引数' : 'time_t' から 'unsigned int' への変換です。データが失われる可能性があります。
mergesort.cpp(31) : error C2365: 'mergesort' : 再定義; 以前の定義は '以前は不明な識別子' でした。
mergesort.cpp(35) : error C3861: 'mergesort': 識別子が見つかりませんでした
mergesort.cpp(36) : error C3861: 'mergesort': 識別子が見つかりませんでした
mergesort.cpp(37) : error C3861: 'merge': 識別子が見つかりませんでした
mergesort.cpp(41) : error C2365: 'merge' : 再定義; 以前の定義は '以前は不明な識別子' でした。
mergesort.cpp(54) : error C4430: 型指定子がありません - int と仮定しました。メモ: C++ は int を既定値としてサポートしていません
mergesort.cpp(54) : error C2365: 'printdata' : 再定義; 以前の定義は '以前は不明な識別子' でした。
mergesort.cpp(59) : warning C4508: 'printdata' : 関数に戻り値の型が指定されていません。戻り値を void 型と見なします。
mergesort - エラー 13、警告 2

========== ビルド: 0 正常終了、1 失敗、0 更新、0 スキップ ==========



ご自身で確認してみてください。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!