C言語関係掲示板

過去ログ

No.103.ポインタの存在理由


No.562

ポインター
投稿者---DT(2001/11/19 05:07:55)


こんにちわ。質問があります。基本的な、しかし重要な質問です。
「ポインターって何故必要なのでしょう?」
私はBASICやVB経験者でVCも1年前に買ったのですが
ちょっとやろうとするとVBのほうに逃げてしまう。
その理由を考えたのですが、やはり「ポインターがわからない」
ことにつきます。もっと言えば「ポインターの存在理由」が
わからないのです。
何故VBにはポインターが必要なくてCには必要なのか、も含めて
ご教授いただけませんか?



No.563

Re:ポインター
投稿者---shu(2001/11/19 11:06:48)


>こんにちわ。質問があります。基本的な、しかし重要な質問です。
>「ポインターって何故必要なのでしょう?」
>私はBASICやVB経験者でVCも1年前に買ったのですが
>ちょっとやろうとするとVBのほうに逃げてしまう。
>その理由を考えたのですが、やはり「ポインターがわからない」
>ことにつきます。もっと言えば「ポインターの存在理由」が
>わからないのです。
>何故VBにはポインターが必要なくてCには必要なのか、も含めて
>ご教授いただけませんか?

変数とポインタ変数、
実行ファイルとショートカット、
テレビとリモコン、
この関係のようなものです。(あくまで例えなので)
あったほうが便利でしょう?

テレビのチャンネル変える時、いちいちテレビ本体のチャンネル変えるより
リモコンで変えたほうが楽だし、テレビ2台を用意して、TV1のチャンネル
変えて、TV1と同じチャンネルにTV2のチャンネルを合わせるのって、
考えるだけで、とてつもなく面倒でしょう。いかんせんテレビの置き場所
がないでしょう。
(テレビを変数、リモコンをポインタと考えて見てください。)

No.564

Re:ポインター
投稿者---kikk(2001/11/19 16:14:12)


ども。


VBについては、たまにWSHのスクリプトを書くくらいであまり詳しくはないの
ですが。。

VBにもポインタに相当するものはあるようです。VBのリファレンスでSetやDim
ステートメントをみると、オブジェクトの実体がどうとか、参照がどうとかと
いう話が書いてあります。そのなかの、オブジェクトをさす変数がポインタ
(ポインタ変数)になります。

VBはCよりもオブジェクティブなので、参照という概念が割ときれいに表現されて
いますが、やっていることは同じです。ただし、VBと違い、Cは低機能なので
どの変数からも参照されなくなったオブジェクトを勝手に破棄する(ガベージ
コレクション)などといった、気のきいたことはやってくれません。オブジェクト
(メモリ)管理は、全部、プログラマがプログラムに明示的に指示することに
よって行わなければなりません。

(C++はともかく)CはVBなどの最近の言語にくらべるとかなり泥臭いというか、
マシンに近いレベルの言語といえます。ある程度、マシン内部のことも知って
いなければならないので、高機能な言語からCに進む人はちょとつらいかも
しれません。

ちなみに、JAVAにはポインタがないことになっていますが、Cを知っている人に
いわせると、クラスのインスタンスを保持する変数はポインタそのものだ、と
いわれます。


では。

No.565

Re:ポインター
投稿者---ともじ(2001/11/19 17:07:05)


こんにちは、DTさん。

>何故VBにはポインターが必要なくてCには必要なのか、も含めて
>ご教授いただけませんか?

私も初めてCのプログラムを作ったときには、ポインタがきちんと使えず、
core dump(異常終了)をよく起こしていました。それ以前に組んでいた
FORTRANではポインタは使いませんでしたので、DTさんと同じ感想を
持ちました。

で、そのときに、ポインタでアクセスをした方が、配列でアクセスを
するよりも速いと教わりました。Cは制御系の処理やOSを組むのに
良く使われる言語なので、少しでも速く処理できるようにポインタを
使うのではないでしょうか。

今はCPUの処理能力も飛躍的に高くなりましたし、最近の言語では
あえてポインタを使わなくてもいいのかもしれません。そういうわけで、
VBやJAVAは仕様からポインタを省いたのかもしれませんね。

ただ、確かにポインタは配列に置き換えて処理できますが、ポインタ
なしでは処理できないところがあるんです。
例えばコマンドライン引数などでは、ポインタ抜きには考えられません。
そういうポインタの概念の必要な部分を、VBやJAVAでは特別な変数で
処理しているんでしょうね。


No.600

Re:ポインター
投稿者---DT(2001/11/27 01:33:09)



>で、そのときに、ポインタでアクセスをした方が、配列でアクセスを
>するよりも速いと教わりました。Cは制御系の処理やOSを組むのに
>良く使われる言語なので、少しでも速く処理できるようにポインタを
>使うのではないでしょうか。

みなさんレスありがとうございます。
アクセスの速さやCがよりマシン語よりというのがポインターの存在理由と関係しているということよくわかりました。

ところで、「ポインタでアクセスをした方が、配列でアクセスを
するよりも速い」ということについてですが、当方配列についてはある程度理解しているのですが、同じことをやるのに「配列で処理した場合」と「ポインターで処理した場合」でどう違うのか?、、をご教授していただきたいのですが、、、。簡単かつ具体的なコード例などで指示いただけると幸いです。
お手数おかけして恐縮ですが、なにとぞよろしくお願いいたします。m(--)m


No.601

Re:ポインター学習のための教則本
投稿者---DT(2001/11/27 01:42:08)


