電子書籍の厳選無料作品が豊富!

最近奇妙な現象?に遭遇しました。
理解している人には笑われるかもしれませんが・・・・
ソースを以下に示します。

#include <stdio.h>
void function(int *pointer);
void main(){
int sample=3;
int *pointer;

pointer=&sample;
function(pointer);
printf("%d\n", *pointer);
}

void function(int *pointer){
int temp=9;
pointer = &temp;
}
今上のソースとは別のプログラムを作成中で、上のような状態になっています。関数にポインタのアドレスを渡し、関数の中でアドレスを書き換えて表示を違うものにしようと考えました。
上の表示結果は3になります。なぜなのでしょう?
そして目的を達したいため、上のように関数内でのアドレスの書き換えが呼び出し側に反映されるようにすることができないでしょうか?
分かる方はよろしくお願いします。

A 回答 (10件)

まず、ポインタを書き換えたいなら、関数の引数はポインタのポインタにしないといけません。



それから、function内のローカル変数(temp)のポインタを返してはいけません。
あと、mainは、intを返すようにするほうがいいです。(少なくとも規格ではそういうことになっています。)

直すとしたらこんな感じか
#include <stdio.h>
void function(int **pointer);
int main(){
 int sample=3;
 int *pointer;

 pointer=&sample;
 function(&pointer);
 printf("%d\n", *pointer);
 return 0;
}

void function(int **pointer){
 static int temp=9;
 *pointer = &temp;
}
    • good
    • 0

だって、tempは関数functionの中だけで有効な変数やん。


関数の外に出たらtempって変数の存在自体なくなるやん。
値の有効な範囲・領域をちゃんと認識しましょね。
    • good
    • 0

#include <stdio.h>


void function(int *pointer);
void main()
{
int sample=3;
int *pointer;

//pointerはsampleへのアドレス
pointer=&sample;
printf("%d\n", *pointer);

//pointerはsampleへのアドレス
function(pointer);
printf("%d\n", *pointer);

//pointerはsampleへのアドレス
printf("%d\n", *pointer);

}

void function(int *pointer)
{
//pointerはmainのpointerのコピーつまりsampleへのポインタ
int temp=9;

//pointerはtempへのポインタ
pointer = &temp;
printf("%d\n", *pointer);

//tempが破棄される
//pointerのコピーが破棄される
}

やるなら


#include <stdio.h>
void function(int *pointer);
void main()
{
int sample=3;
int *pointer;

//pointerはsampleへのアドレス
pointer=&sample;
printf("%d\n", *pointer);

//pointerはsampleへのアドレス
function(pointer);
printf("%d\n", *pointer);

//pointerはsampleへのアドレス
printf("%d\n", *pointer);

}

void function(int *pointer)
{
//pointerはmainのpointerのコピーつまりsampleへのポインタ
int temp=9;

//pointerの中身つまりsampleへtempの値を代入
//ここでMainのsampleが9へ
*pointer = temp;
printf("%d\n", *pointer);

//tempが破棄される
//pointerのコピーが破棄される
}

厳密にはC言語ではポインタを値渡しします。
    • good
    • 0

 


> このような、間違いをするのは、ANSIの記法の弱点ではないかと思います

 教え方の問題かも。
例えば、

 関数の引数には、'値渡し' と '参照渡し(ポインタ渡し)' があり、
'値渡し' は、呼び出した関数で定義された変数の値がコピーされて渡されるので、
元の変数は影響を受けないが、
'参照渡し(ポインタ渡し)' は、呼び出された関数で元の値に直接アクセスして、
その値を書き換えることができる。

こんな風に教わると、今回のような場合は、
"ポインタが渡されているので、元("main"関数内の"pointer")の値が書き換えられる。"
と思ってしまう人も出てくるんじゃないだろうか。
 
    • good
    • 0

このような、間違いをするのは、ANSIの記法の弱点ではないかと思います(私見ですが)



function(int *pointer)
{
}

は、古い記法では

