コンパイラ方言

私はプログラムを書くのが好きである。といっても、プロのような技量はない。あくまで趣味の範疇である。趣味の範疇であるから、コンパイラもたいていGNU-Cを使っている。さて、ふとしたきっかけでフリーの別のコンパイラを手に入れた。手に入れたら使ってみたくなるのが人情である。新しいおもちゃを買ってもらった子供と同じだ。
なので、過去に作ったプログラムをコンパイルしてみた。しかし、困ったことにコンパイルエラーが起こってしまった。それは、文字列操作関数の以下のような使い方である。
  strncpy(dst, src, size)[size-1] = 0;
GCCでは正常に通っていた所である。おそらく、コンパイラ方言に引っかかったのであろう。
放っておくわけにもいかないので調べてみた。まず、strncpy関数の戻り値であるが、やはりchar型のポインタということで規格上は間違いないようだ。ならば、このソースは通るはずである。しかし通らないということは、このコンパイラのライブラリはポインタを返していないのだろうか?確かに、ポインタを返さなくてもさして困らない。なぜなら、strncpy関数が返すポインタはdstそのものだからだ。とはいえ、規格で定められた戻り値を返さないのは困りものである。
想像だけではなにもわからないので、ソースを以下に変えてみた。
  p = strncpy(dst, src, size);
  p[size-1] = 0;
なんと、この記述は通ってしまった。しかも、想定していたように動く。ということは、このコンパイラのライブラリのstrncpy関数はvoid型のポインタを返しているのだろうか?もしかして、strncpy関数はvoid型のポインタを返してもよいとANSIで定められたのであろうか?確か、malloc系の関数はK&RからANSIへの移行でそのような変更がなされたと聞く。
まあ、とにかく最初の記述が通らないことはわかっているので、通るように修正しないといけない。strncpy関数の戻り値はdstなので、そのまま添字演算子が使えないのならわざわざ戻り値を使う必要はない。なので、まず以下のように修正してみた。
  strncpy(dst, src ,size), dst[size-1] = 0;
カンマ演算子は左から右に評価されることが保障されているCでは数少ない演算子の一つである。これできっちり意図した通りに動くはずだ。実際、その通りに動いた。しかし、ちょっと気持ち悪い。for文の初期化の位置ならともかく、こんなところでカンマ演算子を使うのは気持ち悪すぎる。ここはマクロを使って隠すべきだ。で、以下のようにした。
#define strncpy0(d,s,n) do{strncpy(d,s,n),d[n-1]=0;}while(0)
あとは、元の記述でstrncpy関数を使っていたところをstrncpy0マクロに置き換えるだけだ。一通りの作業を終えてソースを見直してみたが、非常に見やすく変更できた。カンマ演算子をマクロが隠してくれるのは、非常に見た目がいい。そういえば、昔の人も言っていたな。「カンマはマクロに限る」と。