それと、ポインターを学ぶのに適した本を紹介いただきたいのですが。。。
やはりカーニハン&リッチ―の本ということになるのでしょうか?

他にもなにかありましたらよろしくお願いいたします。,,m(--)m,,

No.606

Re:ポインター学習のための教則本
投稿者---ともじ(2001/11/27 18:08:59)


>それと、ポインターを学ぶのに適した本を紹介いただきたいのですが。。。

これなんかどうでしょう。
http://www.bohyoh.com/Books/HidenPtrB/index.html


No.605

Re:ポインター
投稿者---ともじ(2001/11/27 18:01:06)


こんにちは。

>ところで、「ポインタでアクセスをした方が、配列でアクセスを
>するよりも速い」ということについてですが、当方配列についてはある程度理解しているのですが、同じことをやるのに「配列で処理した場合」と「ポインターで処理した場合」でどう違うのか?

自分で実際に計測したことは無かったので、いろいろとやってみました。
ポインタで処理をした方が速くなるのは、関数の引数に使った場合で、
単に同一関数内で配列と使い比べた場合は、ループ変数以外にポインタ
自身のインクリメントが必要になるなど、かえってポインタの方が遅い場合
ばかりでした。
関数の引数に指定した場合もコンパイラによって違いがでるようです。

以下のプログラムで、
Turbo-C++ V.4 では
  配列ループ1000000*100の時間: 1.200000秒
  ポインタループ1000000*100の時間: 0.720000秒
BCC32
  配列ループ1000000*100の時間: 0.759000秒
  ポインタループ1000000*100の時間: 0.960000秒
でした。


#include <time.h>
#include <stdio.h>
void dtset1(int dt[]);
void dtset2(int *p);

int main(void)
{
	clock_t start, end;
	int dt[100];
	long l;

/* 配列を引数に指定した場合 */
	start = clock();

	for (l=0; l<1000000; l++) {
		dtset1(dt);
	}
	end = clock();
	printf("配列ループ1000000*100の時間: %f秒\n",
		 (double)(end - start) / CLOCKS_PER_SEC);

/* ポインタを引数に指定した場合 */
	start = clock();

	for (l=0; l<1000000; l++) {
		dtset2(dt);
	}
	end = clock();
	printf("ポインタループ1000000*100の時間: %f秒\n",
		 (double)(end - start) / CLOCKS_PER_SEC);

	return(0);
}

void dtset1(int dt[])
{
	int	i;

	for (i=0; i<100; i++)
		dt[i] = i;
}

void dtset2(int *p)
{
	int	i;

	for (i=0; i<100; i++)
		*(p++) = i;
}


No.610

Re:ポインター
投稿者---WRX(2001/11/28 09:46:35)



Re:ポインター
投稿者---WRX(2001/11/28 09:28:55)


こんにちはWRXです

さっそくですが、配列とポインタの話は、
”C言語ポインタ完全制覇”(前橋和弥 著)と言う本にのっています

例えば、次のコード
int s[100];
int *p = s;

s[5] = 1;
5[s] = 2;//s[5]=2と同じ
*(s + 5) = 3;//s[5]=3と同じ

p[5] = 4;//s[5]=4と同じ
5[p] = 5;//s[5]=5と同じ
*(p + 5) = 6;//s[5]=6と同じ
こんなかきかたもできます
ただし著者もあまり変な書き方(5[s]とか...)はしない方がいいと書いていす

また、関数の引数として配列を渡す、についてはp68に
>”正しくいうと、Cでは、関数の引数として配列を渡すことはできません。”
>”先頭要素へのポインタを渡すことで、配列を引数として渡したかのように”
>”扱うことができます。”
とあります

処理速度の違いについては、No.607でB.Smithさんが指摘しているように
dtset1の

>dt[i] = i;

がコンパイル後(BCB5のデバッグモード、最適化無し)
アセンブラでは *(dt + i * 4) = i;(4はintのサイズ)をコンパイル
した様なコードになってしまい、毎回乗算が実行されますが、dtset2では、
加算のみで乗算がありません
処理速度は通常、乗算が加算より遅いため処理速度に差がでるようです

BCCでポインタ使用が遅い理由はわかりませんが、BCBではコードガードなど
を使用していると、配列の範囲外への監視が強化されているため遅くなるよ
うです。

No.607

Re:ポインター
投稿者---B.Smith(2001/11/27 20:48:00)


通常はあまり意識しませんが、基本的な違いはコンパイル後のコードにあります。配列をインデックスにより参照した場合、論理的に必要な処理は、

ベースアドレス + 配列1要素分の型サイズ × インデックス

になります。

ポインタにより参照した場合は、ベースアドレスに「配列1要素分の型サイズ」を加算していけば良いことになります(ポインタの値をインクリメント/デクリメントした場合、どのようにアドレスが移動するかは・・・もうご存知ですよね?)。

通常、乗除算は加減算よりも遅いため、繰り返し参照されると乗算にかかる時間が累積となり、速度の違いが出てくるわけです。

この辺りの話は基本的なコード生成の話になってきますので、コンパイラにより違いが発生する可能性があります。また、オプティマイザの影響を強く受けますので、タイムクリティカルな処理を記述するのではない限り、インデックスを使用した方法とポインタを使用した方法の使い分けは、普段はあまり気にする必要は無いと思います。



戻る


「初心者のためのポイント学習C言語」 Last modified:2002.01.11
Copyright(c) 2000-2002 TOMOJI All Rights Reserved