ポインタについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
SD

ポインタについて

#1

投稿記事 by SD » 10年前

明解C言語という本を読んでいて、ポインタというC言語の鬼門とやらにぶちあたっています。

値渡しの際、関数sum_deffの中で仮因数sumやdiffの値を変更していても、
その関数に値渡しする変数にはなんの変化もないということで、それはなんとなく分かるのですが。

それがなぜポインタを使うことによって値渡しをした関数内での変化が、もとの値渡しをする
変数に影響を与えるのかがしりたいです。
よろしくおねがいします

non
記事: 1097
登録日時: 13年前

Re: ポインタについて

#2

投稿記事 by non » 10年前

ポインタも値渡しなんですが、それは置いといて。

値を入れる箱(実体がはいる変数)がどこに用意されるのかということを考える必要があります。
関数に引数で渡された変数はローカル変数ですから、その関数から戻るときに消えてなくなります。
ですから、そこの値を関数内で変更しても呼び出し側からは知る由がないということです。
non

ただの屍のようだ

Re: ポインタについて

#3

投稿記事 by ただの屍のようだ » 10年前

ほとんど回答になってない回答では質問者がかえって混乱するので、わかりやすく説明します。
例としてprintf()にint xを使います。
1.printf("%d\n",x);
仮引数が2つあります。よって、
関数呼び出したときにchar変数列に第一引数のコピー、int 型変数に第二引数(x)のコピーが作成されます。
関数終了時に仮引数は削除され、もとのxや"%d\n"に影響はないです。
2.printf("%d\n",&x);
仮引数が2つあります。よって、
関数呼び出したときにchar変数列に第一引数のコピー、int型ポインタに第二引数(xのアドレス)のコピーが作成されます。
関数終了時に仮引数は削除され、もとの"%d\n"に影響はないですが、アドレスによって参照されたxは変更されたままです。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: ポインタについて

#4

投稿記事 by h2so5 » 10年前

ただの屍のようだ さんが書きました: 例としてprintf()にint xを使います。
1.printf("%d\n",x);
仮引数が2つあります。よって、
関数呼び出したときにchar変数列に第一引数のコピー、int 型変数に第二引数(x)のコピーが作成されます。
「char変数列に第一引数のコピー」ってどういう意味でしょうか?
第一引数で渡されるのはアドレスのコピーであって、char変数列ではないと思いますが。

ただの屍のようだ

Re: ポインタについて

#5

投稿記事 by ただの屍のようだ » 10年前

配列渡すときは先頭のポインタを渡すが
このタイミングでいうと今度は配列とポインタをごちゃごちゃしてしまいそうなので
あえて、わかりやすいようリテラルそのものをコピーしたという表現をとりました。まる

ただの屍のようだ

Re: ポインタについて

#6

投稿記事 by ただの屍のようだ » 10年前

ポインタは便利な機能ですが、やはり2,3行で説明しきれるものではないので、
「C言語 ポインタ完全制覇」をお勧めします。ざっくり読んだだけでもポインタの知識は身につきます。
ついでに、ヘッダファイルやら可変長構造体やらも勉強できるので、
時間取れるのであれば書物での学習のほうが効率いいです。そのぶん、金かかります。

せっかくなので課題出します。

コード:

	char str[] = "Hello!!",*p;		//なぜ str[] を *str にするとエラーとなるのか
	p=str;
	*(++p) = 'a';
	printf("\n%s",str);
あとで聞かれてもいつ返事返せるか(返す気になるのか)わからないので、答えあらかじめいっておきます。 文字列リテラルは変更できません

non
記事: 1097
登録日時: 13年前

Re: ポインタについて

#7

投稿記事 by non » 10年前

SD さんが書きました:それがなぜポインタを使うことによって値渡しをした関数内での変化が、もとの値渡しをする
変数に影響を与えるのかがしりたいです。
そんなんじゃわからないだろうと批判を受けましたので、もう少し詳しく。
ポインタを使う場合、実際に値が入る箱は呼び出し側に用意されており、関数には、その箱のアドレスが渡されます。
関数側では、そのアドレスを使って、アドレスが指している場所(呼び出し側に用意された箱)の値を直接操作します。
関数から帰るとき、アドレスは忘れてしまいますが、呼び出し側の箱は直接変更されているため、値が変わってます。
non

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: ポインタについて

#8

投稿記事 by ISLe » 10年前

int *p, a;
p = &a;
屍さんのようにpがaのエイリアスとなるかのように説明するので混乱するひとが後を絶たないのだと思いますが。

まず変数をきちんと理解しましょう。
int a;
と宣言するとint型の値を格納する記憶域が用意されます。

その記憶域をaという名前で参照できます。
変数名を使うのは記憶域にアクセスするための手段です。

記憶域には固有のアドレスがあります。
アドレスは値です。
&aは変数aの記憶域のアドレスです。

