
どうにも動的確保について間違っている気がするのでお尋ねいたします。
よくメモリを動的に確保する場合に私は次のようなプログラムを書きます
ポインタを用意する(例えば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で質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# C言語初心者 ポインタについて、お助けください、、 2 2023/03/15 23:50
- C言語・C++・C# 関数ポインタの高速化のメリット 7 2023/05/05 20:15
- C言語・C++・C# ポインタの型変換、どうやるんでしたっけ? 2 2022/03/28 11:00
- C言語・C++・C# C言語プログラム変更 2 2022/12/21 15:03
- C言語・C++・C# C言語 ポインタ 配列 2 2022/06/02 17:29
- C言語・C++・C# 10個の実数に対する降順ソート結果を出力するプログラムを作りたいのですが、以下のプログラムをどう直せ 1 2022/07/09 22:16
- C言語・C++・C# leetcode 155 minstack 1 2022/05/07 16:43
- C言語・C++・C# C言語 配列とポインタについて 2 2022/06/02 11:53
- C言語・C++・C# C pointer? or... 2 2022/03/29 00:47
- C言語・C++・C# 10個の実数に対する降順ソート結果を出力するプログラムを作りたいのですが、写真のプログラムをどう直せ 2 2022/07/09 21:13
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
Run-Time Check Failure #3とい...
-
init関数の意味
-
C言語のポインタに直接アドレス...
-
コンストラクタでnewを失敗した...
-
可変長のリスト
-
高校1年です。情報技術基礎のシ...
-
CWnd::EnableWindow()の扱い方
-
単方向リストの解釈
-
C言語の文字列?処理 strcpyやl...
-
構造体とfscanf
-
NULLとブランクの違い
-
自作DLLの引数について、ポイン...
-
アプリを32bitから64bit移行
-
セグメントエラー
-
size_t
-
localtime() 関数についての質問
-
長さゼロの文字列の適切な名称
-
戻り値で構造体を返すことは可...
-
VBはCを混乱させる?
-
連結リスト 要素の入れ替え
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C言語のポインタに直接アドレス...
-
fopne で失敗する原因
-
戻り値で構造体を返すことは可...
-
LPSTR型の初期化について
-
Run-Time Check Failure #3とい...
-
ExcelVBAでのkernel32(64bit)
-
参照型で受け取った引数をポイ...
-
init関数の意味
-
セグメントエラー
-
アプリを32bitから64bit移行
-
ハンドルはポインタか
-
ハンドル、アドレス、ポインタ...
-
C言語でのconstを返す関数
-
C++で関数ポインタから関数名を...
-
パスからファイル名を抽出
-
ReadFileの読み込みエラーについて
-
#define NULL ((void *)0) の弊害
-
CImage GetBitsメソッドについて
-
ポインタ変数の疑問
-
Cで作成したDLL関数をVBから呼...
おすすめ情報