gccのオプション、バージョンアップとともに増え続けてるみたいデス。旧版コンパイラを後生大事に使用中。最新版なら存在するハズのオプションが不在なこともままあり。今回はdangling-pointerを警告するオプションでそれに遭遇しました。ま、古い版のコンパイラでも検出する手段はあり~の、ただし「ステージ」が違うっと。
※『オプション沼』投稿順indexはこちら
※今回は動作確認に以下を使用しています
-
- Windows11 WSL2上のUbuntu 20.04 LTS、gccのバージョンは9.4.0
ダングリング・ポインタ
ダングリング・ポインタは、指している先のメモリオブジェクトが既に無効状態である場合に発生するゆゆしき事態です。まあ、アセンブラで書いていたらそんなもの、自分で管理しろよ、という感じ。コンパイラ素人の古色蒼然老人は、C言語においてもそんなものだと思っていたのですが、コンパイラの中の人々はそんな「ゆゆしき事態」を以前から懸念していたみたいです。
“3.8 Options to Request or Suppress Warnings”
上記は多分「最新版」のgccの警告オプションのドキュメントですが、そこには以下のオプションが厳然として存在しますです。
-Wdangling-pointer
正確にいうと、=1とか、=2とか警告レベルを付加して使用するもの。ちなみに-Wdanglig-pointer=2が、-Wallの中に含まれているみたいです。安心だな。
ところがどうも、新版のgcc(12.xとか13.xとか)では存在するらしい上記オプシションですが、当方が普段お世話になっております 9.x とか 10.x とかの旧版では使えないみたいです。使ってみたかったのに。
しかし、「ゆゆしき事態」なので以前から検出する手段が用意されてました。以下のオプションつけるとチェック用のコードを挿入してくれるアドレス・サニタイザです。
-fsanitize=address
ま、最大の違いは、-Wにつづく警告オプションであれば「コンパイル時」に警告してくれるのに対し、-fsanitize=addressの方は「実行時」にエラー発生とともに、問題個所を指摘してくれるという「ステージ」の違いです。ただフォルトと言われて訳も分からず落ちるより、原因とその場所を究明してくれるアドレス・サニタイザの方が役には立つはず。でもきっとオーバヘッドも馬鹿にならないのでは?
今回実験のC言語ソース
毎度、フォルトを発生するような「いかがなものか」なコードを書いているので、段々慣れて「罪悪感」が消えてきました。今回のもアカラサマにスタック上のローカル変数へのポインタを返すので、エラーにきまっているコードです。
/* option -Wdangling-pointer */ #include <stdio.h> #include <stdlib.h> void f1(int *ptr) { int localVal = 111; ptr = &localVal; //Should be Dangling-pointer, may cause Segmentation fault } int main(int argc, char const *argv[]) { int *ptr = NULL; f1(ptr); printf("f1(ptr) : %d\n", *ptr); return 0; }
フツーにWallしてもザル、そしてフォルト
さて、まずは gcc 9.4.0 で、-Wall付きでビルド。念のため -O0と-O2の両方でやってみました。どちらもノーエラー、ノー警告でビルド出来。そしてどちらもSegmentation fault発生!
本件に関して、gcc 9.4.0 の-Wall オプションはザル。なすすべもないようです。
同じフォルトならアドレス・サニタイザで
しかし、実行時に原因を究明してくれるアドレスサニタイザを使用した場合はどげんでしょうか?
$ gcc -g -O0 -fsanitize=address optDangling.c
SEGVが発生しているのは同じですが、さすが「原因究明委員会」方式?、フォルトの原因だけでなく、その場所まで教えてくれてます。これだとバグの修正はお楽。