どうにも動的確保について間違っている気がするのでお尋ねいたします。
よくメモリを動的に確保する場合に私は次のようなプログラムを書きます
ポインタを用意する(例えばint *p)
mallocでメモリ確保
ポインタを関数に渡す(Func(p))
関数側でreallocし、値を代入する(p=(int *)realloc(p,sizeof(int)*num))
関数呼び出し側で、その値を使う
しかし、この方法を使うとどうにも関数を呼び出した際のポインタ(p)と、reallocした後のポインタ(p)の値が違うことがあり、値が不定になることがあります。
(reallocのメモリの確保の仕方のせいでしょうか)
この使い方は恐らくどこか間違っていると思うんですが、いまいち納得のいく解決策が思いつきません。
例えばポインタを引数ではなく戻り値として得ればできますが、2つ以上のポインタについてはできません。
何卒ご教授のほどをよろしくお願いいたします。
No.1ベストアンサー
- 回答日時:
まず、p=(int *)realloc(p,sizeof(int)*num);
で確保された領域が、それ以前の p と同じであることは保障されていません。
realloc() で保障されているのは、「新旧の領域の、短い長さまでの内容は同じである」ということだけです。
この場合、ポインタの値自体は、値渡しになりますから、例えば、
void func(int **ptr);
int *p = malloc();
// ただし、関数内部で realloc() する前提なら、
// 呼び出し前に、確保する必要性はないかも
func(&p);
....
void func(int **ptr)
{
*ptr = realoc();
...
}
でいけたような気がします(未チェック)
あと、最近は、
(p=(int *)realloc(p,sizeof(int)*num))
と alloc() 系の関数の返値をキャストする必要はありません。
大昔は、alloc() 系は char * を返していましたが、今は、 void * を返すので、これは、キャストなしで任意のポインタに安全に変換できます。
この回答への補足
ありがとうございます。
なるほど、**ptrを使うことで関数呼び出し側にも反映されるんですね。
ただ、テストプログラムを作ってみると、何やらまた動作がおかしいみたいです
おかしい部分を抜き出したソースは次のとおりです
int main()
{
int **p;
int i;
p = (int **)malloc(sizeof(int *));
*p = (int *)malloc(sizeof(int));
for(i=1;i<10;i++){
*p = (int *)realloc(*p,sizeof(int)*(i+1));
*p[i] = i;
}
free(*p);
return 0;
}
このようにするとreallocがメモリ領域を拡張してくれなく(?)、*p[i] = i;の部分でエラー終了します。
主題が変わって(?)しまって恐縮ですが、まだ何かおかしいのでしょうか。
もし宜しければお願いいたします。
No.5
- 回答日時:
> ポインタを用意する(例えばint *p)
> mallocでメモリ確保
> ポインタを関数に渡す(Func(p))
> 関数側でreallocし、値を代入する(p=(int *)realloc(p,sizeof(int)*num))
> 関数呼び出し側で、その値を使う
この場合、Func内部で常にメモリを解放するか、返却値等でpの値を返す必要がありますね。
> しかし、この方法を使うとどうにも関数を呼び出した際のポインタ(p)と、reallocした後のポインタ(p)の値が違うことがあり、値が不定になることがあります。
reallocでは、内部で新しいメモリブロックを割り付けることがありますので、返却値が元のポインタと同じ値になるとは限りません。通常、メモリブロックのサイズを広げるときには、ポインタの値が変わります。
なお、reallocで再割り付けを行うときは次にようにします。
int *t = realloc(p, size);
if (t != NULL)
p = t;
else
/* エラー処理 */
こうしないと、reallocが割り付けに失敗したとき、元のポインタの値が失われ、結果としてメモリリークにつながります。
また、C言語ではmallocやreallocの返却値はキャスト不要ですが、C++ではキャストが必須ですので、必要に応じて読み替えてください。ちなみに、C++ではreallocを使うよりstd::vectorなどを使う方が便利です。
> 例えばポインタを引数ではなく戻り値として得ればできますが、2つ以上のポインタについてはできません。
複数を扱う場合は、返却値にするより、結果を格納する配列を引数として渡した方が便利です。というか、元の値を配列で渡して、変化後の値を同じ配列に入れて返してあげればよいのです。
No.4
- 回答日時:
#3です。
サンプルコードのコメントが
間違っていたので訂正します。
//pはallocの『中身』
↓
//pはallocのコピー
もう一つ。
//pはallocへのポインタ
↓
//pはallocへのポインタのコピー
です。
参照渡しとか言われますが、厳密には、
C言語では、ポインタを値渡しします。
No.3
- 回答日時:
>ポインタを関数に渡す(Func(p))
提示されたコードはやりがちですが、
非常に危険で間違ったプログラムです。
>2つ以上のポインタについてはできません。
これは
int** sub( void );
のようなことを言っているのでしょうか?
であれば可能ですが今回はあまり触れません。
以下は危険なコードサンプル
#include <stdio.h>
#include <stdlib.h>
int sub( int* p );
int main( int argc, char* argv[] )
{
int ret;
int *alloc;
//allocをわざと初期化していません。
ret = sub( alloc );
if( ret ){
return -1;
}
//allocの中身は変わっていない為、不定
printf( "alloc = %#x\n", alloc );
//不定領域を開放
free( alloc );
return 0;
}
int sub( int* p )
{
//pはallocの『中身』
//pに確保領域のアドレスが入る
p = (int*)malloc( sizeof(int)*10 );
if( !p ){
return -1;
}
printf( "p = %#x\n", p );
//関数終了と共にpが破棄される
return 0;
}
上記のように引数に対して戻り値を設定する場合、
ポインタを渡さなければなりません。
上記のコードを修正するならば、
以下のようになります。
#include <stdio.h>
#include <stdlib.h>
int sub( int** p );
int main( int argc, char* argv[] )
{
int ret;
int *alloc;
//allocへのポインタを渡す
ret = sub( &alloc );
if( ret ){
return -1;
}
//allocの中身は確保された領域へのポインタ
printf( "alloc = %#x\n", alloc );
//正しい場所を開放
free( alloc );
return 0;
}
int sub( int** p )
{
//pはallocへのポインタ
//allocへのポインタの中身に確保領域のアドレスが入る
*p = (int*)malloc( sizeof(int)*10 );
if( !p ){
return -1;
}
printf( "p = %#x\n", *p );
//関数終了と共にpが破棄される
return 0;
}
上記のコードはインデントを全角スペースにしているので、
コンパイルする時は半角に置換してください。
ありがとうございます。
参考になりました。
2つというのは語弊がありました。
2つ以上のポインタについて、reallocしたい場合と言う意味です
No.2
- 回答日時:
メイン側でmallocしたのに、関数側でもreallocするのですか?
それなら、メイン側ではポインタだけ提示して、関数側でmallocすればよいのではないでしょうか?
C言語から離れて10余年なので、間違っているかもしれませんが(笑)
main(){
int *p;
// ポインタを関数に渡す(ポインタにmallocされたアドレスが欲しいのでポインタのポインタを渡す)
Func(&p);
}
Func(int **p){
*p = malloc(~);
}
メイン側でしていたのは、Func(p)の場合だとしていない場合エラーしてしまうためでした。
&p、**pを使うところまでは分かりました。
あとはrealloc・・・
別件で再質問することにいたします。
ありがとうございました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・14歳の自分に衝撃の事実を告げてください
- ・架空の映画のネタバレレビュー
- ・「お昼の放送」の思い出
- ・昨日見た夢を教えて下さい
- ・ちょっと先の未来クイズ第4問
- ・【大喜利】【投稿~10/21(月)】買ったばかりの自転車を分解してひと言
- ・メモのコツを教えてください!
- ・CDの保有枚数を教えてください
- ・ホテルを選ぶとき、これだけは譲れない条件TOP3は?
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・【コナン30周年】嘘でしょ!?と思った○○周年を教えて【ハルヒ20周年】
- ・ハマっている「お菓子」を教えて!
- ・最近、いつ泣きましたか?
- ・夏が終わったと感じる瞬間って、どんな時?
- ・10秒目をつむったら…
- ・人生のプチ美学を教えてください!!
- ・あなたの習慣について教えてください!!
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
セグメントエラー
-
C言語のポインタに直接アドレス...
-
init関数の意味
-
Run-Time Check Failure #3とい...
-
Wordでのリンク方法(Ctrlキー...
-
LPSTR型の初期化について
-
C言語で構造体の参照渡しができ...
-
popenした子プロセスのプロセス...
-
fopne で失敗する原因
-
基本アルゴリズムの『返す』の...
-
ポインタ同士の割り算
-
main関数以外での文字列の表示...
-
自作DLLの引数について、ポイン...
-
C言語でのconstを返す関数
-
参照型で受け取った引数をポイ...
-
x64プログラムでアドレスが32bi...
-
#define NULL ((void *)0) の弊害
-
デバイスハンドルとは?
-
C言語について教えてください。
-
VB6でポインタ?
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C言語のポインタに直接アドレス...
-
セグメントエラー
-
init関数の意味
-
fopne で失敗する原因
-
Run-Time Check Failure #3とい...
-
戻り値で構造体を返すことは可...
-
ExcelVBAでのkernel32(64bit)
-
LPSTR型の初期化について
-
参照型で受け取った引数をポイ...
-
C言語でのconstを返す関数
-
ハンドルはポインタか
-
ハンドル、アドレス、ポインタ...
-
PASCALとFARの意味
-
CWnd::EnableWindow()の扱い方
-
ポインタについて
-
デバイスハンドルとは?
-
C言語の文字列?処理 strcpyやl...
-
ポインタのミスでOS壊れるの...
-
C++ vectorのbeginについて
-
基本アルゴリズムの『返す』の...
おすすめ情報