ポインタの参照渡し

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

ポインタの参照渡し

#1

投稿記事 by hennmi » 15年前

質問です。

ポインタの参照渡しを行いたいのですが、中身がどうなっているのかわからず
混乱中です。どうかご指導宜しくお願いします。。。

以下にソースコードを記載します。
=======================================
int main(void) {
    int len;
    char str;

    len = get(&str);
    out(&str, len);
}

int get(char *str) {
    int i;

  str = (char *)malloc(3 * sizeof(char));
    for (i = 0; i < 2; i++) {
        *(str + i) = 'a';
    }
    return 2;
}

void out(char *str, int len) {
    int i;

    for (i = 0; i < len; i++) {
        printf("%c\n", *(str + i));
    }
}

=======================================
get関数で、参照渡しした変数に値を代入し
main関数に戻す という処理を行いたいのですが。。。

get関数で行っていること(私の頭の中での考え)が
1. malloc で参照渡しされたアドレスの領域を 3つ確保する。
----- -------------
| ? | -> | ? | ? | ? |
----- -------------

2. 確保した領域の "1", "2" に "a"を入れる。
------------- -------------
| ? | ? | ? | -> | a | a | ? |
------------- -------------

質問は3つあります。
1. 上記の考え方のどこが間違えているのか。
2. main関数からget関数に "str" を渡すとき
&str としているのですが、これは "str" の先頭アドレスを渡しているのか。

3. get関数内で "*str", "str" はそれぞれ、何を意味しているのか(アドレス, 値)
("*" が付くものはアドレス だと思っているのですが、 "str" これもアドレスじゃないのかと
思ってしまいます。)

2日かけても中身が良くみえてきません。こんなに難しいとは。。。
どうしても、どうしても理解したいので、どなたかご教授お願いします!

たかぎ

Re:ポインタの参照渡し

#2

投稿記事 by たかぎ » 15年前

言語不明ですが、C++であれば...

int get(char*& str)

とすればOKです。

Libra

Re:ポインタの参照渡し

#3

投稿記事 by Libra » 15年前

main関数の中のchar str;をchar *str;に変更して動かしてみました。
#include <stdio.h>
#include <stdlib.h>

//プロトタイプ宣言====================================
int get( char **str ) ;
void out(char *str, int len) ;


//main関数============================================
int main(void)
{
    int len;
    char *str;    //char型のポインタに変更

    len = get( &str ) ;

    out( str , len ) ;
    
    //free( str ) ;    //動的確保したメモリを解放
    
    return 0 ;
}



int get( char **str )//引数をchar型のポインタのポインタに変更
{
    int i;
    
    *str = (char *)malloc( 3 * sizeof(char) ) ;
    
    for( i = 0 ; i < 2 ; i++ )
    {
        *(*str + i) = 'a';
    }
    //*(*str + i) = NULL ;    //終端文字
    return 2;
}



void out(char *str, int len)
{
    int i;
    for(i = 0; i < len; i++)
    {
        printf("%c\n", *(str + i));
    }
}

hennmi1

Re:ポインタの参照渡し

#4

投稿記事 by hennmi1 » 15年前

>たかぎさん
言語は cです、すみません。。

>Libraさん
ポインタのポインタを使用しなくてもできる ときいて
今回の問題が起きたのですが
もしやポインタのポインタを使用しなきゃ参照渡しはできない。。。?

Libra

Re:ポインタの参照渡し

#5

投稿記事 by Libra » 15年前

プログラムの復習中なので以下書いた内容等間違ってたらどなたか訂正お願いします。m(_ _)m

char型の変数を参照渡しした時、
参照渡しした関数で変更できるのは、char型の変数です。


今回、main関数で宣言した変数に、
mallocで確保したアドレスを代入するので、
変更したいものはchar型のアドレスです。
よって、main関数で宣言するのは、char型のポインタ変数、
そして、関数は参照渡しを行うので、引数はchar型のポインタのポインタとなります。




以下、char型の変数を参照渡しで渡し、関数内でアドレス変更しても、
main関数の変数のアドレスは変化しないよーというサンプル。(作ってみました。)
hennmi1さんが最初に書いたプログラムの流れと一応同じ。(mallocは使ってませんが、アドレス変更の流れ等)
#include <stdio.h>


int get(char *str) {

    char *test = "aa";                //適当なアドレス
    
    printf("関数の時点\t%p\n",str);    //アドレス表示

    str = test ;                    //アドレス変更
    
    printf("変更後\t\t%p\n",str) ;    //アドレス表示
    
    return 0 ;
}

int main(void)
{
    int len;
    char str = 'A' ;
    
    printf("最初\t\t%p\n",&str);        //現在のアドレス表示
    
    len = get( &str ) ;
    
    printf("最後\t\t%p\n",&str);//変更されないよー
    return 0;
}