function (pointer)
int *pointer;
{
}

となります。
この記法だと、funcのpointerの領域が、func側に確保されることがなんとなく理解でき、
int main()
{
int *pp;
func(pp)
}
とすれば、
定義側と呼び出し側の引数の括弧内が同じ書き方になり
pp->pointer と、一致する、このことで、
pp の値がfuncのpointerにコピーされるのであり、ppの領域は全く影響されず値は基のままだと理解できるかなと思えます。

ANSIやC++の記法には、プログラムを、却って難解にするような面があるような気がします(私見ですが)。
    • good
    • 0

 


 "temp"をつかってなかった。

void func(int **pointer)
{
int *temp = malloc(sizeof(int));

*temp = 9;
*pointer = temp;
return;
}
 
    • good
    • 0

 


 別解

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

void func(int **pointer)
{
*pointer = malloc(sizeof(int));
**pointer = 9;
return;
}

int main(void)
{
int sample = 3;
int *pointer = &sample;

printf("%d\n", *pointer);
func(&pointer);
printf("%d\n", *pointer);
free(pointer);
return 0;
}
 
    • good
    • 0

 


> これ、ダメです。すごく危険。

 この場合は、関数を抜けたときに"temp"といっしょに"pointer"も
開放されるので、何の危険もない。ただし、意味もない。


> たまたまだと思います。

 たまたまじゃない。
"func"関数での処理結果は、"main"に渡されていないので、
元の値が表示されているだけ。


void function(int **pointer){
int temp=9;
*pointer = &temp;
return;
}

 これは、危険。
 
    • good
    • 0

void function(int *pointer){


int temp=9;
pointer = &temp;
}

これ、ダメです。すごく危険。
なぜかというと、関数内で宣言された変数はスタックに確保されるからです。だから、関数を抜けたら変数 temp は解放されてしまって、他人の手に渡ってしまい、temp があったアドレスに何が書き込まれるか保証できないのです。

> 上の表示結果は3になります。なぜなのでしょう?

たまたまだと思います。きっと debugモードとreleaseモードで値が違ったり、環境が違ったりしたら値が3でなくなるものと思います。なにせ、スタックに確保されたメモリが解放されているのですから。

> 関数にポインタのアドレスを渡し、関数の中でアドレスを書き換えて

ポインタの概念や言葉の意味を正しく理解していないせいで、このような表現になったものと思います。「ポインタのアドレス」というところが混乱してしまっています。例えば、

int *pointer; // ←これポインタ
&pointer   // ←これポインタのアドレス
int temp=9;
pointer = &temp; // ←この場合は、ポインタ「に」アドレスを渡す

です。もっとも、やっちゃダメな例ですが。

> 関数内でのアドレスの書き換えが呼び出し側に反映される
> ようにすることができないでしょうか?

関数内でポインタが指すアドレスを変更したい、ということですよね?
その場合、変更後にポインタが指すアドレスの領域が、関数を抜けたあともちゃんと確保されているところを指してあげましょう。

ローカル変数や関数の引数はスタック領域から確保される。
malloc, calloc, newなどはヒープ領域から確保される。
基本的なことですが教科書に書いてなかったりするので覚えておきましょう。

> 目的を達したいため、

もうちょっと具体的に何をしたいのか書いてもらえれば、どうやってメモリを確保して、ポインタ付け替えればいいか考えてみますよ。
    • good
    • 0

>上の表示結果は3になります。

なぜなのでしょう?

わかり難いので function の方の引数を
void function(int* pt) {
 int temp = 9;
 pt = &temp;
}

として、pt には main の中で宣言されている pointer の値(アドレス値)がコピーされて格納されます。

function の中で pt = &temp とその「値」を書き換えても main の pointer のアドレス値に影響はありません。

function の中で *pt = 9 などと、pt の指す先を変更すれば、pt の指す先は pointer の指す先と同じ( sample )であるため値が function の外に伝播されます。

# たまには長々と回答してみる。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!