68

したがって、構造体のリストに構造体を追加するための、次のようなコードがあります。

void barPush(BarList * list,Bar * bar)
{
    // if there is no move to add, then we are done
    if (bar == NULL) return;//EMPTY_LIST;

    // allocate space for the new node
    BarList * newNode = malloc(sizeof(BarList));

    // assign the right values
    newNode->val = bar;
    newNode->nextBar = list;

    // and set list to be equal to the new head of the list
    list = newNode; // This line works, but list only changes inside of this function
}

これらの構造は次のように定義されています。

typedef struct Bar
{
    // this isn't too important
} Bar;

#define EMPTY_LIST NULL

typedef struct BarList
{
    Bar * val;
    struct  BarList * nextBar;
} BarList;

次に、別のファイルで次のようなことを行います。

BarList * l;

l = EMPTY_LIST;
barPush(l,&b1); // b1 and b2 are just Bar's
barPush(l,&b2);

ただし、この後も、lはbarPush内で作成された変更バージョンではなく、EMPTY_LISTを指します。リストを変更したい場合は、ポインターをポインターとして渡す必要がありますか、それとも他の暗い呪文が必要ですか?

4

6 に答える 6

69

これを行う場合は、ポインターをポインターに渡す必要があります。

void barPush(BarList ** list,Bar * bar)
{
    if (list == NULL) return; // need to pass in the pointer to your pointer to your list.

    // if there is no move to add, then we are done
    if (bar == NULL) return;

    // allocate space for the new node
    BarList * newNode = malloc(sizeof(BarList));

    // assign the right values
    newNode->val = bar;
    newNode->nextBar = *list;

    // and set the contents of the pointer to the pointer to the head of the list 
    // (ie: the pointer the the head of the list) to the new node.
    *list = newNode; 
}

次に、次のように使用します。

BarList * l;

l = EMPTY_LIST;
barPush(&l,&b1); // b1 and b2 are just Bar's
barPush(&l,&b2);

Jonathan Lefflerは、コメントでリストの新しい先頭を返すことを提案しました。

BarList *barPush(BarList *list,Bar *bar)
{
    // if there is no move to add, then we are done - return unmodified list.
    if (bar == NULL) return list;  

    // allocate space for the new node
    BarList * newNode = malloc(sizeof(BarList));

    // assign the right values
    newNode->val = bar;
    newNode->nextBar = list;

    // return the new head of the list.
    return newNode; 
}

使用法は次のようになります。

BarList * l;

l = EMPTY_LIST;
l = barPush(l,&b1); // b1 and b2 are just Bar's
l = barPush(l,&b2);
于 2009-04-20T04:35:11.040 に答える
24

一般的な答え:変更したいものへのポインターを渡します。

この場合、変更するポインターへのポインターになります。

于 2009-04-21T00:43:59.043 に答える
17

Cでは、すべてが値によって渡されることを忘れないでください。

このように、ポインタをポインタに渡します

int myFunction(int** param1, int** param2) {

// now I can change the ACTUAL pointer - kind of like passing a pointer by reference 

}
于 2009-04-20T04:33:38.030 に答える
3

これは古典的な問題です。割り当てられたノードを返すか、ポインターのポインターを使用します。Cでは、XへのポインターをXを変更する関数に渡す必要があります。この場合、ポインタを変更したいので、ポインタをポインタに渡す必要があります。

于 2009-04-20T04:35:49.523 に答える
2

はい、ポインタをポインタに渡す必要があります。Cは、参照ではなく値で引数を渡します。

于 2009-04-20T04:33:27.890 に答える
2

別の関数でポインターを変更するには、複数の間接参照と呼ばれる概念が必要です。これについては後で説明します。@geofftnzを指定したスポイラーソリューションは複数の間接参照を使用します。私がやろうとしているのは、Cでの複数の間接参照を説明するために最善を尽くすことです。

次の2つのプログラムについて考えてみましょう。コードについて説明します。

次のプログラムは複数の間接参照を使用していないため、失敗します。

エラーのあるプログラム:

// filename: noIndirection.c
#include <stdio.h>
#include <stdlib.h>

void allocater(int *ptrTempAllctr)
{
    ptrTempAllctr = malloc(sizeof(int));
    if (ptrTempAllctr == NULL) {
        perror("in allocater() memory allocation error");
        exit(EXIT_FAILURE);
    }
}

int main() 
{
    int *ptrMain = NULL;
    allocater(ptrMain);
    if (ptrMain == NULL) {
        printf("ptrMain is points to NULL\n");
        return 1;
    }
    //free(ptrMain);  // we don't have to free because it will be invalid free.
    return 0;
}

上記のprogram()について考えてみましょう。このプログラムには、intを指すポインターでnoIndirection.cある変数があります。ptrMain関数に渡された場合、関数の引数は一時変数であるため、関数scope(body)に一時ポインター変数が作成され、スコープ外になると削除されます。

一時ポインター変数(引数)は、関数に引数として渡されたときにptrTempAllctr呼び出し元(main)関数の変数ptrMain(を指している)が指しているものを指します。NULL

