『Linux技術者のためのC言語入門』が届いた。
高木です。おはようございます。
たまには自分たち以外の書籍の話をしたいと思います。
今回取り上げるのは、平田豊さんの『Linux技術者のためのC言語入門』です。
この本、なかなかの人気のようで、アマゾンでも頻繁に売り切れになっていました。
初刷の部数がそう多くないようで、私のようなロートルよりもっと必要としている人がいるだろうという考えから、アマゾンで注文だけして気長に待っていました。
その本がようやく昨日届いたというわけです。
私の手元に届いたのが昨夜なので、まだざっとしか目を通せていません。
そんな状況なのですが、とりあえず気づいたこと、感じたことを書いてみたいと思います。
驚いたのは、「翻訳段階」について非常に詳細な解説がされていることです。
かなりマニアックな内容になるので、何とか入門と付いた本でここまで解説しているものはまずないと思います。
個人的にはこの部分には非常に好感を持っています。
全体を通して気になったのは、本文の記述がC規格のどのバージョンを対象にしているのかが不明瞭な点です。
GCC 8.1.1を使用しているということ、コンパイル例でとくにバージョンに関するオプションを指定してないことから、C11を用いているのだとは思います。
しかし、そんなことはわからない読者も多いでしょうし、記述との矛盾も見られます。
たとえば、p10に
「return文」の指定がなくても、「コンパイル」が通ってしまうところが「C言語」の恐いところではありますが、
との記述があるのですが、C99以降であれば、main関数のreturn文を省略したとしても、main関数末尾の}に到達すれば値0を返すことが規格で保証されています。
C規格のバージョンに関していえば、C89、C99、C11、C17の違いも詳細に解説されています。
Cを長く使っているプログラマーでも、こういう知識がない方は結構多いと思いますので、重宝されるのではないでしょうか。
ここでも一点気になったのは、C17は実用上C11と区別する必要はないので、それよりもC95の解説に紙面を割いたほうがよかったのではないかということです。
たとえば、C89からC90への変化点として挙げている内容のうち。ワイド文字ライブラリと<iso646.h>ヘッダはC95で導入されたものです。
また、現時点でも最も広く使用されているバージョンはC89というよりC95だと思います。
明らかな間違いも見つけました。
あまり細々したことを指摘することは今回は行いませんが(後日、重箱の隅をつついてもいいかなとは考えています)、大きな間違いだけ2点だけ指摘しておきます。
まずは一つ目です。
p52に「文字列リテラル」に関する記述があります。
「文字列リテラル」は「ポインタ」なので、「sizeof」では「ポインタ」の「サイズ」になり、「データ」の大きさにはなりません。
という箇所です。
実際には、「文字列リテラル」は「ポインタ」ではなく「配列」になります。
そのため、末尾のナル文字を含むバイト数が「文字列リテラル」に「sizeof」を適用した場合の結果になります。
具体的にコードで示しましょう。
1 2 3 4 5 6 7 8 | #include <stdio.h> int main(void) { printf("%u\n", (unsigned)sizeof("a")); printf("%u\n", (unsigned)sizeof("0123456789")); return 0; } |
上記のコードを実行すると、2と11が出力されるはずです。
もし「ポインタ」であれば、データの内容に依存して結果が変わることはありません。
次に二つ目です。
先ほどのコードで、私は「sizeof」の式をunsigned型でキャストしていました。
二つ目の間違いは、このことと密接に関係しています。
「sizeof」演算子の評価結果は「size_t」型になります。
p199にも記述があるように、32ビット環境と64ビット環境ではサイズが異なります。
size_t型がunsigned long型に定義されていた場合、int型より大きなサイズになる可能性があります。
ところが、printf関数の書式文字列の変換指定子に「%d」を使った場合、これは対応する実引数にint型を期待しています。
符合の有無が異なる程度ならOKですが、サイズが異なる場合は問題です。
可変個の実引数が1個しかなく、かつリトルエンディアンの環境であれば、たまたま期待通りに動作してしまいます。
しかし、言い換えれば、可変個の実引数が複数ある場合や、ビッグエンディアンの環境であれば破綻することを意味します。
ただ、コーリングコンベンション次第では、実引数を64ビット単位でスタックに積む場合があり、可変個実引数が複数あってもビッグエンディアンでも期待通りに動くことはあります(未定義の動作であることには変わりありません)。
確実なのは、私が上記のコードで書いたように、明示的にキャストを行うことです。
あるいは、C99以降であれば、変換指定子に「%zu」を使うことで対応する実引数がsize_t型であることを期待するようになります。
(MinGW-w64ではコンパイルオプションに-D__USE_MINGW_ANSI_STDIO=1を指定する必要があります)
というわけで、辛口の指摘もしましたが、全体としては非常によく書けた本だと思います。
私が現在手がけているようなゲテモノではなく、本当に実用価値を追求したよい仕事をされています。