次のようなコードがいくつかの異なる関数に含まれています。
_void someFunction (int *data) {
data = (int *) malloc (sizeof (data));
}
void useData (int *data) {
printf ("%p", data);
}
int main () {
int *data = NULL;
someFunction (data);
useData (data);
return 0;
}
_
someFunction ()
およびuseData ()
は、個別のモジュール(* .cファイル)で定義されています。
問題は、mallocが正常に動作し、割り当てられたメモリがsomeFunction
で使用可能である一方で、関数が返されると同じメモリを使用できないことです。
プログラムの実行例は、さまざまなメモリアドレスを示す出力とともに here で確認できます。
誰かが私にここで何が間違っているのか、そしてこのコードを機能させるにはどうしたらいいですか
編集:これを行うにはダブルポインタを使用する必要があるようです-実際に使用する必要があるときにどうすれば同じことを実行できますかダブルポインタ?したがって、たとえばデータは
_int **data = NULL; //used for 2D array
_
次に、関数呼び出しでトリプルポインターを使用する必要がありますか?
ポインターツーポインターを使用する場合:
_void someFunction (int **data) {
*data = malloc (sizeof (int));
}
void useData (int *data) {
printf ("%p", data);
}
int main () {
int *data = NULL;
someFunction (&data);
useData (data);
return 0;
}
_
どうして?さて、メイン関数のdata
を変更したいとします。 Cでは、パラメーターとして渡されたものを変更する場合(およびその変更を呼び出し元のバージョンに表示させる場合)、変更するものへのポインターを渡す必要があります。この場合、「変更したいもの」はポインターです。そのポインターを変更できるようにするには、ポインターツーポインターを使用する必要があります...
主な問題に加えて、コードに別のバグがあったことに注意してください:sizeof(data)
は、ポインターを格納するために必要なバイト数を示します(32ビットOSでは4バイト、64ビットでは8バイト) -bit OS)、ただし、ポインタが指すものを保存するために必要なバイト数が本当に必要な場合(int
、つまりほとんどのOSでは4バイト) 。通常はsizeof(int *)>=sizeof(int)
であるため、問題が発生することはおそらくありませんが、注意する必要があります。上記のコードでこれを修正しました。
ポインターからポインターへのいくつかの有用な質問はここにあります:
特にJavaをC/C++に移動した場合の一般的な落とし穴
ポインタを渡すときは、値渡しです。つまり、ポインタの値がコピーされます。ポインタが指すデータに変更を加えるのに適していますが、ポインタ自体への変更はコピーであるため、ローカルです。
トリックは、ポインターを変更したいので、参照でポインターを渡すことです。つまり、mallocなどです。
**ポインタ->初心者Cプログラマを怖がらせます;)
ポインターを変更する場合は、ポインターをポインターに渡す必要があります。
すなわち。 :
void someFunction (int **data) {
*data = malloc (sizeof (int)*ARRAY_SIZE);
}
編集:ARRAY_SIZEを追加しました。ある時点で、割り当てたい整数の数を知る必要があります。
これは、ポインタデータが値によってsomeFunction
に渡されるためです。
int *data = NULL;
//data is passed by value here.
someFunction (data);
//the memory allocated inside someFunction is not available.
ポインターへのポインターまたは割り当てられたポインターを返すと、問題が解決します。
void someFunction (int **data) {
*data = (int *) malloc (sizeof (data));
}
int* someFunction (int *data) {
data = (int *) malloc (sizeof (data));
return data;
}
someFunction()は、パラメーターをint *として受け取ります。つまり、main()から呼び出すと、渡した値のコピーが作成されます。関数内で変更しているのはこのコピーなので、変更は外部に反映されません。他の人が示唆したように、int **を使用して変更をデータに反映させることができます。それを行う別の方法は、someFunction()からint *を返すことです。
ダブルポインター技術の使用とは別に、1つだけの戻りパラメーターがある場合、必要な書き換えは次のとおりです。
int *someFunction () {
return (int *) malloc (sizeof (int *));
}
そしてそれを使う:
int *data = someFunction ();
次に、関数にメモリを割り当て、パラメータを介してポインタを返す一般的なパターンを示します。
_void myAllocator (T **p, size_t count)
{
*p = malloc(sizeof **p * count);
}
...
void foo(void)
{
T *p = NULL;
myAllocator(&p, 100);
...
}
_
別の方法は、ポインターを関数の戻り値にすることです(私の推奨メソッド):
_T *myAllocator (size_t count)
{
T *p = malloc(sizeof *p * count);
return p;
}
...
void foo(void)
{
T *p = myAllocator(100);
...
}
_
メモリ管理に関する注意事項:
sizeof (T)
の代わりに_sizeof *p
_)。これにより、データ型を変更する必要がある場合(たとえば、intからlongまたはfloatからdoubleに変更する場合)の胸焼けを節約できます。また、コードがIMOを少し読みやすくなります。ダブルポインターを使用するのではなく、新しいポインターを割り当てて返すだけで済みます。ダブルポインターを渡す必要はありません。関数のどこでも使用されていないからです。
void *
を返すので、あらゆるタイプの割り当てに使用できます。
void *someFunction (size_t size) {
return malloc (size);
}
そしてそれを次のように使用します:
int *data = someFunction (sizeof(int));
ここで、ポインタを変更しようとしています。つまり、 "data == Null"から "data == 0xabcd"に割り当てた他のメモリに変更しています。したがって、必要なデータを変更するには、データのアドレス、つまり&dataを渡します。
void someFunction (int **data) {
*data = (int *) malloc (sizeof (int));
}
編集した追加の質問に返信する:
「*」は、何かへのポインタを示します。したがって、「**」は何かへのポインタへのポインタ、「***」は何かへのポインタへのポインタなどになります。
「データが関数パラメータでない場合」の「int **データ」の通常の解釈は、int配列のリストへのポインタです(たとえば、「int a [100] [100]」)。
したがって、最初にint配列を割り当てる必要があります(簡単にするために、malloc()への直接呼び出しを使用しています)。
data = (int**) malloc(arrayCount); //allocate a list of int pointers
for (int i = 0; i < arrayCount; i++) //assign a list of ints to each int pointer
data [i] = (int*) malloc(arrayElemCount);