- PR -

String型の値渡し

投稿者投稿内容
みすたーX
会議室デビュー日: 2005/08/20
投稿数: 4
投稿日時: 2005-08-20 18:20
こんなプログラムを書いてみました。
chgメソッドには参照情報が渡されているので、chgメソッドで値が変更された場合呼び出し元に影響するのかなとおもったのですが…。
つまりメッセージで表示されるのは2かな?と思ったのですが…。
私の考えは間違ってますでしょうか?

Module Module1
Sub main()
Dim s As String = "1"
chg(s)
MessageBox.Show(s)
End Sub

Private Sub chg(ByVal s As String)
s = "2"
End Sub
End Module
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2005-08-20 18:28
コード:

Module Module1
Sub main()
Dim s As String = "1"
chg(s)
MessageBox.Show(s)
End Sub

Private Sub chg(ByVal s As String)
s = "2"
End Sub
End Module



それは、「変更している」ではなく、「参照先を変えている」では?
それだと String 型だとか関係なく他の参照型も変わらないです。

--編集
もうちょっと詳しく書きます。
string a = "aiueo";
string b = a;
とした場合、a と b を格納するメモリと、"aiueo"を格納するメモリができます。(a,bはスタックに、"aiueo"はヒープに)
a には入る値は、"aiueo"のアドレスです。
b に入る値も、"aiueo"のアドレスです。
次に、
a = "kakikukeko";
としたら、同様に、"kakikukeko"がヒープに格納され、a には"kakikukeko"のアドレスが入ります。b は "aiueo"のアドレスが入ったまま。
上記の場合は、b = a としてますが、メソッドに値渡しするときも同じことです。

[ メッセージ編集済み 編集者: 囚人 編集日時 2005-08-20 18:35 ]

[ メッセージ編集済み 編集者: 囚人 編集日時 2005-08-20 18:36 ]
架空兎
ベテラン
会議室デビュー日: 2003/08/18
投稿数: 78
お住まい・勤務地: さいたま氏
投稿日時: 2005-08-20 19:41
引用:

みすたーXさんの書き込み (2005-08-20 18:20) より:

コード:

Private Sub chg(ByVal s As String)
    s = "2"
End Sub




chg メソッドの引数 s に渡されたインスタンスと、chg メソッド内の「"2"」は別のインスタンスです。
つまり、引数 s のインスタンスに対して操作をしているわけではなく、
引数 s に別のインスタンスを代入しているだけなので呼び出し元には影響は与えません。
ほげた
ベテラン
会議室デビュー日: 2002/05/08
投稿数: 67
お住まい・勤務地: なごやん
投稿日時: 2005-08-20 23:46
みすたーXさんの疑問は
 stringは参照型なのに、ByValで渡した先で変更しても
 呼び出しもとのstringが変更されない。
ということですよね。

囚人さんのおっしゃるように、stringの値を変更する場合は
格納値を変更するのではなく、参照先の変更が行われます。
一度インスタンス化されたstringの文字列が変更されることはありません。
変更は必ず、新規文字列を別に用意して、参照先をそこへ変更することで実現されます。

なので string が参照型であっても、呼び出し先で変更された時点で
呼び出し元とは異なるものを指すようになるだけで
呼び出し元の値に影響を与えません。

stringは参照型だが、値型として扱えるようになっている
と思うと簡単です。
jk
ベテラン
会議室デビュー日: 2005/08/19
投稿数: 94
投稿日時: 2005-08-21 00:06
横レスですが、c言語で書くとこういうことですよね?

typedef char *STRING;

void chg(STRING p){
 p="2"; // スタック上のポインタ変数pを変えたに過ぎない
}

main(){
STRING s = "1";
 chg(s);
}
-------------------------------------
逆に呼び出しもとに影響を与えたい場合は ByRefにすればいいことですよね?

同じくc言語で記述すると

typedef char *STRING;

void chg(STRING *p){
 *p="2"; // ポインタがさしている参照を書き換え。
}

main(){
STRING s = "1";
 chg(&s);
}

------------------------------------------
VB.netを持っていないのでわかりませんけど、通常はこんな挙動ですよね?
間違っていたら指摘してください。
未記入
大ベテラン
会議室デビュー日: 2005/03/12
投稿数: 148
投稿日時: 2005-08-21 02:27
イメージ的にはこっちだと思う。
typedef char const * STRING;

俺が試したのはC#だけど似たような疑問もったな。
『C/C++的に考えて、文字列はポインタだから
オブジェクトの参照渡しと同じで
呼び出しもとのオブジェクト(メモリ)を書き換えるはずなのに!』
ってね。

この方が安全なんだろうなってそのときは思った。
でも間違えてしまうよな。
katsum
大ベテラン
会議室デビュー日: 2002/02/27
投稿数: 119
お住まい・勤務地: 東京都
投稿日時: 2005-08-21 11:03
引用:

みすたーXさんの書き込み (2005-08-20 17:52) より:
String型は参照型なので値渡し(Byval)で渡したとしても実際は値じゃなくて参照情報がわたされるので、
渡された先で値が変更された場合、呼び出し元の値も変更されてしまうのか?と思ったのですが、サンプルのプログラムを作って動かしたところ呼び出し元の変数の値は変更されていませんでした。
この考え方はそもそも間違っているのでしょうか?


C++でオブジェクトのポインタを値渡しした場合、ポインタの値のコピーが渡されますが、そのコピーポインタが指してるオブジェクトは呼び出し元のオブジェクトなので、ポインタを値渡ししても渡されたポインタ経由で呼び出し元のオブジェクトの中身を変える(不注意の場合は壊す)ことができます。おそらくこの現象のことを指しているのではないでしょうか。

ところがC++もそうですが、ポインタではなくオブジェクトを値渡しした場合は、そのオブジェクトのコピーが作られ、そのコピーが呼び出し先に渡されるので、呼び出し先のコピーをいくら変更したところで呼び出し元のオブジェクトに影響を与えることはなくなります。
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2005-08-21 11:47
単純に、System.Stringにはメンバを変更するメソッド/プロパティがないから変更できない、というだけだと思いますが。
例えば文字列の1文字目だけを置き換えようにもCharsプロパティ(C#のインデクサ)は読み取り専用であるとか。
直接文字列を代入したりするのは参照先そのものを変更するという事ですから値渡しの時に呼出元に影響しないのは当然ですよね。

//内部実装的にはもっと色々問題があると思いますが(変更不可にしてるのは速度を稼ぐためとか聞いたことがあるし)
//取りあえずの認識として。

実のところ、System.Stringにフィールドとして存在しているm_firstCharメンバをリフレクションを利用して変更してやれば、呼出元の文字列も変更されますし。

コード:

//using System;
//using System.Reflection;
static void Main() {
string str = "aaa";
Console.WriteLine(str);
Does(str);
Console.WriteLine(str);
}
static void Does(string value) {
BindingFlags flag = BindingFlags.NonPublic | BindingFlags.Instance;
FieldInfo field = typeof(string).GetField("m_firstChar", flag);
field.SetValue(value, 'z');
}



[ メッセージ編集済み 編集者: Hongliang 編集日時 2005-08-21 12:07 ]

スキルアップ/キャリアアップ(JOB@IT)