malloc()一時変数に別のポインターを使用または割り当てるptrTempAllctrと、それがポイントさmainれますが、to関数に引数として渡されたcaller()関数のポインター変数は、以前にallocater()ポイントされたのと同じデータ( )をポイントします。NULL関数呼び出し。

呼び出された(allocater())関数がスコープ外になると、一時ポインタ変数がスタックからポップされ、メモリが未割り当てのままになり、メモリリークが発生します。この制限を回避するには、複数の間接参照を使用する必要があります。

複数の間接参照:

Multiple indirection when we use of pointer/s to pointer/s in varying level(with multiple `*`) eg: `int **pp, int ***ppp`, etc.

&そして、address-of( )演算子を使用してそれらを割り当てます。

複数の間接ポインター型変数が行うことは、上記のプログラムを修正するためのポインター変数自体へのポインターを作成できるようにすることです。 これにより、この呼び出しptrMainを使用してのアドレスを渡すことができますallocater()

allocater(&ptrMain);

したがって、上記のプログラムnoIndirection.cではこれを行うことができませんwithIndirection.c。この複数の間接参照を実装するプログラムを参照してください。

上記のバグのあるプログラム(noIndirection.c)を解決するには、この場合、関数のint **ptrMain関数引数としてint pointer()へのポインターが必要です。allocater()

これは、次のプログラムで使用されました。

次のプログラムは、複数の間接参照を使用して、前のプログラムのバグを解決します。

// filename: withIndirection.c
#include <stdio.h>
#include <stdlib.h>

void trueAllocater(int **ptrTrueAllocater)
{
    *ptrTrueAllocater = (int *) malloc(sizeof(int));
    if (ptrTrueAllocater == NULL) {
        perror("in trueAllocater() memory allocation error");
        exit(EXIT_FAILURE);
    }
}

int main(void) 
{
    int *ptrMain = NULL;
    trueAllocater(&ptrMain);
    if (ptrMain == NULL) {
        printf("memory not allocated\n");
        return EXIT_FAILURE;
    }

    printf("memory allocated and assigned to ptrMain");
    printf(" from trueAllocater\n");

    free(ptrMain);
    return EXIT_SUCCESS;
}

withIndirection.cこれからの参照のためにプログラムを参照してください。

この問題を解決するには、ポインタ変数ptrMaintrueAllocater(&ptrMain);)のアドレスをtrueAllocaterに渡すptrMain必要がありtrueAllocater()ます。これを行うには、関数が正しいレベルの間接ポインタを受け入れる必要があります。間接参照。これは、渡される変数の現在の理解に、引数宣言に追加された別の*を追加することです。

つまり、間接レベルが統計化されるように、ではなくinからのtrueAllocater()関数引数を持つ必要があります。int **int *withIndirection.cnoIndirection.c

呼び出し元の引数変数ptrMainのアドレスが関数に渡されたとき。関数内の一時ptrTrueAllocater引数変数は、(プログラム内にある)ポインター変数がfunction()内で指しているものではなくptrMain、caller(main)関数内のポインター変数のアドレスを指している。ptrMainNULLmain

変数を逆参照すると、一時変数がその内容ではなくcaller()変数自体を指しているため、指してptrTrueAllocaterいるアドレスptrMainが明らかになります。ptrTrueAllocatermainptrMain

逆参照された変数の内容はptrTrueAllocater、caller(main)のvariable(ptrMain)によってポイントされたデータのアドレスになるため、最終的なデータを取得するには、さらに1つの逆参照を実行する必要があります。

ptrMainしたがって、ポイントする必要のある場所を変更するために、ポイントするアドレスを取得するために1回逆参照する必要があります。また、であるptrMainによってポイントされる実際のデータを取得するために2回逆参照する必要があります。ptrMainNULL

@PaulWicksは変更するつもりだったので、ポインティングの場所を割り当てたり変更したりするには、一度参照を解除する必要があります。

ポインターを使用した複数の間接参照の目的は、多次元配列を作成し、何かを指す必要のあるポインター引数を渡すことです。

次のように操作する必要があるタイプに応じて変数を変更する必要があります。

宣言に*を追加するたびに、ポインターの間接レベルが増加し、逆参照するたびに、データに近づくポインターの間接レベルが減少します。

この問題は、必要なポインター変数を割り当てる呼び出し元関数にアドレスを返すことで解決できます。

はい、この多間接変数構文を使用して、1次元または多次元の配列を作成できます。これは初心者を最初は混乱させるでしょう、彼らがたくさんのコードを読むのに時間をかけるならば、彼らはそれらの間の違いを見つけることができるでしょう。

私が間違っている場合は訂正してください。フィードバックを提供し、複数の間接ポインターのその他の使用法を教えてください。私の悪い英語をお詫びします。これらは、複数の間接的な方法を理解するのに役立つリソースです。 https://boredzo.org/pointers/#function_pointers https://cseweb.ucsd.edu/~ricko/rt_lt.rule.html

于 2021-06-02T16:08:17.083 に答える