int *p;
変数pも変数です。
int*型の値を格納する記憶域を持ちます。

p = &a;
&aで取得したアドレス(値)をコピーして変数pに格納します。

pは変数pの記憶域にアクセスします。
そこには、アドレスが入っています。
*pだと変数pの記憶域に格納されたアドレスが示す先の記憶域にアクセスできます。

関数の仮引数にアドレスのコピーを渡すことで、そのアドレスが示す先の記憶域を書き換えることができるわけです。

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: ポインタについて

#9

投稿記事 by ISLe » 10年前

ただの屍のようだ さんが書きました:あとで聞かれてもいつ返事返せるか(返す気になるのか)わからないので、答えあらかじめいっておきます。 文字列リテラルは変更できません
こういうふうに書くと、ウィンドウズだと書き換えられるけどなんで?って話になってしまうでしょう。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: ポインタについて

#10

投稿記事 by h2so5 » 10年前

ただの屍のようだ さんが書きました:ポインタは便利な機能ですが、やはり2,3行で説明しきれるものではないので、
「C言語 ポインタ完全制覇」をお勧めします。ざっくり読んだだけでもポインタの知識は身につきます。
ついでに、ヘッダファイルやら可変長構造体やらも勉強できるので、
時間取れるのであれば書物での学習のほうが効率いいです。そのぶん、金かかります。

せっかくなので課題出します。

コード:

	char str[] = "Hello!!",*p;		//なぜ str[] を *str にするとエラーとなるのか
	p=str;
	*(++p) = 'a';
	printf("\n%s",str);
あとで聞かれてもいつ返事返せるか(返す気になるのか)わからないので、答えあらかじめいっておきます。 文字列リテラルは変更できません
文字列リテラルはこの場合 "Hello!!" の部分を指します。
str[] と str* はリテラルとは関係ありませんから、文字列リテラルは変更できないというのは誤りです。

ただの屍のようだ

Re: ポインタについて

#11

投稿記事 by ただの屍のようだ » 10年前

変更できません。実行時エラーになるはずです。
少なくとも自分のgccではちゃんと強制終了になりました。
あと、警告メッセージ見てみるとわかると思いますが、const付きなのでC++でconst_castしない限り変更できないはずです。
そして、変更できない理由としては、文字列リテラルは変数ではなく、定数ですから。
(結局、自分で問題出して、自分で解説して回答まで書く形になるとは・・・)

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: ポインタについて

#12

投稿記事 by softya(ソフト屋) » 10年前

私の環境で試すと、ファイル名をmain.cと仮定しますがcygwinのgcc main.cでもcl main.cでコンパイルしてもエラーは出ませんし、実行したらhalloに書き換えられています。
状況を限定するなら特定の状況ではと書くべきで、全部に通じるかのように書くのは危険だと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ただの屍のようだ

Re: ポインタについて

#13

投稿記事 by ただの屍のようだ » 10年前

もちろん、↓のようなコードで実行しましたよね?

コード:

    char *str = "Hello!!",*p;
    p=str;
    *(++p) = 'a';
    printf("\n%s",str);

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: ポインタについて

#14

投稿記事 by softya(ソフト屋) » 10年前

ただの屍のようだ さんが書きました:もちろん、↓のようなコードで実行しましたよね?

コード:

    char *str = "Hello!!",*p;
    p=str;
    *(++p) = 'a';
    printf("\n%s",str);
その通りですが、検証方法書かれていないただの屍のようださんの方が不明確な論証だと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: ポインタについて

#15

投稿記事 by ISLe » 10年前

だからウィンドウズの処理系では文字列リテラルを書き換えられると既に書いているでしょう。

未定義の動作についてきちんと調べてから書くようにしてはいかがですか?
そもそも今回は未定義の動作に触れる必要ないですし。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: ポインタについて

#16

投稿記事 by softya(ソフト屋) » 10年前

そうですね。質問者の方に迷惑なので、話を元に戻しましょう。
リテラルへの代入は話がややこしくなるだけなので、今回はパスしたほうが良いと思います。
ISLe さんのNo: 8の説明が一番明確だと思いますので、SDさんはその内容で疑問があれば質問してもらうのが良いと私は思います。

【追記】 一応図に書いてみたけど、イマイチかもしれません。
アドレスは仮の値で現実には、この値ではないです。アドレス0x1000などが変数に割り当てられるメモリ・アドレスです。
ポインタ.png
ポインタ
ポインタ.png (8.75 KiB) 閲覧数: 6439 回
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ただの屍のようだ

Re: ポインタについて

#17

投稿記事 by ただの屍のようだ » 10年前

ふむふむ、これは突っ込むべきか、スルーすべきか、実に悩むところです。
必要最低限だけ説明しよう。
char str[] = "Hello!!"; は単なる str = {'H','e','l','l','o','!','!','\0'}; の略で、配列をHello!!で初期化すると言う意味です。
char *str = "Hello!!" は 文字列リテラルの先頭アドレスをstrに代入するという意味です。
*ちなみに、自分のPCはwindows7です。コンパイラはgccでもVC++2010でも実行時エラーで異常終了します。

