関数内変数のポインタを受け取る時

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

関数内変数のポインタを受け取る時

#1

投稿記事 by テニス » 11年前

コード:

void charTest(char *str){
	char *num = "charTest";
	str = num;
}

int _tmain(int argc, _TCHAR* argv[])
{
	char* cMoji = "main";

	charTest(cMoji);

	std::cout << cMoji << "\n";
	return 0;
}
このようなプログラムがあり、charTest()関数の変数numのアドレスを受け取っているのですが、
main関数内のcMojiの出力は「main」のままになっています。

関数を抜けた時点でnumという変数は消滅するので、cMojiの中身は変な文字列でも入っているのかなと
思ったのですが、文字列が「main」のままというのがよくわからないです。

もし良かったら説明頂ければ嬉しいです。

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

Re: 関数内変数のポインタを受け取る時

#2

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

ポインタ変数ではなく、ポインタ値を受け渡していますので書き換わるのはポインタ値です。この違いがわかりますか?
「ちょっと補足」
ポインタ変数ではなく、ポインタ値を受け渡していますので書き換わるのは仮引数側のポインタ値です。この違いがわかりますか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

テニス

Re: 関数内変数のポインタを受け取る時

#3

投稿記事 by テニス » 11年前

ごめんなさい良く分かっていないです・・・
イメージとしては、良く参照渡しの例として挙がるスワップ関数で、
呼び出し側の変数の値を、関数内で書き換えるという形のことをしたかったのですが・・・

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

Re: 関数内変数のポインタを受け取る時

#4

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

そうですね。呼び元の引数と関数側の引数は値をコピーされます。
つまり、char *strと言うローカル変数に、char* cMojiのの代入が行われるわけです。
str = cMoji;
と同じ動作です。
これで感じはつかまめますでしょうか?

【補足】
ポインタ変数を書き換える場合、ポインタ変数のポインタを受け渡す必要があります。
俗にいうダブルポインタです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

YuO
記事: 947
登録日時: 15年前
住所: 東京都世田谷区

Re: 関数内変数のポインタを受け取る時

#5

投稿記事 by YuO » 11年前

# iostreamの<<の多重定義などのことは一切無視して,多少強引なやり方で話を進めます。

コード:

#include <iostream>
void test (int arg)
{
    int n = 10;
    arg = n;
}

int main (void)
{
    int n = 20;
    test(n);
    std::cout << n << std::endl;
    return 0;
}
が標準出力に20を出力することは理解できているでしょうか。

形式的に,intをchar *に置き換えると,

コード:

#include <iostream>
void test (char * arg)
{
    char * n = 10;
    arg = n;
}

int main (void)
{
    char * n = 20;
    test(n);
    std::cout << n << std::endl;
    return 0;
}
となります。もちろん,10や20がエラーになりますが,その点だけを修正すると,

コード:

#include <iostream>
void test (char * arg)
{
    char * n = "test";
    arg = n;
}

int main (void)
{
    char * n = "main";
    test(n);
    std::cout << n << std::endl;
    return 0;
}
となります。これは標準出力にmainという文字列を出力します。

さて,3番目のプログラムは,私が最初に提示したプログラムとは,intを単純にchar *に置き換え,エラーを潰すために整数定数を文字列に置き換えただけのものです。
つまり,最初のプログラムと3番目のプログラムに構造の違いはありません。
よって,main関数中のnはtestの呼び出しで変更されません。
そして,3番目のプログラムはテニスさんが書かれたプログラムと実質的に同じものです。

最初のプログラムでは,test関数に「nという変数」ではなく「nという変数の値」を渡しました。
構造が同じ3番目のプログラムでも,同じくtest関数に「nという変数」ではなく「nという変数の値」を渡します。
int型では「nという変数の値」はint型の値,つまりは整数値でした。
char *型では「nという変数の値」はchar *型の値,つまりはchar型のオブジェクトへのポインタになります。


swap関数という話が出て来たので,swap関数も例にしてみましょうか。
まずはintで。

コード:

#include <iostream>
void swap (int * lhs, int * rhs)
{
    int temp = *lhs;
    *lhs = *rhs;
    *rhs = temp;
}

int main (void)
{
    int n1 = 10;
    int n2 = 20;
    std::cout << "n1 : " << n1 << " / n2 : " << n2 << std::endl;
    swap(&n1, &n2);
    std::cout << "n1 : " << n1 << " / n2 : " << n2 << std::endl;
    return 0;
}
次に,これを単純にintとchar *を置き換え。

コード:

#include <iostream>
void swap (char * * lhs, char * * rhs)
{
    char * temp = *lhs;
    *lhs = *rhs;
    *rhs = temp;
}

int main (void)
{
    char * n1 = 10;
    char * n2 = 20;
    std::cout << "n1 : " << n1 << " / n2 : " << n2 << std::endl;
    swap(&n1, &n2);
    std::cout << "n1 : " << n1 << " / n2 : " << n2 << std::endl;
    return 0;
}
そして,char *になったことに伴うエラーつぶし。

コード:

#include <iostream>
void swap (char * * lhs, char * * rhs)
{
    char * temp = *lhs;
    *lhs = *rhs;
    *rhs = temp;
}

int main (void)
{
    char * n1 = "abc";
    char * n2 = "zyx";
    std::cout << "n1 : " << n1 << " / n2 : " << n2 << std::endl;
    swap(&n1, &n2);
    std::cout << "n1 : " << n1 << " / n2 : " << n2 << std::endl;
    return 0;
}
このように,ある型Tの変数の中身を呼び出す関数側で書き換えたいならば,T *と宣言します。
# C++ならばT &でもよい。
「何の型を扱っているのか」を意識してプログラムを書くと,このあたりで悩むことはなくなると思います。

テニス

Re: 関数内変数のポインタを受け取る時

#6

投稿記事 by テニス » 11年前

softyaさんYuOさんありがとうございます!なんとなくわかってきた気がしてきました!

つまり、自分は charTest(cMoji); と関数を呼び出すことで、charTest( &cMoji[0] )という感じで、
文字列の先頭アドレスを参照渡ししていた思っていたつもりが、
文字列は配列として扱われるので、

文字列の先頭アドレスを渡す(cMoji or &cMoji[0]) = char型変数に格納された文字列が表示される = 単に値渡しをしているだけだった

ということでしょうか。文章下手で申し訳ないですが。

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

Re: 関数内変数のポインタを受け取る時

#7

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

文字列は配列というか、char*はポインタ変数ですよね。
で、参照渡しは書き換える変数のポインタを渡す必要があります。
(char*)という変数のポインタは(char*)*となります。
と単純に考えて頂ければ。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

テニス

Re: 関数内変数のポインタを受け取る時

#8

投稿記事 by テニス » 11年前

普通の変数とポインタ変数を同じように扱って、あまり区別がついてなかったようです・・・
(ポインタ変数にもポインタがあるということがいまいち分かっていなかった)

1回の質問で2重ポインタのことまでわかりました。
ありがとうございました!

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

Re: 関数内変数のポインタを受け取る時

#9

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

あと、調子に乗って書きましたが (char*)* は実際には書けませんのでご注意ください。あくまでイメージです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

テニス

Re: 関数内変数のポインタを受け取る時

#10

投稿記事 by テニス » 11年前

いえ、その書き方が凄くイメージ伝わりやすかったですw
まさか2重ポインタまで理解できるようになるとは思いませんでした。

閉鎖

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