エラー: expected yyy before xxx
xxxの直前に、文法上の誤りがある。
xxx から遡ってコードを確認し、誤っているところを修正する。 おそらく yyy が抜けているか、 xxx と yyy の間に余計なものが入っている。
for
やif
などの予約語をスペルミスしていたり、
変数名・関数名に予約語を使っていたりしないか。
例:
int main(void){ int fir; for = 0; id (fir) fir = -1; }
3行目の
=
および5行目の
fir
の前で文法エラーが出る。
前者は変数
fir
をスペルミスして予約語
for
になっているため、
for
文を開始しようとしていると見なされる。
for
文なら予約語
for
の後に丸括弧がこなければならないが
=
が現れたため、文法エラーになる。
後者は予約語
if
をスペルミスしたため、
これは(すぐ後に丸括弧があるので)関数
id
の呼び出しと見なされる。
この場合、閉じ丸括弧までは関数の引数に見えるのでエラーにならないが、
関数呼び出しなら閉じ丸括弧の後にセミコロンが必要である。
上記コードではセミコロンなしで次行の代入文に続いているので、
その時点で文法エラーになる。
なお、 後者のパターンでは、 予約語をスペルミスした結果が偶然存在する関数名になっていない限り、 未定義関数の警告も併せて出るので見つけやすい。 上記の例では、警告 「関数 ‘id’ の暗黙的な宣言です」が出る。
この場合の直前とは、 プログラム先頭から見てxxxの直前(の意味のある要素)なので、 xxxが行頭ならその上の行、 直前が空行ならさらに上の行の可能性もある。
例:
int a
return 0;
「expected '=', ',', ';', 'asm' or '__attribute__' before 'return'」 と指摘されるが、 実際に間違っているのは2行上である(宣言後にセミコロンが抜けている)。
また、xxxはコンパイラが誤りに気づいた場所なので、 実際に誤っているのはもっと前の可能性もある。
例:
int main(void){ int i, j; for (i = 0 ; i < 10 ; i++){ for (j = 0 ; j < 10 ; j++){ } }
内側のforに対応する「}」が抜けているが、 「expected declaration or statement at end of input」 と指摘される。 コンパイラは「}」を一つずつ(書き手の意図と)ずれて対応させていったものの、 末尾に到達した時点で「}」が足りなくなったため、 この時点でエラーと認識している。
さらに、マクロの場合には定義時点でエラーチェックは行われず、 展開された場所で(展開結果が文法に反していれば)エラーになる。
例:
#define N 100; int main(void){ int i, d[N]; for (i = 0 ; i < N ; i++) d[i] = i; }
マクロ
N
の値として100を定義したが誤って行末にセミコロンを書いたため、
マクロ
N
は「100;
」に置き換えられる。
このため、マクロを定義している1行目ではエラーが出ず、
int i, d[100;];
や
for (i = 0 ; i < 100; ; i++)
などと置き換えられる3,4行目で文法エラーになる。
エラー: 'var' が宣言されていません (この関数内での最初の使用) 備考: 未宣言の識別子は出現した各関数内で一回のみ報告されます
error: 'var' undeclared (first use in this function) note: each undecleared identifire is reported only once for each function it appears in
プログラム中で変数varを使用しているのに、 その宣言を行っていない。
その変数を使用しているなら、宣言文を追加する。
スペルミスにより実在しない変数を書いてしまった場合も、 「使用しているのに宣言のない変数」と見なされる。
例
int main(void){ int i, data[10]; data[0] = 1; for (i = 1 ; i < 10 ; i++){ data[i] = date[i-1] * 2; } }
「'date' が宣言されていません (この関数内での最初の使用)」と指摘されるが、
この場合は
date
の宣言が抜けているのではなく、
data
と書くつもりでスペルミスをしている。
この場合は当然、
date
の宣言を追加するのではなく、
スペルミスを修正しなければならない。
また、構造体のメンバ変数を参照する際に、 誤ってメンバ変数名だけを書いてしまった場合も、 (偶然同名の変数が宣言されていなければ)このエラーになる。
例
typedef struct { int x, y; } point_t; int main(void){ point_t p; x = 1; }
構造体型のメンバ
x
に代入しようとしたが、
この書き方では
x
という変数が対象となってしまう。
変数として宣言しているのは
p
だけなので、
p.x = 1;
のようにして
p
のメンバ
x
が対象であることを示さなければならない。
/tmp/yyyyyyyy.o: 関数 `wwww' 内: yyyyyyyy.c:(.text+0xzzzz): `xxx' に対する定義されていない参照です collect2: エラー: ld はステータス 1 で終了しました
/tmp/yyyyyyyy.o: in function `wwww': yyyyyyyy.c:(.text+0xzzzz): undefined reference to `xxx' collect2: error: ld returned 1 exit status
変数または関数xxxを使用しているのに、 宣言や定義がない。
変数/関数xxxを実際に使っていて、 宣言や定義をしていなければ、 追加する。
変数の使用や関数の呼び出し時にスペルミスをしていると、 その(実在しない)変数/関数が見つからないとしてこのエラーになる。
例
int main(void){ prinft("hello\n"); }
このプログラムはprintf関数の呼び出しで、 関数名のスペルミスをしている。 当然、prinftという関数は存在しないが、 ライブラリや別のソースファイルに存在しているかもしれないので、 コンパイルの段階ではエラーにならない (ただし、「Implicit declaration」という警告は出る)。 そしてリンク時に、prinftがどこにも見つからないため、 prinftが未定義のシンボルと指摘される。
指摘されたシンボルに心当たりがなければ、 ソースファイル中を検索し、 スペルミスしている箇所を修正すればよい。
実際に定義している関数なのに、このエラーが出る場合は、 該当関数の定義が他の関数定義の内側に入り込んでいる可能性がある。
たとえば、以下のコードはコンパイルを通るが、
リンク時に関数
f
が未定義シンボルになる。
int f(int n); int main(void){ int i; for (i = 0 ; i < 10 ; i++){ printf("%d^2 = %d\n", i, f(i)); } int f(int n){ if (n%2) { return n*n; } else return n; } }
このコードは一見正しく見えるが、
main
関数内の
for
ブロックが閉じておらず、
逆に関数
f
内では
else
節の後に余分な閉じ中括弧がある。
このため、偶然括弧の対応がとれてしまいコンパイラが受理してしまうが、
関数
f
の定義全体が
main
の定義内に入り込んでしまっている。
以下のように正しく中括弧を付け直すことで、このエラーを解決できる。
int f(int n); int main(void){ int i; for (i = 0 ; i < 10 ; i++){ printf("%d^2 = %d\n", i, f(i)); } } int f(int n){ if (n%2) { return n*n; } else return n;}}
エラー: 'func' と型が競合しています 備考: 前の 'func' の宣言はここです
error: conflicting types for 'func' note: previous declaration of 'func' was here
あるいは
エラー: 'func' と型が競合しています 備考: 前の 'func' の暗黙的な宣言はここです
error: conflicting types for 'func' note: previous implicit declaration of 'func' was here
関数funcに関して、プロトタイプ宣言、定義、 呼び出しの間に型の矛盾がある。
呼び出しより前の行に定義もプロトタイプ宣言もないため、 コンパイラがint型を仮定したが (「関数...の暗黙的な宣言」参照) 間違っていた。
あるいは、ユーザがプロトタイプ宣言、定義、呼び出しのいずれかで、 間違った型を指定した。
警告「関数...の暗黙的な宣言」 が出ているなら、そちらを修正すればおそらくこのエラーも消える。
単独でこのエラーが出ている場合は、 エラーメッセージ中、 「'func' と型が競合しています」 に表示されている行と、 「前の 'func' の宣言はここです」 に表示されている行を確認し、 正しい方の型に統一する。
例
void square(int n); int main(void){ int n; n = square(10); } int square(int n){ return n*n; }
関数squareの返り値の型が、 プロトタイプ宣言と実際の定義で食い違っている。 この場合、 (整数値をreturnしているため)プロトタイプ宣言の方を直せばよい。
int square(int n); int main(void){ int n; n = square(10); } int square(int n){ return n*n; }
なし
エラー: 関数 'func' への引数が多すぎます
error: too many arguments to function 'func'
または
エラー: 関数 'func' へ渡す引数が少なすぎます
error: too few arguments to function 'func'
関数の呼び出し時に引数の数が少ない/多い。
funcがライブラリ関数であれば、 呼び出し側の誤りなので、manコマンドなどで必要な引数を確認して 修正する。
funcが自分で定義した関数であれば、 同様に呼び出し側が誤っている場合の他、 定義側の仮引数が間違っている(追加した引数を書き忘れた、 不要になった仮引数を消し忘れたなど) 可能性もある。この場合は定義側を修正する。
例
double vec_size(double x, double y){ return sqrt(x * x + y * y + z * z); } int main(void){ double x=10, y=20, z = 5, l; l = vec_size(x, y, z); }
関数vec_sizeはベクトルの大きさを返す関数であるが、 最初に2次元用に作成した後、3次元用に変更した。 このときに大きさの計算式は修正したが仮引数zの追加を忘れたため、 引数を2つとる関数のままである。この場合、呼び出し側の行で 「引数が多すぎます」と警告される。
printf関数のように引数の個数が可変のものを除き、 C言語の関数は必ず宣言した数の引数を与えなければならない。 仮引数を持つ関数は引数を省略できないし、 引数なしで呼びたい関数は定義時に引数なし(void)と宣言しておく必要がある。
エラー: 代入の左側の被演算子として左辺値が必要です
error: lvalue required as left operand of assignment
代入の右辺は様々な計算式を書けるが、左辺は代入先の変数でなければならない。
正確には、「代入先の変数を示す値」であればよいので、 以下のような記述ができる。
例
int x, d[10]; int *p = &x, *q = d; x = 1+2; d[3] = x+1; *p = 10*20; *(q+3) = 5;
d[3]はint型配列中の一要素を表しているので、 整数値を一つ代入できる。 pやq+3も、 int型の変数を指すポインタなので、 それに*演算子をつけると指す先の変数を意味し、 やはり整数を代入可能である。
一方で、以下のような記述はすべて許されず、このエラーになる。
例
int x, d[10]; int *p = &x, *q = d; x+1 = 1+2; d[3]*2 = x+1; *q+1 = 10*20;
x+1やd[3]*2を計算した結果は整数値であり、 特定の変数を表しているわけではないので代入できない。 *q+1はd[1]を表しているように見えるが、 演算子の優先度から(*q)+1と解釈されるため、 d[0]+1と同じ意味であり、やはり代入できない。
無効な左辺値に代入しようとすることは考えにくいので、 通常このエラーは、 誤って代入を書いたか、 左辺値の書き方を間違えたかのどちらかである。
前者の典型的な例は、比較演算子と代入演算子の書き間違いである。
例
if (x*x = 16)
このコードはif (x*x == 16)と比較をするつもりでタイプミスし、 等号を一つ書き漏らしている。 この場合、比較式ではなく代入式と見なされるため、このエラーになる。
後者は上記の*q+1の例のように、 ポインタ使用時に優先度の誤解や括弧の不足などによって起きる。
なし。
エラー: 型 ‘xxx’ への型 ‘yyy’ からの代入時に互換性のない型です
代入の左辺と右辺の型が合っていない。
数値同士であれば暗黙の変換が行われるし、 ポインタ値と非ポインタ値の混在であれば 「代入で...からキャスト無しに...を作成」 になる。 このエラーの典型的な例は、 配列変数として宣言したのに添え字をつけずに代入しようとした場合である。
例
int d[10]; d = 1;
添え字の付け忘れを確認する。
なし。
エラー: 変数またはフィールド ‘var’が void と宣言されています
変数(あるいは構造体のメンバなど)
var
の型がvoid
で宣言されている。
void
は、関数の返値や引数がないことを示すものであり、
int
や
double
のように、特定の変数の型にすることはできない。
例
void j;
void
で宣言している変数などを、正しい型で宣言する。
あるいは、下記のvoid
ポインタを宣言したいのであれば、
*
を付加する。
ポインタについては、以下のような宣言ができる。
void *p;この場合の
void
は、
「任意の型のポインタを代入できるポインタ」であることを意味している。
エラー: void の配列としての ‘var’の宣言です
変数
var
がvoid
型の配列として宣言されている。
void
を変数の型にすることはできない
(「変数またはフィールド...がvoidと宣言」参照)
ので、
当然
void
型の配列も作れない。
例
void d[10];
void
で宣言している配列変数を、正しい型で宣言する。
あるいは、void
ポインタの配列を宣言したいのであれば、
*
を付加する。
ポインタについては、以下のような宣言ができる。
void *p[10];これは、「任意の型のポインタを代入できるポインタ」を10個並べた、 ポインタの配列である。
エラー: ‘type’ は ‘mem’ という名前のメンバを持っていません
存在しない構造体メンバ mem に対する参照/代入が行われている。
型type
は構造体型であるがmemという名前のメンバを持たない。
あるいはvar->
mem
という記述がある場合、
ポインタvar
が指す型は構造体であるがmemという名前のメンバを持たない。
ミスの内容としては、おそらく以下のいずれかのケースである。
ケース1,2の場合は構造型の定義を確認し、書き間違いや欠落部分を修正する。 ケース3の場合は、その変数の型とやりたいことを確認したうえで、 参照自体をやめる/ s2_t型の別の変数に変更する/ varをs2_t型で宣言する、などの対応をとる。
例
typedef struct { int x, y; } point_t; typedef struct { int x, y, color; } cpoint_t; typedef struct { point_t lt, rb; } rect_t; int main(void){ point_t p; rect_t r; point.c = 1; r.x = 1; p.color = 0xffff00; }
上記コード末尾の3行の代入文は、すべてこのエラーがでる。
最初の代入文では
point_t
型のメンバ
x
への代入がタイプミスにより
c
になっているが、
この名前のメンバは存在しないためエラーになる。
次の代入文では、四角形の左上隅のx座標を1にしようとしたが、
rect_t
型は左上隅、右下隅の座標を表す
point_t
型のメンバしか持たず、
x
という名前のメンバは存在しないためエラーになる。
このようなネスト構造の場合は、
「r
のメンバの
lt
のメンバの
x
」
というように順に並べる必要がある。
最後の代入文では、
p
が表す点に色を設定しようとして
color
メンバに代入している。
型の定義を見ると
cpoint_t
型には
color
メンバがあるが、
p
は
point_t
型で宣言されており、この型には
color
メンバはない。
したがって、
この点に色情報を持たせる必要があるならば、
p
を
cpoint_t
型で宣言しておく必要がある。
したがって、修正したコードは以下のようになる(修正行のみ)。
cpoint_t p; <略> point.x = 1; r.lt.x = 1;
構造体型を指すポインタ変数の場合も、 このエラーが出る場合がある。 この場合も、 ポインタ変数が指す先の構造体型について、 同様に考えて対処すればよい。
例
int main(void){ point_t p, *pp; pp = &p; pp->c = 1; }
pp->c
はポインタ変数
pp
が指している先の構造体のメンバ
c
を意味する。この場合は
pp
が
p
を指しているから、
このメンバ参照は
p.c
と同じ意味になり、
前述のコードと同じエラーになる。
エラー: 構造体または共用体ではない何かのメンバ ‘mem’ の要求です
構造体型でない変数に対し、メンバ参照を行った。
int
型など構造型でない変数に対してメンバ参照を行いたい状況は考えにくいので、
ミスの内容として、以下のケースが考えられる。
構造体型の変数に対しメンバ参照を行おうとしたのに、
変数名のスペルミスなどで違う型の変数が対象になってしまった場合(ケース1)は、
変数名を修正する。
構造体として使うつもりの変数でありエラーになったメンバ参照が正しくても、
その変数の宣言時に誤って
int
型などで宣言してしまった場合(ケース2)もこのエラーになる。
その場合は変数宣言時の型を正しく修正する。
また、
(まれなケースと思われるが)
他の記号などをタイプミスしてピリオドを入力してしまった場合も、
それがメンバ参照演算子と解釈されてこのエラーになる可能性がある。
例
typedef struct { int x, y; } point_t; void plot(int x, int y); int main(void){ int a=2, x=10, y; int p; point_t q; a.x = 1; // 点Qのx座標は1 p.x = 1; // 点Pのx座標も1 y = x.a; // y座標はx座標/aに設定 plot(x. y); }
上記コードの代入文および関数呼び出しは、すべてこのエラーになる。
最初の代入文は
q
のメンバに代入しようとして
a
とタイプミスしているが、偶然
int
型の
a
が存在していたため、このエラーになる。
次の代入文は、点Pのx座標を設定したかったのだが、
誤って
p
を
int
型で宣言しているため、やはりこのエラーになる。
3番目の代入文は、単なる整数型変数同士の計算式だったのが、
`/
'
を
`.
'
とタイプミスしたため、メンバ参照と誤認識されこのエラーになっている。
同様に、最後の関数呼び出しも
`,
'
を
`.
'
とタイプミスしたため、やはりメンバ参照と誤認識されている
(このミスについては、引数が足りなくなるため
「
引数が多すぎ/少なすぎます」も出る)。
したがって、修正したコードは以下のようになる(修正行のみ)。
point_t p; <略> q.x = 1; // 点Qのx座標は1 p.x = 1; // 点Pのx座標も1 y = x/a; // y座標はx座標/aに設定 plot(x, y);
構造体型を指すポインタ変数に対し、
構造体型変数と同様に
`.
'
演算子によりメンバ参照を行おうとした場合もこのエラーになる。
ポインタ変数内には対象のアドレスが格納されているだけで、
メンバ自体が含まれていないためである。
例
int main(void){ point_t p, *pp; pp = &p; pp.x = 1; }
pp
はポインタ変数であるので、メンバは持たない。
pp
が指す先のメンバ
x
を参照したいのであれば、
pp->x
あるいは
(*pp).x
のように書く。
エラー: 二項演算子 op への無効な被演算子です (‘type1’ と ‘type2’)
演算子opに対し、 その演算が適用できない式 (型type1とtype2) が与えられている。
演算子opの前後の式について、 意味的にその演算が適用できるか確認する。
たとえば、構造体には四則演算子(
+
,
-
,
*
,
/
)や
関係演算子(
==
,
!=
,
<
など)は適用できない。
例
typedef struct { int x, y; } vector_t; int main(void){ int n; vector_t v1={1, 2}, v2={3,1}; if (v1 != v2){ // 同一ベクトルでなければ n = v1*v2; // 内積を計算 } }
上記のコードのように2次元ベクトルを表す構造体型
vector_t
を定義したとする。
この型の変数に対し、
==
,
!=
で同一ベクトルかどうかの判定をしたり、
*
で内積(外積?)
を求めたりしたくなるが、
vector_t
はあくまでユーザが勝手に定義した型なので、
そのような書き方はできない。
以下の修正例のように、
「ベクトルの同一性判定」や「ベクトルの内積」の意味になるよう、
メンバ同士の比較や演算を書く必要がある。
typedef struct { int x, y; } vector_t; int dot(vector_t v1, vector_t v2){ return v1.x * v2.x + v1.y * v2.y; } int main(void){ int n; vector_t v1={1, 2}, v2={3,1}; if (v1.x != v2.x || v1.y != v2.y){ // 同一ベクトルでなければ n = dot(v1, v2); // 内積を計算 } }
なし
警告: 制御が非 void 関数の終りに到達しました
warning: control reaches end of non-void function
返り値がある(void型ではない)関数の中で、 returnせずに関数末に到達している。
例
int f(int n){ if (n != 0) return 0; }
関数fの返り値はint型なので、 必ず整数値を返さなければならない。 上記のコードでは、nの値が0のとき、 returnがないため返り値を返さずに関数を終了してしまう。
関数の定義内で、宣言した型の返り値をreturnする。
単なるreturnの書き忘れなら、 関数末に追加する。 すでにreturnがある場合は、 おそらく関数内にif文などがあり、 分岐の仕方によってはそのreturnを通らないようになっている。 その場合は、どう分岐しても必ずreturnに到達するように書く。
そもそも返すものがない場合は、関数をvoid型で宣言する。
main
関数は返り値が
int
型と定められているので、
整数値を返すようにしなければこの警告が出る
(main
関数自体を
void
型で宣言してはいけない)。
慣習的には、プログラムを正常終了させるときは0を返し、
処理を中断して異常終了(ファイルのオープンに失敗したなど)する場合は非0を返す。
警告: 戻り値の型をデフォルトの ‘int’ にします
warning: type defaults to 'int'
関数を定義する際に、返り値の型を指定していない。
例
f(int n){ return n*2; }
関数を定義する際に返り値の型を省略すると、 デフォルトで int 型として扱われ、この警告が出力される。 上の例のように、実際に int 型を変えす関数であれば実害はないが、 もし異なる型を返すコードを書いている場合は正常動作しない可能性がある。
関数の定義の際に、関数名の前に返り値の型を記述する。 とくに、返すものがない場合は、必ず関数をvoid型で宣言する。 未指定だとその関数は「int 型を返す」ことになってしまうからである。
なし。
警告: 真偽値として使われる代入のまわりでは、丸括弧の使用をお勧めします
warning: suggest parentheses around assignment used as truth value
if, whileなどの条件式に、代入文が書かれている。
上級者が意図的にやっているのでない限り、 比較演算子「==」の代わりに誤って代入演算子「=」を書いている。
例:
if (a=1){
おそらく「aの値が1のとき」と書くつもりだったが、 「aに1を代入」している。 文法的に誤りではないので、警告を無視すればプログラムを実行できるが、 この行でaの値が1になり、条件文は常に成立する (代入した値1が条件の真偽値: 非0なので真)。 したがって、おそらく誤動作する。
比較演算子の誤りであれば、修正する。
C言語では代入演算子による代入は「代入式」であり、 左辺に代入された値(つまり右辺の計算結果)が式の値となる。 一般的には、代入式単独で「代入文」として用いるが、 式の中に代入を埋め込むこともできる。
例:
if (a=b){
この例では、「bをaに代入」したうえで、 「bの値が真(非0)なら条件が成立」する。 一般的には「a==b」つまり 「aとbの値が一致したら条件が成立」 の書き間違いの可能性が高いので警告が出る。 意図的にやりたい場合は「if ((a=b)){」のように 代入式を括弧で括れば意図的なものだとコンパイラが認識し、警告は出なくなる。 しかし、コードがそれほど短くなるわけではなく、 読みにくく/勘違いしやすくなるデメリットの方が大きいので、 以下のように書く方がよい。
a=b; if (a){
ただしループ文の場合は、 条件式内で代入しない限り同じ代入を2箇所に書く必要になる場合がある。 とくに、以下のようなケースでは慣用句的に条件式内の代入を使うことが多い。
while ((c = fgetc(fp)) != EOF){...};
警告: 使用されない変数 ‘var’ です
warning: unused variable ‘var’
変数varの宣言があるが、一度も使っていない。
使うつもりで宣言したが、結局使っていない変数ならば、宣言を削除する。
使っていない変数があること自体はあまり実害がないので、 後で使うつもりで先に変数だけ宣言しておいた、 など心当たりがあるなら、 この警告は無視してもかまわない。
勘違いにより、変数名が途中ですり替わっているような場合は、 単純に宣言文を消しても問題は解決しない。
例
int main(void){ int i, j, a[10][10];
for (y = 0 ; y < 10 ; y++){ for (x = 0 ; x < 10 ; x++){ a[x][y] = x * y; } } }
この例では二重ループ用に変数i, jを宣言したのに、 コード中では変数x, yを使っている。 この場合は当然、「i, jが未使用」という警告と、 「x, yの宣言がない」というエラーが共に出る。
警告: 関数 ‘func’ の暗黙的な宣言です
関数funcを呼び出している行より前に、 この関数の定義も宣言もない。
この関数の呼び出し行より前に、この関数の定義を書く。
あるいは、プログラムの先頭にこの関数のプロトタイプ宣言を書く。
例
int main(void){ int n; n = square(10); } int square(int n){ return n*n; }
関数squareはint型引数をとり、 int型の値を返すものとして定義されている。 しかしコンパイラは上から順に処理するため、 3行目でsquareの呼び出しを発見した時点では、 この関数の引数や返り値の型がわからない。
これを防ぐには、先頭にプロトタイプ宣言を書いておく。
int square(int n); int main(void){ int n; n = square(10); } int square(int n){ return n*n; }
あるいは、squareの定義を先に書いておく。
int square(int n){ return n*n; } int main(void){ int n; n = square(10); }
コンパイラは未知の関数呼び出しを発見した時点で、 返り値や引数がintと仮定して処理を続行する。 この時点では警告にとどまるが、 あとで関数定義を見つけた時点で仮定と矛盾した場合は、 エラーとなる(「型が競合しています」参照)。
また、ライブラリ関数に対してこの警告が出る場合は、 おそらく必要なヘッダファイルのincludeを書き忘れている。 たとえば、include<stdio.h>を書かずにprintf 関数を呼び出すと、 「警告: 関数'printf'の暗黙的な宣言です」と指摘される。 これは、 stdio.hに printf関数などのプロトタイプ宣言が書かれているためである。
警告: ポインタと整数との比較を行なっています
比較式の左辺・右辺の一方がポインタ(アドレス式)で、 もう一方が整数型である。 異なる型だと気づかずに変数同士を比べている可能性もあるが、 初心者のミスのほとんどは、 ポインタ変数の扱いを間違っているか、 文字・文字列の混同である。
例
int n; int *p; if (*p == n){ ...} if (p == n){ ...} if (&p == n){ ...}
最初の if 文は、右辺がint型であり、 左辺もint型へのポインタ変数pに 間接参照演算子*を適用しているからint型 (pが指す先の値)である。よって整数同士の比較であり、正しい。
一方、2番目の if 文は、左辺がint型へのポインタであるから、警告が出る。 おそらく、原因は以下のどちらかである。
3番目については、 初心者であればおそらく&演算子と*演算子の混同である。
文字・文字列の混同については次の「対策」を参照。
指摘された行の比較式の両辺を確認し、整数・ポインタのどちらかに統一する。 どちらに統一しても警告は消えるが、 間違った方に統一してしまえば当然正しい結果は得られない。 必ず両辺の式の意味を考えて修正すること。
文字と文字列の混在は 単なる打ち間違いの場合もあるが、 両者の区別のついていない初心者がよくやるミスである。
例
char c; char *str; if (c == "a"){ ...} if (str == 'a'){ ...}
最初の if 文でこの警告が出るため、 「整数」も「ポインタ」も使っていないつもりの初心者は驚くことになる。 この場合の「整数」とはchar型(1バイト整数)であり、 右辺が文字列リテラルでchar型へのポインタになる。 おそらくはシングルクォートとダブルクォートの書き間違いか、 両者の違いを区別できていないのが原因である。 修正するには、ダブルクォートをシングルクォートに直せばよい。
2番目の if 文は逆にポインタ変数と文字定数(char型)を比較している。 この場合は、いくつかの可能性が考えられる。
警告の出ている行に原因があるため修正しやすいバグだが、 上で書いたように初心者の場合、 「ポインタになっている」「整数になっている」 ことに気づかないことが多い。 両辺の型が意図したものになっているか、よく確認すること。 間違ってもキャストで強引につじつま合わせを図ってはならない。
文字型変数に対する比較式であっても、 以下のコードのように、 複数文字で構成される明らかな「文字列」と比べているケースもある。 この場合、やりたいことが「"hello"と比較する」のであれば、 左辺を文字列を保持可能な型 (charへのポインタ型か配列) にしなければならないし、 文字列同士の比較であるから比較演算子の代わりにstrcmp()を 使わなければならない。
char c; if (c == "hello"){ ...}
警告: i 番目の ‘func’ の引数へ渡すときにYYYからキャスト無しにXXXを作成しています 備考: expected ‘type1’ but argument is of type ‘type2’
関数funcの第i引数には 型type1の値を渡す必要があるのに対し、 型type2を渡している。
(1バイト整数である文字型も含め)数値の場合は暗黙の型変換が行われるため、 この警告は以下のようにポインタ型引数に非ポインタ値を、 あるいは非ポインタ型引数にポインタ値を渡した場合に起きることが多い。
警告: 1 番目の ‘func’ の引数へ渡すときにポインタからキャスト無しに整数を作成しています 備考: expected ‘int’ but argument is of type ‘int *’
第i引数で要求されている型を確認し、 その型の値を渡すようにする。引数が複数ある場合、 引数の書き忘れなどで対応がずれた場合も、このエラーになる場合がある。
例void f(int old, int *new){ *new = old * old; } int main(void){ int x=0, y; f(x, &y); f(x, y); f(&y, x); }
関数fの定義では、 引数としてint型と int型へのポインタをとると宣言されている。 fの1回目の呼び出しはこれに合致しているが、 2回目の呼び出しは第2引数がint型 (おそらく&の付け忘れ) なので、以下のような警告が出る。
警告: 2 番目の ‘f’ の引数へ渡すときに整数からキャスト無しにポインタを作成しています 備考: expected ‘int *’ but argument is of type ‘int’
また、3回目の呼び出しはおそらく第1、第2引数を逆に書いてしまったため、 それぞれの引数について警告が出る。
警告: passing argument 1 番目の ‘f’ の引数を渡すときにポインタからキャスト無しに整数を作成しています 備考: expected ‘int’ but argument is of type ‘int *’ 警告: 2 番目の ‘f’ の引数へ渡すときに整数からキャスト無しにポインタを作成しています 備考: expected ‘int *’ but argument is of type ‘int’
ポインタ型変数を使っていない場合でも、 文字列を受け取る関数でこの警告が出る場合がある。 たとえばprintfは第1引数にフォーマット文字列、 つまりcharへのポインタを渡す必要がある。 通常は文字列リテラル ("hello"などダブルクォートで囲んだ文字列) を書くためポインタを意識しないが、 フォーマット文字列を書き忘れるとこの警告になる。
例
int main(void){ int n = 10; printf(n); }
フォーマット文字列が来るべき第一引数が int型のnになっているため、 以下のように警告される(さらに、警告「書式が文字列リテラルでは無く」も出る)。
警告: 1 番目の ‘printf’ の引数へ渡すときに整数からキャスト無しにポインタを作成しています 備考: expected ‘const char * __restrict__’ but argument is of type ‘int’ 警告: passing argument 1 of ‘printf’ makes pointer from integer without a cast note: expected ‘const char * __restrict__’ but argument is of type ‘int’
メッセージでは「YYYからキャスト無しにXXXを作った」 となっているが、ほとんどの場合はキャストしていないことではなく、 間違った型の引数を渡していることが問題である。 したがって、(意味を理解して意図的にやるのでない限り) キャストを書き加えることで修正を図ってはならない (警告は出なくなるが、実行時に誤動作する)。
警告: 初期化でYYYからキャスト無しにXXXを作成しています
YYYで宣言した変数に対し、初期化子として XXXの値を与えている。
初期化子として、変数の型に合った値を与えているか確認する。 もしその値を与える必要があるなら、 逆に変数を不適切な型で宣言していないか確認する。
例char c = "hello";
cは文字型であるのに対し、 初期化子として文字列リテラルを与えている。 これは文字列の先頭を指すcharポインタになるため、 このエラーになる。 "hello"という文字列を格納する必要があるなら、 以下のように宣言の方を変更する。
char *c = "hello";
あるいは、あとでこの文字列を書き換える必要があるなら、 以下のように配列として宣言する。
char c[6] = "hello";
メッセージでは「YYYからキャスト無しにXXXを作った」 となっているが、ほとんどの場合はキャストしていないことではなく、 間違った型の初期化子を渡していることが問題である。 したがって、(意味を理解して意図的にやるのでない限り) キャストを書き加えることで修正を図ってはならない (警告は出なくなるが、実行時に誤動作する)。
警告: 代入でYYYからキャスト無しにXXXを作成しています
XXXで宣言した変数に対し、 YYYの値を代入している。
左辺の型に合った値を持つ式になっているか、右辺を確認する。 もし右辺の値を代入する必要があるならば、 左辺の変数を不適切な型で宣言していないか確認する。 左辺がポインタを含む式の場合は、 左辺式の計算結果が意図したものになっているか確認する。
例int n, *p; p = &n; p = 10;
pはintへのポインタとして宣言しているので、 p自体には int型変数へのポインタ値(アドレス)しか格納できない。 3行目で10という整数値を代入しようとしているので、 このエラーとなる。 pの指す先(n)に10を格納したいのであれば、 以下のように書く。
int n, *p; p = &n; *p = 10;
メッセージでは「YYYからキャスト無しにXXXを作った」 となっているが、ほとんどの場合はキャストしていないことではなく、 両辺のどちらかの型が間違っていることが問題である。 したがって、(意味を理解して意図的にやるのでない限り) キャストを書き加えることで修正を図ってはならない (警告は出なくなるが、実行時に誤動作する)。
警告: 複数文字からなる文字定数 警告: 暗黙の定数変換でオーバーフローしました
文字定数の文字数が多すぎる。
文字定数(シングルクォートでくくった文字または文字の並び)は、 char型に格納可能な1バイトの1文字でなければならない。 バックスラッシュで始まるエスケープ系列と呼ばれる記法は、 見かけ上は複数文字で構成されているが、 実際には1バイト分の1文字を表す。
例
char c1 = 'x'; char c2 = '\n'; char c3 = 'hello';
'x'は1文字なので問題ない。 '\n'はソースプログラム上で2文字あるが、 これは「改行コード(値13)」という直接表記できない文字を書くための記法であり、 c2には値13が格納されるので問題ない。 一方、'hello'は5文字(5バイト)で構成されているので、 この警告が出る。
シングルクォート内に書く文字を1文字にする。
エスケープ系列の書き方を間違えて2文字以上になってしまう場合もある。
例
char c1 = '\n'; char c2 = '/n'; }
上の例で、1行目は正しいが、 2行目はバックスラッシュとスラッシュを間違えている。 スラッシュは普通の一文字なので、後者はスラッシュとnの2文字分になり、 この警告が出る。
また、文字列リテラルを書く際に誤ってシングルクォートでくくっただけであれば、 ダブルクォートに直せばよい。
初心者の場合、文字と文字列を混同していることが多い。 本当に文字列リテラルを書きたかったのであれば、 シングルクォートをダブルクォートに直すだけでよいが、 文字定数と文字列リテラルは代入できる変数の型や 与えられる関数が異なるため、 区別して使わないと正しいプログラムを書くことは難しい。 どちらを使いたいのか、 使おうとしている関数や変数に与えてよいものなのか、よく確認すること。
警告: 非 void を戻す関数内に値が無い ‘return’ があります
返り値がvoid型でない関数の中で、 値を返さないreturnを行っている。
例
int f(int n){ if (n != 0) return 0; else return; }
関数fの返り値はint型なので、 必ず整数値を返さなければならない。 上記のコードでは、nの値が0のとき、 返り値なしのreturnが行われる。
関数内のすべてのreturnで、何らかの値を返すようにする。
関数内のすべてのreturnで返す値がない場合は、 その関数をvoid型で定義する。
なし
警告: 効果がない文です
文になにも効果がない。 つまり、後に残るものがない(「副作用がない」と言う)文を書いている。
具体的には、比較式やただの数式 (代入やインクリメント演算子など副作用のある演算子を含まないもの)を、 単文などの形で書いている。
例
int main(void){ int x = 2; x == 1; x+1; 1; }
直感的に
x=1
や
y+=x
など)やインクリメント・デクリメント(
i++
など):
単独で文として使う「命令」
x==1
や
a < b
など)や、
ただの計算式(x+y*2
など):
if
文の条件や関数の引数など、
値が必要な場所で使う「式」
といったふうに理解している者もいると思われるが、
Cではこれらはすべて値を持つ式(代入は代入結果が値)である
(「真偽値として使われる代入」の注意点参照)。
文法的には、式が書ける場所には任意の式を書くことができるため、
if
文の条件に代入を書いたり、
上記の例のように副作用のない比較や数式を文として書いても、
エラーにはならない。
しかし無意味、
あるいはあまりなさそうな使い方の場合は警告してくれる。
上記の例の場合、
3行目はおそらく
x=1;
と代入するところを、誤って比較式にしているのだろうが、
比較の結果の値(偽であるので0)は使い道がないので捨てられる。
同様に、
x++;
としたいところを4行目のように書いてしまった場合も、
1加えた値は計算されるが、結果はそのまま捨てられる
(最適化されると、実際には計算すらされない)。
5行目も定数式「1」を評価してそのまま捨てるという、無意味だが誤りではない文である。
指摘された行で無意味な処理をしている(おそらく演算子の漏れや間違い)ので、 そこを修正する。
無意味なだけで害はないので、 無視しても良いように思えるし (最適化オプションをつければ、おそらく無意味な計算自体を消してくれるので、 速度低下の問題もない)、警告を消すだけなら該当コードを削除すればよい。 しかし、「無意味なコードを書いた」というのは 「意味のあるコードを書こうとして間違えた」場合がほとんどなので、 単純に削除したのでは間違ったプログラムになる可能性が高い。 必ず、「何をするつもりでそのコードを書いたのか」を自問して、 正しいコードに修正すること。
なお、「地の文」以外にも、for
文内でこの警告が出る場合がある。
for (x == 0; x < 10; x+1)
括弧内の最初のセミコロンより前は、ループ開始時に行う初期化処理であるため、 ここに比較式などを書いてもなにも起こらない。 同様に、2つめのセミコロンの後はループ末に到達するたびに実行する処理 (通常はループ変数のインクリメント/デクリメント)なので、 ただの計算式を書いても効果がない。 一方、セミコロン間の部分は、 ループ末でループを抜けるかどうかの判定に使われるため、 通常は副作用のない式を書く(副作用があってもよいが、 ループ毎の終了判定時に毎回その副作用が発生することに注意)。
書式 ‘%x’ は引数の型が ‘type1’ であると予期されますが、第 i 引数の型は ‘type2’ です。
scanf や printf などの関数で、フォーマット文字列中の指定の型(type1)と、 対応する引数の型(type2)が一致していない。
printf などの出力関数では、 第1引数で渡したフォーマット文字列中の%で始まる指定に従い、 第2引数以降の値を文字列に変換し、出力する。 したがって、各指定と対応する引数の型が一致しなければならない。 また、 scanf などの入力関数では、同様にフォーマット文字列中の指定に従い、 第2引数以降のポインタが指す領域に格納する。 したがって、引数はすべてポインタでなければならず、 ポインタの型も指定と一致する必要がある。
scanf関数の場合、 int型などの単純変数に格納したいだけなら、 &演算子をつける。 逆に、文字型配列に文字列を格納する場合などは、 &演算子をつけてはいけない。
例
int main(void){ int n; scanf("%d", n); }
nの値ではなく、 nのアドレスを渡さないと、 scanf内でnに値を入れることができない。
int main(void){ int n; scanf("%d", &n); }
printf関数の場合は、 フォーマット文字列の内容と第n引数の型を確認し、 どちらかに合わせる。
配列変数の場合、 添え字をつけずに変数名だけ書けば、 配列の先頭を指すポインタになる。 この場合は&をつけてはいけない。
例
int main(void){ char buf[256];
scanf("%s", buf); }
以下のように書くと誤りである。
int main(void){ char buf[256];
scanf("%s", &buf); }
警告: 書式への引数が多すぎます
printf, scanfなどの関数で、 フォーマット文字列中で指定した変換仕様(%dなど)の個数よりも その後の引数の個数が多い。
例
printf("%s\n", "hello", "world");
1個の%sに対応する文字型ポインタ(文字列)が必要だが、 文字列リテラルが2個与えられている。
変換仕様か引数のどちらかを修正して、個数を一致させる。
書式文字列中の変換仕様は「%」で始まるので、 タイプミスなどでこの文字が抜けてしまうと変換仕様が足りなくなる。 たとえば下の例は一見正しく見えるが、先頭の「%」がないため 「2d」はそのまま文字として出力されてしまい、 変換仕様は2つしかないことになる。
例
printf("2d/%2d/%4d\n", day, month, year);
書式 ‘%x’ は対応した ‘type’ 引数が予期されます
printf, scanfなどの関数で、 フォーマット文字列中で指定した変換仕様(%dなど)の個数よりも その後の引数の個数が少ない。
例
printf("(%d, %d)\n", x);
2個の%dに対応する整数型の引数が必要だが、 xしか与えられていない。
変換仕様か引数のどちらかを修正して、個数を一致させる。
なし。
警告: ‘main’ は通常は非静的関数です。
main関数が、他の関数定義の内側で定義されている。
意図的に書くことはないので、おそらく書き間違いの結果、 main関数の定義が他の関数定義の内側に入り込んでしまっている。 したがって、該当箇所を見つけてmain関数の定義を外に出せばよい。 以下のようなミスが考えられる。
関数定義の先頭行をコピーしてプロトタイプ宣言を作っている場合は、 コピー後に行末を修正し忘れたことによりこの警告が発生する可能性がある。
例
int f(int n); int g(int n){ int main(void){ printf("%d %d\n", f(9), g(9)); } int f(int n){ return n*n; } int g(int n){ return n*n*n; }
関数 f, g の定義を main 関数より下に書いた後で、 それぞれの一行目を main 関数の上にコピーしてプロトタイプ宣言を作った。 このとき、元の行では行末が「{」になっているのを 「;」に直さなければならないが、gについては直し忘れている。 この結果、2行目は g のプロトタイプ宣言ではなく定義の始まりと認識され、 対応する閉じ括弧が出ないためその後の main 関数の定義はこのgの定義(?)の内側にあると判断される。 当然、最終的には括弧の対応が取れなくなり エラー 「expected ... before ... 」も出る。
警告: 宣言が何も宣言していません
構造体や共用体の宣言内に、無意味な宣言がある。
例
typedef struct { int n; union u { int x; char c; }; } hoge_t;
上記の例では、構造体型hoge_tの定義の中で 共用体uが宣言されている。 この宣言自体は誤りではないが、この結果としては 後で共用体uが使えるようになるだけで、 構造体型hoge_tの定義自体には影響を与えない。 一般的には共用体をhoge_tのメンバにしたかったケースと推測されるため、 この警告が出る。
この共用体を構造体型hoge_tのメンバとしたいのであれば、 以下のようにメンバを列挙したブロックの後に、メンバ名をつける。 これにより、hoge_t型変数hを定義したとき、 h.uv.x, h.uv.c などとして共用体メンバにアクセスできる。
typedef struct { int n; unionu{ int x; char c; } uv; } hoge_t;
上の例の共用体のように、 入れ子になった内側の構造体・共用体を他の箇所で使わないのであれば、 メンバ名(uv)だけでよい。 共用体の名前(u)も残しておけば、後でもう一度この共用体を使うことができる。 しかし読みにくいので、 そのような場合は先にこの共用体(型)を定義しておいてから、 定義した共用体名/共用体型名を使って構造体を定義する方が良い。
なお、以下のように、共用体名・メンバ名とも省略することもできる。
typedef struct { int n; union { int x; char c; }; } hoge_t;
この場合、無名共用体となり、 h.x, h.c などとしてアクセスできる。入れ子の構造体についても同様のことが可能。 ただし、この書き方はC言語の最新規格C11で拡張されたものなので、 古いコンパイラではコンパイルできない可能性がある。
警告: 無名構造体/共用体が、そのインスタンスを定義していません
構造体や共用体の宣言で、名前も変数名も指定されていない。
例
struct { int x, y; };
この宣言文ではstructの後に構造体名がない(無名)ため、 あとでこの構造体を利用することができない。 また、閉じ括弧の後に変数名も指定されていないため、 無名構造体を用いた変数定義になっているわけでもない。 したがって、文法的には間違っていないが無意味な宣言文になっている。
一般的には、構造体名か変数名の書き忘れであるので、 どちらかをつければよい。
struct st { int x, y; };
struct { int x, y; } var;
前者の場合は、後で struct st var; のようにして変数を定義する必要があるが、 何度でも同じ構造体を利用できる。 後者の場合は変数定義も合わせて行えるが、 構造体自体に名前がないため、後で他の変数定義に利用することはできない。
あるいは、typedefを用いて構造体型を定義してもよい。
typedef struct { int x, y; } st_t;
この場合は、後でst var; のようにして構造体型を利用できる。
無名構造体・共用体は最新の規格C11で拡張されたものであるため、 古いコンパイラでは異なる警告・エラーになる可能性がある。
警告: 配列初期化子内の要素が多すぎます 警告: ('var'用の初期化付近)
配列変数varを宣言したときに与えた初期化子の要素の個数が、 配列の大きさより多い。
例
int a[3] = {1, 2, 3, 4};
配列の大きさと初期化子の要素数を合わせる。
間違って初期化子の要素を書きすぎたのであれば、取り除く。 逆に、初期化子の内容が合っているのであれば、配列の大きさが不足しているから、 初期化子に合わせて配列を大きくする。あるいは、次のように書く方法もある。
int a[] = {1, 2, 3, 4};
この場合、初期化子の個数に合わせて自動的に配列の大きさが調整される。 後から初期化子の要素の追加・削除を行いたい場合は便利であるが、 配列の全要素を処理するコードを書くときに、ループ回数を決めにくいという問題がある。 これについては、(1)初期化子の最後に、データの終了を表す特別な値を入れておき、 whileループで回す、(2)配列の大きさを自動で求めるマクロを定義する、 のいずれかで解決できる。
例
int a[] = {1, 2, 3, 4}; #define N (sizeof(a)/sizeof(int)) : for (i = 0 ; i < N ; i++){ s += a[i]; }
または
int a[] = {1, 2, 3, 4, 0}; : i = 0; while (a[i] != 0)){ s += a[i]; i++; }
前者の場合、 配列変数に対してsizeof演算子を用いると配列変数全体のバイト数が得られるから、 これを1要素当たりのバイト数(つまり配列の要素の型にsizeofを適用)で割ると、 配列の要素数が得られる。 後者の方法では、データ終了を示すために配列要素が「取りえない値」を用いる必要がある。 たとえば上の例では0をデータ終了に用いているため、配列の要素を0で初期化できない。
初期化子の要素の個数が配列の大きさより少ない場合は、 警告は出ない。 これは、 「初期化子が与えられた場合、全要素を初期化するが、 初期化子に対応する値のない要素については0に初期化する」 というルールがあるためである。 このため、 誤って初期化子の一部を書き漏らしても見つけてもらえないので注意が必要である。 ただし、これを利用すると、以下のような初期化が可能である。
int main(void){ int a[100] = {0}; }
a は関数内で宣言されているので初期化子を与えないと内容は不定値になるが、 100個の要素の初期化子を書くのは大変である。 もしすべて0 (文字型ならヌル文字、ポインタならヌルポインタ) に初期化して良いのであれば、 上のように書くことで、指定しない99個についてもルールにより0に初期化される。
警告: 構造体初期化子内の要素が多すぎます 警告: ('var'用の初期化付近)
構造体型変数varを宣言したときに与えた初期化子の要素の個数が、 構造体のメンバの個数より多い。
例
typedef struct { int x, y; } point_t; point_t p = {1, 2, 3};
構造体のメンバと初期化子の要素の対応をとる。
初期化子に余計な要素がある場合は、取り除く。 逆に、初期化子の内容が合っているのであれば、構造体に必要なメンバが宣言されていない。
初期化子の要素の個数が構造体のメンバ数より少ない場合は、 警告は出ない。 これは、 「初期化子が与えられた場合、全要素を初期化するが、 初期化子に対応する値のないメンバについては0に初期化する」 というルールがあるためである。 配列と異なり、構造体で意図的にこのルールを利用することは比較的少ないと思われるので、 誤って初期化子の一部を書き漏らしていないか、注意が必要である。
なお、構造体の初期化子の一部を意図的に省略するケースとして、 以下のように定数値と可変値を混在させる場合がある。
typedef struct { char *name; // 名称 int hp_max, str; // 体力上限, 攻撃力 int x, y; // 現在座標 int hp; // 現在の体力 } monster_t; monster_t monsters[] = { {"slime", 4, 1}, {"goblin",12, 8}, : };
ゲームのモンスターを表す構造体型monster_tは様々な属性を持つが、 名称や体力の上限が固定値なのに対し、 現在の座標や体力は変化していく。 したがって、後者は最初に初期化しても意味がないので、変数宣言時には 前者のみ初期化子で与える。 このため構造体宣言時に、固定値のメンバを前の方にまとめておく。 ただし、このような場合は固定値メンバのみのmonster_class_tと 可変値メンバのみのmonster_tに分離し、 後者のメンバで対応する前者を参照する方がよいかもしれない。 そうすれば同じ固定値を持つモンスターを複数作るのが容易になる。
警告: structやunionの最後にセミコロンがありません
構造体または共用体を宣言する際に、最後のメンバの後にセミコロンがない。
例
typedef struct { char *name; int x, y; int color } obj_t;
抜けているセミコロンを追加する。
最後のメンバ宣言以外(上の例では、nameやx, y) の後にセミコロンがない場合は、 「expected ... before ...」 が出る。
最終更新: 2023年 7月 28日 金曜日 15:03:07 JST
御意見、御感想は ohno@arch.info.mie-u.ac.jp まで