char *answer;A:
printf("なにか入力してください:\n");
gets(answer);
printf("あなたは \"%s\" と入力しました\n", answer);
上の質問のプログラムを修正する一番やさしい方法は、ポインターの 代りにローカルの配列を使って、コンパイラに領域の割当てをまかせ ることである。
この例はgets()のかわりにfgets()を使って、配列に続く部分が上書 きされないような工夫もしている。(質問12.23参照。残念ながらこの 例ではfgets()はgets()とは違って、後ろの改行を自動的には削除し ない)。malloc()を使って返答用のバッファを割り当てることもでき る。
- #include <stdio.h>
- #include <string.h>
- char answer[100], *p;
- printf("なにか入力してください。:\n");
- fgets(answer, sizeof answer, stdin);
- if((p = strchr(answer, '\n')) != NULL)
- *p = '\0';
- printf("あなたは \"%s\"と入力しました\n", answer);
char *s1 = "Hello, ";変な答えが返ってきた。
char *s2 = "world!";
char *s3 = strcat(s1, s2);
A:
上の質問7.1と同じように、ここでも一番の問題は連結した結果を貯
える領域がうまく確保されていないことである。C言語には、自動的
に管理される文字列型はない。ソースコードで明示的に表されたオブ
ジェクトに領域を割り当てるだけである("文字列"についてはcharの
配列と"でくくられた文字列を含む)。プログラマーは、文字列の連結
のような実行時の操作の結果に対して十分な領域を、配列を定義した
り、malloc()を起動することで明示的に確保しなければならない。
strcat()は、領域の割り当てを行わない。二番目の文字列は最初の文字 列に、その場で連結される。一つの解決方法は、最初の文字列を十分 な大きさを持つ配列として宣言することである。
char s1[20] = "Hello, ";strcatは第一引数を返すので(この場合はs1)、s3は余計である。
質問のコードの元のstrcat()の呼び出しは、実際には二つの難点があ る。s1によって指される文字列リテラルが、連結されてできるどんな テキストも保存できるほど大きくないかもしれないだけではなく、そ もそも書き込み不可かもしれない。質問1.32参照。
References:
CT&P Sec. 3.2 p. 32.
A:
一般にポインターを使うときは、いつも領域の割り当てのことを考え
ておかなければならない。少なくともコンパイラが代わりにやってく
れることを確認しておかなければならない。ライブラリーのドキュメン
トに記憶領域の割り当てについてはっきり記述していなければ、割り
当ては普通は使う人の責任である。
Unix形式のmanの先頭やANSI C規格の要約の章は、誤解を招くかもし れない。そこに載っているコードの一部は、使い方というよりは、関 数の実装で使われる関数定義に近い。特に(構造体や文字列への)ポイ ンターを扱う関数の多くはなんかしらのオブジェクト(構造体か配列 への … 質問6.3や6.4を参照)へのアドレスを引数に起動される。ほ かによくある例はtime() (質問13.12参照)やstat()である。
A:
関数が返す文字列を格納する領域が正しく割り当てられていることを
確認すること。返されたポインターは静的に割り当てられたバッファ
か、呼んだ側の関数から渡されたバッファを指すべきで、呼ばれた関
数のローカルな(自動変数の)配列を指していてはいけない。つまり以
下のようなことは絶対にやってはいけない。
修正する方法のひとつは、バッファを以下のように宣言することであ る(これも、f()が再帰的に起動される場合や、戻り値が同時に複数の 箇所で必要な場合にうまく行かないことを考えると不十分である)。char *itoa(int n) { char retbuf[20]; /* 間違い */ sprintf(retbuf, "%d", n); return retbuf; /* 間違い */ }
static char retbuf[20];質問12.21と20.1も参照のこと。
References:
ANSI Sec. 3.1.2.4; ISO Sec. 6.1.2.4.
A:
<stdlib.h>を#includeしたか、してないとしてmalloc()が正しく宣言
されるようにしたか。
References:
H&S Sec. 4.7 p. 101.
A:
ANSI/ISO規格のC言語がvoid *という汎用のポインター型を導入する
までは、互換性のないポインター型の間で代入をするときに警告を黙
らせるのにこうしたキャストが必要となることがよくあった。
(ANSI/ISO規格のCでは、こうしたキャストはもう必要ない。)
References:
H&S Sec. 16.1 pp. 386-7.
char *p = malloc(strlen(s) + 1);malloc((strlen(s) + 1) * sizeof(char))では?
strcpy(p, s);
A:
sizeof(char)をかける必要がある場合は絶対にない。なぜなら
sizeof(char)は定義によりぴったり1であるから。 (一方、
sizeof(char)をかけても何も害はない。こう書くことで式にsize_tが
現れて理解しやすくなるかもしれない。)質問8.9も参照のこと。
References:
ANSI Sec. 3.3.3.4; ISO Sec. 6.3.3.4; H&S Sec. 7.5.2
p. 195.
A:
これは難しい。規格は、システムはこういう風に振る舞ってもよいと
書いていないけれど、こういう風に振る舞ってはいけないとはっきり
書いてあるわけでもない。
References:
ANSI Sec. 4.10.3; ISO Sec. 7.10.3.
double *array = malloc(256 * 256 * sizeof(double));malloc()はヌルを返すわけではないけれど、プログラムの動きが変だ。 メモリーを上書きしたり、こっちが望んだだけmalloc()が確保してな かったりとかするようだ。
A:
256 x 256は65,536で、これはsizeof(double)をかける前から、16ビッ
トの整数にはおさまらないことに注意。こんなに大きなメモリーを確
保する必要があるときは、注意する必要がある。使っているマシンの
size_t(malloc()が認めたデータ型)が32ビットであれば、256 * (256
* sizeof(double))と書くことでやっていけるかもしれない(質問3.14
参照)。これで駄目ならデータの構造をもっと小さな単位に分解する
か、32ビットのマシンを使うか、標準ではないメモリー確保ルーチン
を使うかしなければならない。質問19.23も参照のこと。
A:
PC互換機のセグメント付きのアーキテクチャーでは、640Kより多くの
メモリーを使うことは非常に難しい。質問19.23も参照のこと。
A:
mallocした領域の内部のデータ構造は残念ながら非常に簡単に壊れて
しまう。しかも引き起こされる障害は追跡しにくいものとなることが
ある。いちばんよくある障害の元は、mallocした領域に確保したサイ
ズよりも多く書き込んでしまうことである。特によくある例は、大き
さstrlen(s)+1ではなく、大きさstrlen(s)だけmalloc()で確保するこ
とである。他にはfreeした領域を指すポインターを使うことや、2回
freeしたり、malloc()で確保した先を指していないポインターを
free()を使って開放しようとしたり、ヌルポインターを使って
realloc()を呼び出すことも障害を引き起こす(質問7.30を参照)。
A:
使えない。malloc()の昔の解説には解放された領域は「壊されずに残っ
ている」と記述してあるものもあった。このうかつな保証は一般的に
なることはなく、C規格では、このようなことを保証することは要求
されていない。
意識して解放した領域の中身を使うプログラマーは少ない。けれど偶 然使ってしまうことはよくある。一重リンクのリストを解放する以下 の(正しい)コードを考えてみよう。
一時変数のnextpを使うことなくlistp = listp->nextを使ったとすれ ばどうなったか考えてみること。
- struct list *listp, *nextp;
- for(listp = base; listp != NULL; listp = nextp) {
- nextp = listp->next;
- free((void *)listp);
- }
References:
K&R2 Sec. 7.8.5 p. 167; ANSI Sec. 4.10.3; ISO
Sec. 7.10.3; Rationale Sec. 4.10.3.2; H&S Sec. 16.2 p. 387; CT&P
Sec. 7.10 p. 95.
A:
free()を呼ぶと、free()に渡したポインターの指す先のメモリーは解
放されるが、呼び出した側のポインターの値は変わらない。それはC
の値渡しとは、呼ばれた側の関数が自分の引数を永久に変えたままに
することはないということだからである。(質問4.8も参照)
解放されたポインターの値は厳密にいえば無効で、それをどう使って も、たとえ間接参照以外のことに使っても理屈の上ではトラブルの元 である。ただ、これは実装の質の話だけれど、無効なポインターの害 のない使いかたにわざわざ例外を発生させる実装はたぶんない。
References:
ANSI Sec. 4.10.3; ISO Sec. 7.10.3; Rationale
Sec. 3.2.2.3.
A:
もちろん。ポインターとポインターが指す先は別物であることを忘れ
てはいけない。ローカル変数は関数から戻るときに解放される。ただ
しポインター変数に関しては、ポインターが解放されるのであって、
ポインターが指す先が解放されるわけではない。malloc()によって確
保されたメモリーは明示的に解放するまで必ず残る。一般に、すべて
のmalloc()の呼び出しに、対応するfree()がなければならない。
A:
そのとおり。一般にmalloc()が返してきたポインターを(解放すると
したら)それぞれ一度だけfree()に渡すしくみを用意しなければいけ
ない。
A:
その必要はない。まともなオペレーティングシステムならきっとプロ
グラムが終了した時点ですべてのメモリを取り返すだろう。にもかか
わらず、個人向けコンピュータ(PC)の中にはメモリを取り戻すことが
確実にはできないものもあるようである。ANSI/ISO C規格から結論つ
けられることは、解放してくれるかどうかは「実装の品質がどれくら
い高いかによる」ということだけである。
References:
ANSI Sec. 4.10.3.2; ISO Sec. 7.10.3.2.
A:
たいていのmalloc/freeの実装は、解放されたメモリをO/Sに(O/Sがあっ
たとして)返さない。後からmalloc()が呼び出されたときのために取っ
ておく。
A:
malloc/freeの実装は、メモリのブロックを割り付けて、そのブロッ
クの先頭アドレスを返す。その時に、ブロックのサイズを記憶する。
よって解放するときにfreeに思い出させる必要はない。
A:
移植性の高い方法では不可能である。
A:
ANSI Cはこの使用方法を許している(これに関係するrealloc(...,0)
も許している。これが領域を解放する)。けれども昔のコンパイラに
は対応していないものもあるので、この方法は移植性が高いとはいえ
ない。第一引数をヌルポインターにすることで、確保する領域をだん
だん増やしていくアルゴリズムを実現するときに、起動する部分を書
くことが容易になる。
References:
ANSI Sec. 4.10.3.4; ISO Sec. 7.10.3.4; H&S Sec. 16.3
p. 388.
p = malloc(m * n);0を埋めるというのは全ビット0にするということで、ポインターや浮 動小数点数について意味のある0を埋めることを保証していない(1章 を参照のこと)。calloc()が割り当てた領域の解放にfree()を使うこ とは可能である(そうすべきである)。
memset(p, 0, m * n);
References:
ANSI Sec. 4.10.3 to 4.10.3.2; ISO Sec. 7.10.3 to
7.10.3.2; H&S Sec. 16.1 p. 386, Sec. 16.2 p. 386; PCS Sec. 11
pp. 141,142.
A:
alloca()は領域を割り当て、alloca()を起動した関数を抜けた時点で
その領域は自動的に解放される。すなわちalloca()によって割り当て
られた領域は、特定の関数の「スタックフレーム」やその関数の前後
の状況に局所的となる。
alloca()を移植性が高いように書くことはできないし、スタックのな いマシン上に実装することは難しい。これを使うことは、戻り値を直 接別の関数に渡す場合に(すぐに思いつくような実装では、スタック に基づくマシンでは必ず失敗する)、たとえば fgets(alloca(100),100, stdin)のような式で問題を招く。
これらの理由により、alloca()は便利にみえるが、広く移植性が高く なければならないプログラムでは使うことはできない。
質問7.22も参照のこと。
References:
Rationale Sec. 4.10.3.