hennmi1

Re:ポインタの参照渡し

#6

投稿記事 by hennmi1 » 15年前

>Libra さん
回答ありがとうございます。

>> char型の変数を参照渡しした時、
>> 参照渡しした関数で変更できるのは、char型の変数です。
ということは、参照渡しされた関数での
"get(char *str)" の "*str" はアドレスではなく値ということですか?

ポインタのポインタではなくても、参照渡し?ができているソースを以下に記載します。
=============================================================
int main(void) {
    int len;
    char str;

    len = get(&str);
    out(&str, len);
}

int get(char *str) {
    int i;
    for (i = 0; i < 2; i++) {
        *(str + i) = 'a';
    }
    return 2;
}

void out(char *str, int len) {
    int i;

    for (i = 0; i < len; i++) {
        printf("%c\n", *(str + i));
    }
}
=============================================================
これで、ちゃんと表示はされるのですが
Run-Time Check Failure #2 - Stack around the variable 'str' was corrupted
というエラーがでます。

そこで、malloc を追加し(最初の投稿ソースコード) 動かしてみたところ
表示が正しくないという問題が。。。

ポインタのポインタを使用するべきなんでしょうか。。

box

Re:ポインタの参照渡し

#7

投稿記事 by box » 15年前

>     char str;

1バイトしか確保していない領域に対して、

>     for (i = 0; i < 2; i++) {
>         *(str + i) = 'a';
>     }

こんなことをしてはいけません。

Libra

Re:ポインタの参照渡し

#8

投稿記事 by Libra » 15年前

>>これで、ちゃんと表示はされるのですが

hennmi1さんの↑のコードだと、(mallocで領域を確保していないのもありますが)

*(str + i) = 'a'; この部分で確保した領域外を参照している事になります。
結果が正しく表示されているように見えるのは、たまたま値が残っていただけです。


添付したソースを実行して、mallocで確保した領域が反映されていない事を確認してください。
※添付したソースはNo:45894のソースのアドレス変更部をmallocで確保したアドレスに変更したものです 画像

hennmi

Re:ポインタの参照渡し

#9

投稿記事 by hennmi » 15年前

>Libraさん
回答ありがとうございます。
確認させていただきました。

その後何時間かソースコードを色々いじってみたのですが
自分の思った通りにいきません。。。

「main関数で宣言した変数を関数に参照渡しし、その関数内で文字列を設定した後
main関数で設定した文字列が見れる」という関数が作成したいのです。
(文字ではなく文字列です)

贅沢な注文なのですが
お手本でいいので、そのような関数を一つ見せていただけないでしょうか。。。
どうか。。お願いします。

box

Re:ポインタの参照渡し

#10

投稿記事 by box » 15年前

「main関数で宣言した変数を関数に参照渡しし、その関数内で文字列を設定した後 
main関数で設定した文字列が見れる」という関数が作成したい

という題意を満たすためにポインタの参照渡しっていうのが必要なのかどうか
よくわかりません。
こんなのでいいんじゃないでしょうか。


#include <stdio.h>

void f(char *s, int sz)
{
    int i;

    for (i = 0; i < sz - 1; ++i) {
        s = 'a' + i;
    }
    s[sz-1] = '\0';
}

int main(void)
{
    char str[10];

    f(str, 10);
    printf("%s\n", str);
    return 0;
}


【実行結果】
abcdefghi
画像

hennmi1

Re:ポインタの参照渡し

#11

投稿記事 by hennmi1 » 15年前

>boxさん
回答ありがとうございます。
が、ごめんなさい。提示した説明が悪かったです。

たとえば、こんな機能がほしいとします。
「ユーザーが文字列を入力し、"end" という文字列がでるまで入力を続ける。
"end" という文字列が入力されたら、今まで入力された文字列を全て表示する」

そこに、先ほどの提示した説明の
「main関数で宣言した変数を関数に参照渡しし、その関数内で文字列を設定した後
main関数で設定した文字列が見れる」

を加える感じです。。。
(ちなみに、"end"が ~ というのは悪魔で例です)

たびたびすみません。
自分も色々探してみているのですが、もう頭がパンクしてなにがなんやら。。。
お手数ですがどうか。。。

hennmi1

Re:ポインタの参照渡し

#12

投稿記事 by hennmi1 » 15年前

連続投稿すみません。

>>s = 'a' + i;

上記では文字を設定していますが、文字列を設定したいのです。
s = "aaaa" のような (ていうかこれできるのか・・・?)

ごめんなさい
提示した説明が悪かったので、指摘をしているわけではないです。。。

qwerty

Re:ポインタの参照渡し

#13

