「C言語 ポインタ完全制覇」正誤表
このページは、拙書「C言語 ポインタ完全制覇」技術評論社 ISBN4-7741-1142-2の正誤表です。
日付が2001年2/2以前の誤りは、第2刷で修正されています。
日付が2001年3/28以前の誤りは、第3刷で修正されています。
日付が2001年5/29以前の誤りは、第4刷で修正されています。
日付が2001年12/1以前の誤りは、第5刷で修正されています。
日付が2002年11/30以前の誤りは、第6刷で修正されています。
誤
http://member.nifty.ne.jp/maebashi/programmer/pointer.html
正
http://kmaebashi.com/programmer/pointer.html
Webサイト移転時に直したのだと思いますが、この正誤表に入れるのを忘れていました。
誤
int *a; と int a[]; は,どのような場合に置換可能なのか?
正
int *a と int a[] は,どのような場合に置換可能なのか?
間違いというわけでもないですが、セミコロンが付いていると、 (新しい関数定義の形式を使う限り)置換可能なケースがなくなってしまうので...
誤
UNIXの作者であるKen Tompsonは
正
UNIXの作者であるKen Thompsonは
誤
また、JISハンドブックとしても入手できます。 『情報処理 プログラミング言語編』に含まれています。 書店で注文もできますし、大型書店なら店頭にあるかもしれません。
正しくは「プログラム言語編」であるというのもありますが、 それ以前に、JISハンドブックの2001年度の全面改訂に伴い、 「プログラム言語編」に相当する部分はハンドブックから外されたようです。
同じだけの情報を全部規格書で取り寄せようとすると、 いくらかかることやら。とほほ。
誤
(5)移植性を犠牲にしない範囲で、高速にしよう(Make it fast, even if it is not guaranteed to be portable)
誤訳です。正しくは、
正
(5)たとえ移植性が保証されなくても、高速にしよう。(Make it fast, even if it is not guaranteed to be portable)
です。
これはかなり情けないポカですね。 Rationaleでは、続きにこれに関する説明が書いてありまして、 そこも読んでたはずなのですが...
誤
たとえば、Pascalでは、Cのprintf()相当のことを行なうのに writeという命令を使います。これはPascalのキーワードの1つです。
write()は、Pascalの標準手続きであって、 キーワード(予約語)ではありません。
確かに、write()は可変個の引数を取ります (Pascalにおける通常の手続きは可変個の引数が許されないにもかかわらず)。
JIS X3008 6.6.4.1の備考によれば、
標準手続き及び標準関数は、手続き又は関数の一般規則には必ずしも従わない。
とされています。
また、「参考1 構文規則」の中の「手続き呼び出し文」の中に 「write パラメタ並び」というのが入っていたりします。 つまり、構文上、特別扱いをしているわけです。
しかし、やっぱりキーワード(予約語)ではありません。
誤
・構造体を関数の引数として返す
正
・構造体を関数からの戻り値として返す
誤
ここで表示している「アドレス」が、すわなち「ポインタ型の値」そのものなのです。
正
ここで表示している「アドレス」が、すなわち「ポインタ型の値」そのものなのです。
誤
ということは、hogeとpiyoとhoge_pがどんな順で並んでいるのかを意識しても しかたがないわけですから、Fig.1-2は、Fig.1-3のように に表現することもできます。
正
(前略)Fig.1-3のように表現することもできます。
「ほげを考えるページ」は、現在オリジナルが公開されていませんので、 作者の吉田さんの許可を得まして、私のページに転載させていただきました。
URLは、
http://member.nifty.ne.jp/maebashi/programmer/hoge.html(移転済み)
http://kmaebashi.com/programmer/hoge.html(移転後)
です。
誤
今回のサンプルプログラムでは、hoge_pは配列を指していませんから、 これに加算を行なうことは、それだけで規格に反しています。
一応規格では、
配列でないオブジェクトへのポインタは、 要素型としてそのオブジェクトの型をもつ長さ 1の配列の最初の要素へのポインタと同じ動きをする
と書いてありますので、 ふたつ超えた所に向けない限り問題なさそうです。
List1-2の9行目
誤
hoge_p = hoge;
正
hoge_p = &hoge;
誤
#include <stdio.h>
正
#include <stdio.h> #include <ctype.h> #include <stdlib.h>
ctype.hとstdlib.hが抜けていました。
誤
int len = 0;
別にバグではありませんが、 後の方でlen = 0と書いているのでこの初期化は不要です。
誤
scanf()は、戻り値として、変換に 成功した変換指定子の数を返しますが、
正
scanf()は、戻り値として、代入に 成功した変換指定子の数を返しますが、
代入抑止文字*を使用した場合、変換の数と代入の数は食い違います。
誤
C言語の変数には、段階的なスコープ(有効範囲)があります。
C言語では「scope」と「linkage」という言葉は別々に定義されていて、 ブロックで囲むのはscope、staticとexternはlinkage(結合)を制御します。
プログラマの感覚としては、「staticを付けるとスコープが ファイル内に限定される」ように感じるわけですが、 規格上用語は明確に分けられています。
いわゆるグローバル変数は、scopeがfile scopeで、 linkage(結合)がexternal linkage(外部結合)ということになります。
誤
Cでは、malloc()を関数を使用して
正
Cでは、malloc()関数を使用して
誤
7: void func1()
正
7: void func1(void)
同様に、
誤
16: void func2()
正
16: void func2(void)
誤
「関数へのポインタ」に読み替えらえるので
正
「関数へのポインタ」に読み替えられるので
誤:
1から4までに伸張した分のスタックが,その関数が参照する(後略)
読者の方から、この1と4がどの図の番号かわからないという質問がありました。 これは、この箇条書き自身の番号です。
増刷時には、箇条書きと表記を合わせ、黒の丸数字に変更します。
誤
"&d, %s"へのポインタ
正
"%d, %s\n"へのポインタ
誤
va_end();
正
va_end(ap);
List2-6 12行目
誤
12: for ( ; data[right_index] < pivot; right_index--)
正
12: for ( ; data[right_index] > pivot; right_index--)
不等号の向きが逆です。
ベンチマークも同一条件でやりなおしました。同一ページより。
誤
私の環境で,5万件のランダムな整数データをソートしたところ, バブルソートでは92秒かかりましたが,クイックソートでは51ミリ秒 (1ミリ秒は1000分の1秒)で済みました.
正
私の環境で,5万件のランダムな整数データをソートしたところ, バブルソートでは117秒かかりましたが,クイックソートでは65ミリ秒 (1ミリ秒は1000分の1秒)で済みました.
誤
struct BookData_tag *next; } BookData;
正
struct BookData_tag *next; } BookData;
インデントが変でした。
誤
いままで、
void func(int hoge[2][3]);
とか、
void func(int hoge[][3]);
のように書いていた方も多いかもしれませんが、これらはすべて
正
いままで、
void func(int hoge[3][2]); ←2と3が逆
とか、
void func(int hoge[][2]); ←3ではなくて2
のように書いていた方も多いかもしれませんが、これらはすべて
誤
*細かいことをいうと,規格では「基本型」という言葉は定義して いないようなのですが(後略)
規格ではちゃんと定義しているので、注ごと削除してください。
次のページの列挙型に関する注と思いっきり矛盾してます。
誤
符合(7箇所)
符号ですね(とほほ)。
誤
: struct Man_tag *husband; /* 夫 */ };
正
: struct Man_tag *husband; /* 夫 */ : };
ここだけタテの中略記号がありません。
誤
struct Woman_tag; ←タグだけ先に宣言 struct Man_tag { ... struct Woman_tag *wife; /* 妻 */ ... } struct Woman_tag { ... struct Man_tag *husband; /* 夫 */ ... } (中略) struct Woman_tag { ... Man *husband; /* 夫 */ ... }
正
struct Woman_tag; ←タグだけ先に宣言 struct Man_tag { ... struct Woman_tag *wife; /* 妻 */ ... }; struct Woman_tag { ... struct Man_tag *husband; /* 夫 */ ... }; (中略) struct Woman_tag { ... Man *husband; /* 夫 */ ... };
セミコロンが抜けています。
誤
「intへの配列(要素数5)」となる。
正
「intの配列(要素数5)」となる。
誤
これは、多言語に比べて極端に多いため
正
これは、他言語に比べて極端に多いため
演算子の優先順位表の?:の結合規則
誤
左から右
正
右から左
誤
constがその直後の単語を修飾していることに注意しながら
正
read-onlyがその直後の単語を修飾していることに注意しながら
誤
const指定指定した識別子は、
正
const指定した識別子は、
誤
さらに、早期の移行を用意 にするべくデザインされたいくつかの規則は、
正
さらに、早期の移行を容易 にするべくデザインされたいくつかの規則は、
誤
*この宣言は,ANSI C以前の古い形式の宣言ですね.
正
*これは,ANSI C以前の古い形式ですね.
原文で「in the function declaration」になっていますから、 「関数宣言」と訳すしかないと思うのですが,これは関数「定義」 なので、注の中で「宣言」と連発するのはどうも...
int a; のようなものは正確には仮定義(tentative definition)であり、 初期化子がつかないと「外部定義」にはなりません。
K&Rのp.39〜41にも同様の説明がありますし、 「仮定義」は「定義」かどうかという問題のような気もしますが、 一応補足しておきます。
誤
「charへの配列(要素数6)」
正
「charの配列(要素数6)」
誤
ただし、式の中では、配列はポインタに読み替えらえるので
正
ただし、式の中では、配列はポインタに読み替えられるので
誤
/* Free BSDのman pageから抜粋 */
正
/* FreeBSDのman pageから抜粋 */
スペースが余計です。
間違いになるかどうかはわかりませんが、 式の中で配列名に代入できない理由について議論がありました。 こちらの補足を参照してください。
誤
#include <stdio.h> #include <stdlib.h>
正
#include <stdio.h> #include <stdlib.h> #include <string.h>
string.hが抜けていました。
p.217 Lis4-5 read_slogan.cの16行目
誤)
slogan[i] = malloc(sizeof(char) * (slogan_len + 1))
正)
slogan[i] = malloc(sizeof(char) * slogan_len)
文字列長がlenのときlen + 1だけmalloc()するのはCの定石ですが、このケースではslogan_lenは改行文字分を含んでおり、その改行文字を'\0'で置き換えるわけですから、+ 1は不要です。
誤
#include <stdio.h> #include <stdlib.h> #include <assert.h>
正
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h>
string.hが抜けていました。
誤
9: char *read_line(FILE *fp);
7行目で#includeしているヘッダに含まれているので、 不要です(あっても害はないですが)。
誤
#include <stdio.h>
正
#include <stdio.h> #include <stdlib.h>
stdlib.hが抜けていました。
誤
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include "read_line.h"
正
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h> #include "read_line.h"
string.hが抜けていました。
誤
fpから1行読みこむ。ファイルの末尾に来たら、NULLを返す。
正
fpから1行読みこむ。
List4-6からの直し忘れでした
誤
double[][] polyline = new int[nPoints][2];
正
double[][] polyline = new double[nPoints][2];
Fig 4-11の中の一番上の箱の中。
誤
point_num
正
npoints
誤
#ifndef GET_WORD_H_INCLUDED #define GET_WORD_H_INCLUDED
正
#ifndef GET_WORD_H_INCLUDED #define GET_WORD_H_INCLUDED #include <stdio.h>
ポリシーに従えば、stdio.hを#includeしなければなりません。
「POINT ヘッダファイルを書く際に必ず守ること!」は、 内容的には補足の一部なので、掛け網部分の中にあるべきです。
List5-7 9行目
誤
9: printf("%-20s%5d\n",
正
9: fprintf(fp, "%-20s%5d\n",
元のままでは、fpを渡している意味がありません。
誤:
9: fprintf(fp. "%-20s%5d\n",
正:
9: fprintf(fp, "%-20s%5d\n",
3刷で修正を入れる際、コンマの代わりに 誤ってピリオドを入れてしまったようです。
誤
pが最後の要素の場合、
正
posが最後の要素の場合、p.261 List5-11 15行目
誤
15: dest = malloc(sizeof(char) * strlen(src) + 1);
正
15: dest = malloc(sizeof(char) * (strlen(src) + 1));
sizeof(char)が1であることは規格で保証されていますから 結果は別に変わりませんが、 わざわざsizeof(char)と書くからにはこうなってないと変ですね。
誤
#include <stdio.h>
正
#include <stdio.h> #include <string.h>
List5-14 14行目
誤
14: while (left < right) {
正
14: while (left <= right) {
20行目
誤
20: right = mid;
正
20: right = mid - 1;
元のままだと、場合によっては left == rightになった瞬間returnしないでループを抜けます。
ソースのミスは罪が重いですね。申し訳ありません(_o_)
誤
voidを使うという方法もありますが、 それでは「どんな型を指す可能性があるのか?」がさっぱりわかりませんから、 ソースの可読性を考えればポ<ぷっつり>
この後に、
ソースの可読性を考えればポインタの共用体にすべきです。
と続きます。
誤
typedef struct { Shape shape; Shape *prev; Shape *next; } LinkableShape;
正
typedef struct LinkableShape_tag { Shape shape; struct LinkableShape_tag *prev; struct LinkableShape_tag *next; } LinkableShape;
誤
typedef struct { Shape *shape; Shape *prev; Shape *next; } LinkableShape;
正
typedef struct LinkableShape_tag { Shape *shape; struct LinkableShape_tag *prev; struct LinkableShape_tag *next; } LinkableShape;
誤
typedef struct Linkable_tag { void *object; Shape *prev; Shape *next; } Linkable;
正
typedef struct Linkable_tag { void *object; struct Linkable_tag *prev; struct Linkable_tag *next; } Linkable;
誤
最上段左からふたつめのGroupの箱にポインタを示す●がなく、2段目右端のShapeのnextのポインタがNULL(×)になっていません。
正
誤
(strncat()も同様です)
正
削除
JIS X3010 7.11.3.2には、 「文字列の終端を示すナル文字を、常に結果に付加する」 とはっきり書いてありました。
strncpy()とは仕様が異なります。確認不足でした。
誤
srcからlenに、最大でlen文字だけコピーします。
正
srcからdestに、最大でlen文字だけコピーします。
誤
strncpy()は、
strcpy(dest, src, len); のように使い、(後略)
正
strncpy()は、
strncpy(dest, src, len); のように使い、(後略)
「あるプログラマがこんなコーディングをしたそうな.」の次の行。
誤
strncpy(dest, src, strlen(src))
正
strncpy(dest, src, strlen(src) + 1)
誤
実は、printf()の書式指定子では、%fと書こうが%lfと書こうが、 まったく同じものとして扱われます。 よって、double型も%fで表示可能です。それどころか、 私の使っているコンパイラ(gcc)では、 %lfを使うとコンパイラが警告メッセージを出してくださりやがります。
申し訳ありません。完全に間違ってます。
規格では、printf()の%f指定子は、
double型の実引数を[-]ddd.ddd形式の10進表記に変換する。
とはっきり書いてあります。
また、
l(エル)は、それにd, i, o, u, x又はX変換指定子が続く場合...
と書いてありまして、さらに、
h, l, 又はLを他の変換指定子とともに指定した場合の動作は、未定義とする。
とあります。
つまり、GCCが親切にも警告してくださる%lfは、 やはり未定義なのであって、 警告を出す方が正しいことになります。
暴言を撤回するとともに、GCC関係者にお詫びいたします。
誤
struct Woman_tag { ... Man *husband; /* 夫 */ ... }
正
struct Woman_tag { ... Man *husband; /* 夫 */ ... };
誤
char str[3] = "abc"; ← 間違い.'\0'の分を忘れている
正
char str[3] = "abc"; ← '\0'の分を忘れている
char str[] = "abc"; とは意味が違うという点では確かに間違いなのですが、 規格上は間違ってはいないので、 「間違い」と断定すると要らぬ誤解を招きそうです。
誤
すから、"red", "green" "blueyellow"からなる要素数3の配列に
正
すから、"red", "green", "blueyellow"からなる要素数3の配列に
コンマが抜けていました。
誤
と書くことで,構造体の内容を初期化できます.
正
以下のように書くことで,構造体の内容を初期化できます.
誤
と、きちんと対応がとれるように初期化子を書けば,初期化可能です.
正
以下のように、きちんと対応がとれるように初期化子を書けば,初期化可能です.