「とりあえず」警告は全部載せの -Wall オプション使っておけと言われます。しかし、ここはあえてバラバラに警告オプションをひとつひとつ丁寧に?味わっていきたいと思います。それにしても-Wallに含まれる警告オプションだけでいくつあるんだ?それに警告の裏側には新たな世界が広がってるみたいな兆候も。大丈夫か。
※『オプション沼』投稿順indexはこちら
※今回は動作確認に以下を使用しています
-
- Windows11 WSL2上のUbuntu 20.04 LTS、gccのバージョンは9.4.0
-Waddress オプション
アドレス(ポインタ)の「怪しい」使い方を警告するのがこのオプションに期待される役割のようです。ポインタという概念を持たない言語もあり、あっても変な使い方ができないように制限のキツイものも多いなか、C言語の「良いところ」はほぼほぼアセンブラ同様に「何でもあり」に扱えるところかと思います。自由の大海原。その代わり自己責任ってことで。
そこに手を差し伸べてくれているのがこの -Waddress オプションみたいです。「怪しい」使い方している部分をたたき出してくれるもの。ただし、全面否定するわけでなくプログラマ様に「分かって使っているんでしょうね」と念おしするくらいでしょうか。
公式ドキュメントは以下に。
3.8 Options to Request or Suppress Warnings
今回実験のコード
上記の公式ドキュメントを見ながら「怪しい」使い方をするコードを書いてみましたぜ。これで警告が発生する筈。
/* option -Waddress */ #include <stdio.h> #include <stdlib.h> #include <string.h> int f1(void) { return 0; } void f2(const char *x) { if (x == "abc") { printf("x=%s, Expression is true!\n", x); } else { printf("x=%s, Expression is false!\n", x); } } int main(int argc, char const *argv[]) { if (!f1()) { printf("f1() returns false: OK\n"); } if (!f1) { printf("f1 returns true: NEVER!\n"); } else { printf("f1 always true: WARNING case\n"); } f2("abc"); f2("xyz"); return 0; }
ビルドして警告を受け取り
以下のようにしてコンパイルすれば、予定通りに警告2つが報告されてきます。こんな感じ。
上記では単独の -Waddress オプションつけてますが、-Wall でも同じ警告が出現します。一方、警告オプション付けないと何もでません。スルー。
実行結果
警告はあくまで警告なので、実行可能なオブジェクトファイルは生成されており、勿論実行も可能です。こんな感じ。
f1()、関数呼び出しとf1、関数ポインタのところは予定どおり。ただここをウイーク・シンボル使ってさらにほじくりたい気がしないでもないです。また今度かな。
一方、意外な挙動だったのはf2()の方っす。文字列リテラルは読み書き可能なところに「それぞれ」置かれる昔風の頭でいると変です。ソースコードでいうと、以下の上の方、11行目の文字列リテラル “abc” と、下の28行目の文字列リテラル”abc”が、同じアドレスを指しているように見える実行結果です。
同じ “abc”という文字列(定数、const)なので、同じ実体を指すようになっている?念のため gdb を使って上記の該当部分を実行し、比較に使われているアドレスを確認してみました。こんな感じ。
たしかに、cmp命令で比較しているraxに格納されているアドレスと、スタックフレーム上に置かれているアドレスは同じっす。そしてそのアドレスが指す先のメモリには 0x61, 0x62, 0x63, 0x0(つまり”abc”(末尾が\0)がおかれてます。
多分、コンパイラによっては最初の”abc”と2つめの”abc”が違うところに置かれているケースもあるのではないかと(そうすれば比較は偽。)今回使用のバージョンのgccでは比較結果は真でしたが。そういうことがあるから、文字列のリテラルのアドレス(ポインタ)を 比較演算子で比較するようなことをすると
-Waddress
が警告してくるというわけね。知っていたら反って使ってしまいそうだけれども。。。