投稿記事 by qwerty » 15年前

言語はCって書いてあるけど、Cに参照なんていう概念はあるんですかね?

それとboxさんもおっしゃっていますが、hennmi1さんが満足するような結果を得るのに、
わざわざ参照を利用する必要はないと思います。

文字列を複数扱いたいのなら、2次元配列か、構造体を用意するかでしょうね。

box

Re:ポインタの参照渡し

#14

投稿記事 by box » 15年前

参考になるかどうかは、全くわかりません。


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int f(char ***p)
{
    char s[1000];
    int n;

    *p = (char **) malloc(sizeof(char *));
    if (*p == NULL) {
        fprintf(stderr, "memory allocation error 1");
        exit(1);
    }

    for (n = 0; ; ++n) {
        printf("文字列を入力(endで終了)> ");
        fgets(s, sizeof(s), stdin);
        if (s[strlen(s)-1] == '\n') {
            s[strlen(s)-1] = '\0';
        }
        if (strcmp(s, "end") == 0) {
            break;
        }
        (*p)[n] = (char *) malloc(sizeof(char) * (strlen(s) + 1));
        if ((*p)[n] == NULL) {
            fprintf(stderr, "memory allocation error 2");
            exit(1);
        }
        strcpy((*p)[n], s);
    }
    return n;
}

int main(void)
{
    char **p;
    int n, i;

    n = f(&p);
    for (i = 0; i < n; ++i) {
        printf("%s\n", p);
        free(p);
    }
    free(p);
    return 0;
}
画像

Ma

Re:ポインタの参照渡し

#15

投稿記事 by Ma » 15年前

>boxさん

>ポインタのポインタではなくても、参照渡し?ができている
ということから、ポインタのポインタを使用しないで、またはポインタのポインタのポインタも使用しない。
というのが前提になっているので、求められる回答になっていないかもしれません。

なら、おまえがやれよ と言われちゃいますがw

さきほど自分でできるかぎりやってみようとしたんですが、一重?のポインタだけではどうもエラーがでてきて
どうも私の力ではできませんでしたorz

というか、ポインタのポインタじゃないとできない、というのが答えなのでは・・・?
#include <stdio.h>
#include <stdlib.h>

void set(char* heap){
    heap = (char *)malloc(sizeof(char) * 10);
    if (heap == NULL) exit(0);


    for (int i = 0;i < 10;i++) {
        heap = 'a' + i;
    }
}

int main()
{
    char *heap = NULL;

    set(heap);

    printf("%c\n",heap[5]);

    free(heap);

    return 0;
}

当然ながら?、こんな方法ではアクセス違反になりました。
画像

フリオ

Re:ポインタの参照渡し

#16

投稿記事 by フリオ » 15年前

 
>ポインタのポインタではなくても、参照渡し?ができているソースを以下に記載します。

 ひょっとして、質問者は、

 Cで一般にいう"参照渡し"とは、ポインタを渡すことで、
ポインタのポインタを渡すのは"参照渡し"ではない。

もしくは、

とにかく、ポインタさえ渡せば"参照渡し"である。

と思っているのでは?
 
画像

たいちう

Re:ポインタの参照渡し

#17

投稿記事 by たいちう » 15年前

引数についての例え話をします。
例え話なので当然現実とは違いますが、
今回の現象の理解には十分ではないかと。


AさんがBさんに仕事を頼むときにFAXでデータを渡します。
Bさんは受け取ったFAX用紙のデータを書き換えても良いですが、
Aさんの持つオリジナルのデータは影響を受けません。

これは関数が引数の値を変更しても呼び出し元では変わってないことを表しています。
C++の「参照」やVBのByValの付いた引数の場合は、
FAXではなく物質転送装置として動作しますので、
Bさんがデータを書き換えると、Aさんのオリジナルも変更されます。

C言語には物質転送装置に該当する機能がありません。
FAXなので必ずコピーがBさんに届きます。
オリジナルのデータを書き換えたい場合、
データの保管してある棚の番号をFAXします。
Bさんは受け取ったFAXに書かれた棚から書類を取り出し、
データを書き換えます。Bさんから完了報告を受けたAさんは、
その棚から書き換えられたデータを手にすることができます。

これがC言語の参照渡しですので、実際はあくまでもコピーを渡しているだけです。
書き換えたいデータがchar型ならば、char型へのポインタをコピーして渡します。
書き換えたいデータがchar型へのポインタならば、char型へのポインタのポインタを
コピーして渡します。

例えに戻ると、char型のデータのある棚の番号がどこかの棚に保管されていて、
それをFAXすることになります。


「ポインタで渡すのが参照渡し」や「ポインタを渡したら書き換えられる」等と
思っていたならば、理解を深めて下さい。

閉鎖

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