かずま

Re: ポインタについて

#18

投稿記事 by かずま » 10年前

ただの屍のようだ さんが書きました:変更できません。実行時エラーになるはずです。
少なくとも自分のgccではちゃんと強制終了になりました。
gcc -fwritable-strings main.c を試してみてください。

ただの屍のようだ

Re: ポインタについて

#19

投稿記事 by ただの屍のようだ » 10年前

別にgcc -fwritable-strings main.c で試すまでもない、ボーランドC++5.5なら警告すら出さずに通してくれます。エラーにもなりません。
昔、この掲示板で言われたことをそのまま(正確に覚えてないが可能な限り原文に近い形で)言い返します。
VC++だけ通るコードをいくら書いてもなんの価値もないです。そもそも肝心のVC++すら通らないじゃないですか(笑)

アバター
usao
記事: 1887
登録日時: 11年前

Re: ポインタについて

#20

投稿記事 by usao » 10年前

「実際にどうなるのか」については置いといても,
>ボーランドC++5.5なら警告すら出さずに通してくれます。エラーにもなりません。
これって,ご自身で 「なぜ エラーになる のか」 という課題とやらを出してることに対して矛盾してますよね?

本題については 私も
>ISLe さんのNo: 8の説明が一番明確だと思います

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: ポインタについて

#21

投稿記事 by softya(ソフト屋) » 10年前

既に質問内容と論点がかけ離れてますのでSDさんの迷惑になります。リテラル問題は別のトピックに移っていただくようにお願いします。 以後はあちらでお願いします。
「激論中の文字列リテラル • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/viewtopic.php?f=3&t=13395
こちらは、質問主であるSDさんの質問に回答する場としたいと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
usao
記事: 1887
登録日時: 11年前

Re: ポインタについて

#22

投稿記事 by usao » 10年前

>2.printf("%d\n",&x);
>仮引数が2つあります。よって、
>関数呼び出したときにchar変数列に第一引数のコピー、int型ポインタに第二引数(xのアドレス)のコピーが作成されます。
>関数終了時に仮引数は削除され、もとの"%d\n"に影響はないですが、アドレスによって参照されたxは変更されたままです。

誰も触れてないけど これでxの値が何か別のものに変更されるとは思えないのですが??

アバター
Tatu
記事: 445
登録日時: 13年前
住所: 北海道

Re: ポインタについて

#23

投稿記事 by Tatu » 10年前

ポインタ型でない変数を引数にした場合とポインタ型変数を引数にした場合で
値の変化にどのような違いが出るかを調べるために以下のようなコードを書きました。

コード:

#include <stdio.h>

void functionA(int x){
	x=10;
}

void functionB(int *q){
	int b;
	q=&b;
	printf("functionB内で宣言されたbのアドレス\n");
	printf("&b=%d\n\n",&b);
}

void functionC(int *q){
	*q=10;
}

int main(void){
	int a=0;
	int *p=&a;

	printf("初期化後\n");
	printf("a=%d,&a=%d,p=%d,*p=%d\n\n",a,&a,p,*p);

	functionA(a);

	printf("functionA(a)実行後\n");
	printf("a=%d,&a=%d,p=%d,*p=%d\n\n",a,&a,p,*p);

	functionB(p);

	printf("fuctionB(p)実行後\n");
	printf("a=%d,&a=%d,p=%d,*p=%d\n\n",a,&a,p,*p);

	functionC(p);

	printf("functionC(p)実行後\n");
	printf("a=%d,&a=%d,p=%d,*p=%d\n\n",a,&a,p,*p);

	return 0;
}
実行結果の例

コード:

初期化後
a=0,&a=4127592,p=4127592,*p=0

functionA(a)実行後
a=0,&a=4127592,p=4127592,*p=0

functionB内で宣言されたbのアドレス
&b=4127352

fuctionB(p)実行後
a=0,&a=4127592,p=4127592,*p=0

functionC(p)実行後
a=10,&a=4127592,p=4127592,*p=10

続行するには何かキーを押してください . . .
functionAの場合は引数xで整数型の値を受け取り、引数xの値を変えるだけで他には何の影響もありません。

functionBの場合は引数qで整数型のポインタの値を受け取り、
整数型の変数bを宣言して引数qの値を&bに変えていますがこれも何の影響もありません。

functionCの場合は引数qで整数型のポインタの値を受け取り、引数qの値が示す場所の値を変えています。
例の場合ではqには&aが入り、aの値を変えることになります。


functionAでは受け取った値を10にしていて(x=10;)、
functionCでは受け取った値が示す場所の値を10にしている(*q=10;)
という違いがあります。

閉鎖

“C言語何でも質問掲示板” へ戻る