web-dev-qa-db-ja.com

二重の間接参照を使用するのはなぜですか?またはポインタにポインタを使用するのはなぜですか。

Cで二重の間接参照を使うべきなのはいつですか?誰もが例で説明できますか?

私が知っていることは、二重の間接指定はポインタへのポインタであるということです。ポインタへのポインタが必要なのはなぜですか?

225
manju

もしあなたが文字のリスト(Word)を持ちたいのなら、あなたはchar *Wordを使うことができます。

単語のリスト(文章)が必要な場合は、char **sentenceを使用できます。

文のリスト(独白)が必要な場合は、char ***monologueを使用できます。

モノローグのリスト(伝記)が必要な場合は、char ****biographyを使用できます。

あなたが略歴(バイオライブラリー)のリストが欲しいならば、あなたはchar *****biolibraryを使うことができます

バイオライブラリーのリスト(???)が必要な場合は、char ******lolを使用できます。

... ...

はい、私はこれらが最良のデータ構造ではないかもしれないことを知っています


非常に非常に退屈な使用例

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

int wordsinsentence(char **x) {
    int w = 0;
    while (*x) {
        w += 1;
        x++;
    }
    return w;
}

int wordsinmono(char ***x) {
    int w = 0;
    while (*x) {
        w += wordsinsentence(*x);
        x++;
    }
    return w;
}

int wordsinbio(char ****x) {
    int w = 0;
    while (*x) {
        w += wordsinmono(*x);
        x++;
    }
    return w;
}

int wordsinlib(char *****x) {
    int w = 0;
    while (*x) {
        w += wordsinbio(*x);
        x++;
    }
    return w;
}

int wordsinlol(char ******x) {
    int w = 0;
    while (*x) {
        w += wordsinlib(*x);
        x++;
    }
    return w;
}

int main(void) {
    char *Word;
    char **sentence;
    char ***monologue;
    char ****biography;
    char *****biolibrary;
    char ******lol;

    //fill data structure
    Word = malloc(4 * sizeof *Word); // assume it worked
    strcpy(Word, "foo");

    sentence = malloc(4 * sizeof *sentence); // assume it worked
    sentence[0] = Word;
    sentence[1] = Word;
    sentence[2] = Word;
    sentence[3] = NULL;

    monologue = malloc(4 * sizeof *monologue); // assume it worked
    monologue[0] = sentence;
    monologue[1] = sentence;
    monologue[2] = sentence;
    monologue[3] = NULL;

    biography = malloc(4 * sizeof *biography); // assume it worked
    biography[0] = monologue;
    biography[1] = monologue;
    biography[2] = monologue;
    biography[3] = NULL;

    biolibrary = malloc(4 * sizeof *biolibrary); // assume it worked
    biolibrary[0] = biography;
    biolibrary[1] = biography;
    biolibrary[2] = biography;
    biolibrary[3] = NULL;

    lol = malloc(4 * sizeof *lol); // assume it worked
    lol[0] = biolibrary;
    lol[1] = biolibrary;
    lol[2] = biolibrary;
    lol[3] = NULL;

    printf("total words in my lol: %d\n", wordsinlol(lol));

    free(lol);
    free(biolibrary);
    free(biography);
    free(monologue);
    free(sentence);
    free(Word);
}

出力:

私のLOL中の総単語数:243
440
pmg

その理由の1つは、関数の引数として関数に渡されるポインタの値を変更したい場合です。そのためには、ポインタへのポインタが必要です。

簡単に言うと、関数呼び出しの外側でもMemory-AllocationまたはAssignmentを保持(または変更を保持)する場合は**を使用してください。(したがって、このような関数はダブルポインタ引数で渡してください)

これはあまり良い例ではないかもしれませんが、基本的な使い方を説明します。

void allocate(int** p)
{
  *p = (int*)malloc(sizeof(int));
}

int main()
{
  int* p = NULL;
  allocate(&p);
  *p = 42;
  free(p);
}
160
Asha

これは簡単な答えです!!!!

  • その値がアドレスであることを示すポインタがあるとしましょう。
  • しかし今、あなたはそのアドレスを変更したいのです。
  • pointer1 = pointer2を実行すれば、pointer1はpointer2のアドレスになります。
  • しかし!関数にそれを実行させたい、そしてその関数が終了した後も結果を持続させたいのなら、余分な作業をする必要があります。ただpointer1を指すために新しいpointer3が必要で、その関数にpointer3を渡します。

  • これは楽しい例です(理解するために、最初に下記の出力を見てください)。

#include <stdio.h>

int main()
{

    int c = 1;
    int d = 2;
    int e = 3;
    int * a = &c;
    int * b = &d;
    int * f = &e;
    int ** pp = &a;  // pointer to pointer 'a'

    printf("\n a's value: %x \n", a);
    printf("\n b's value: %x \n", b);
    printf("\n f's value: %x \n", f);
    printf("\n can we change a?, lets see \n");
    printf("\n a = b \n");
    a = b;
    printf("\n a's value is now: %x, same as 'b'... it seems we can, but can we do it in a function? lets see... \n", a);
    printf("\n cant_change(a, f); \n");
    cant_change(a, f);
    printf("\n a's value is now: %x, Doh! same as 'b'...  that function tricked us. \n", a);

    printf("\n NOW! lets see if a pointer to a pointer solution can help us... remember that 'pp' point to 'a' \n");
     printf("\n change(pp, f); \n");
    change(pp, f);
    printf("\n a's value is now: %x, YEAH! same as 'f'...  that function ROCKS!!!. \n", a);
    return 0;
}

void cant_change(int * x, int * z){
    x = z;
    printf("\n ----> value of 'a' is: %x inside function, same as 'f', BUT will it be the same outside of this function? lets see\n", x);
}

void change(int ** x, int * z){
    *x = z;
    printf("\n ----> value of 'a' is: %x inside function, same as 'f', BUT will it be the same outside of this function? lets see\n", *x);
}
  • そして、これが出力です。
 a's value: bf94c204

 b's value: bf94c208 

 f's value: bf94c20c 

 can we change a?, lets see 

 a = b 

 a's value is now: bf94c208, same as 'b'... it seems we can, but can we do it in a function? lets see... 

 cant_change(a, f); 

 ----> value of 'a' is: bf94c20c inside function, same as 'f', BUT will it be the same outside of this function? lets see

 a's value is now: bf94c208, Doh! same as 'b'...  that function tricked us. 

 NOW! lets see if a pointer to a pointer solution can help us... remember that 'pp' point to 'a' 

 change(pp, f); 

 ----> value of 'a' is: bf94c20c inside function, same as 'f', BUT will it be the same outside of this function? lets see

 a's value is now: bf94c20c, YEAH! same as 'f'...  that function ROCKS!!!. 
74

Asha's responseに追加すると、以下の例への単一のポインタ(例:alloc1())を使用すると、関数内に割り当てられたメモリへの参照が失われます。

void alloc2(int** p) {
   *p = (int*)malloc(sizeof(int));
   **p = 10;
}

void alloc1(int* p) {
   p = (int*)malloc(sizeof(int));
   *p = 10;
}

int main(){
   int *p = NULL;
   alloc1(p);
   //printf("%d ",*p);//undefined
   alloc2(&p);
   printf("%d ",*p);//will print 10
   free(p);
   return 0;
}

このようになるのは、alloc1ではポインタが値によって渡されるためです。そのため、alloc1内でmalloc呼び出しの結果に再割り当てされた場合、その変更は異なるスコープ内のコードには関係しません。

42
Silviu

今日要約しているように、 このブログ記事 から非常に良い例を見ました。

リンクリスト内のノードの構造があるとします。

typedef struct node
{
    struct node * next;
    ....
} node;

remove_if関数を実装しましょう。これは削除基準rmを引数の1つとして受け入れ、リンクリストをたどります。エントリが条件を満たしている場合(rm(entry)==trueのようなもの)、そのノードはリストから削除されます。最後に、remove_ifはリンクリストの先頭(元の先頭と異なる場合があります)を返します。

書くことができます

for (node * prev = NULL, * curr = head; curr != NULL; )
{
    node * const next = curr->next;
    if (rm(curr))
    {
        if (prev)  // the node to be removed is not the head
            prev->next = next;
        else       // remove the head
            head = next;
        free(curr);
    }
    else
        prev = curr;
    curr = next;
}

あなたのforループとして。メッセージは、二重ポインタなしで、ポインタを再編成するためにprev変数を維持する必要があります。そして2つの異なるケースを処理します。

しかし二重ポインタで、あなたは実際に書くことができます

// now head is a double pointer
for (node** curr = head; *curr; )
{
    node * entry = *curr;
    if (rm(entry))
    {
        *curr = entry->next;
        free(entry);
    }
    else
        curr = &entry->next;
}

prev->nextが指しているものを直接変更できるので、今はprevは必要ありません。

物事を明確にするために、コードに少し従いましょう。削除中に:

  1. entry == *headの場合:*head (==*curr) = *head->nextになります - headは新しい見出しノードのポインタを指すようになりました。これを行うには、headのコンテンツを新しいポインタに直接変更します。
  2. entry != *head:同様に、*currprev->nextが指し示しているもので、今はentry->nextを指しています。

どちらの場合でも、ポインタを二重ポインタで統一的に再編成することができます。

21
ziyuang

1.基本コンセプト -

次のように宣言した場合: -

1. char * ch - (キャラクタポインタと呼ばれる)
- chは1文字のアドレスを含みます。
* ch)は文字の値を間接参照します。

---(2。char ** ch -
'ch'は文字ポインタの配列のアドレスを含みます。 (1のように)
'* ch'は単一文字のアドレスを含みます。 (宣言の違いにより、1とは異なることに注意してください)。
(** ch)は、文字の正確な値を参照します。

ポインタをさらに追加すると、文字から文字列、文字列の配列など、データ型の次元が拡張されます。1d、2d、3dの行列に関連付けることができます。

つまり、ポインタの使い方は宣言方法によって異なります。

これは簡単なコードです。

int main()
{
    char **p;
    p = (char **)malloc(100);
    p[0] = (char *)"Apple";      // or write *p, points to location of 'A'
    p[1] = (char *)"Banana";     // or write *(p+1), points to location of 'B'

    cout << *p << endl;          //Prints the first pointer location until it finds '\0'
    cout << **p << endl;         //Prints the exact character which is being pointed
    *p++;                        //Increments for the next string
    cout << *p;
}

2。二重ポインタのもう一つの応用 -
(これは参照による合格も含みます)

関数から文字を更新したいとします。あなたが以下を試してみた場合: -

void func(char ch)
{
    ch = 'B';
}

int main()
{
    char ptr;
    ptr = 'A';
    printf("%c", ptr);

    func(ptr);
    printf("%c\n", ptr);
}

出力はAAになります。あなたは関数に "値渡し"をしているので、これはうまくいきません。

これを行う正しい方法は - になります -

void func( char *ptr)        //Passed by Reference
{
    *ptr = 'B';
}

int main()
{
    char *ptr;
    ptr = (char *)malloc(sizeof(char) * 1);
    *ptr = 'A';
    printf("%c\n", *ptr);

    func(ptr);
    printf("%c\n", *ptr);
}

文字の代わりに文字列を更新するためのこの要件を拡張しましょう。
これには、関数内のパラメーターをダブルポインターとして受け取る必要があります。

void func(char **str)
{
    strcpy(str, "Second");
}

int main()
{
    char **str;
    // printf("%d\n", sizeof(char));
    *str = (char **)malloc(sizeof(char) * 10);          //Can hold 10 character pointers
    int i = 0;
    for(i=0;i<10;i++)
    {
        str = (char *)malloc(sizeof(char) * 1);         //Each pointer can point to a memory of 1 character.
    }

    strcpy(str, "First");
    printf("%s\n", str);
    func(str);
    printf("%s\n", str);
}

この例では、methodは文字列の値を更新するためのパラメータとしてdoubleポインタを必要とします。

21
Bhavuk Mathur

ポインタへのポインタはまた、メモリ間の「ハンドル」としても役立ちます。この場合、関数間で「ハンドル」を渡してメモリを再配置することができます。これは基本的に、関数がハンドル変数内のポインタが指すメモリを変更できることを意味します。ハンドルを使用しているすべての関数またはオブジェクトは、新しく再配置された(または割り当てられた)メモリを正しく指します。ライブラリはこれを「不透明な」データ型でやるのが好きです。つまり、データ型が指し示されているメモリに対して何をしているのかを気にする必要はなく、単に「ハンドル」を渡すだけです。ライブラリの関数は、そのメモリ上でいくつかの操作を実行します...ライブラリ関数は、メモリ管理のプロセスやハンドルが指している場所を明示的に心配することなく、内部でメモリの割り当てと割り当て解除を行うことができます。

例えば:

#include <stdlib.h>

typedef unsigned char** handle_type;

//some data_structure that the library functions would work with
typedef struct 
{
    int data_a;
    int data_b;
    int data_c;
} LIB_OBJECT;

handle_type lib_create_handle()
{
    //initialize the handle with some memory that points to and array of 10 LIB_OBJECTs
    handle_type handle = malloc(sizeof(handle_type));
    *handle = malloc(sizeof(LIB_OBJECT) * 10);

    return handle;
}

void lib_func_a(handle_type handle) { /*does something with array of LIB_OBJECTs*/ }

void lib_func_b(handle_type handle)
{
    //does something that takes input LIB_OBJECTs and makes more of them, so has to
    //reallocate memory for the new objects that will be created

    //first re-allocate the memory somewhere else with more slots, but don't destroy the
    //currently allocated slots
    *handle = realloc(*handle, sizeof(LIB_OBJECT) * 20);

    //...do some operation on the new memory and return
}

void lib_func_c(handle_type handle) { /*does something else to array of LIB_OBJECTs*/ }

void lib_free_handle(handle_type handle) 
{
    free(*handle);
    free(handle); 
}


int main()
{
    //create a "handle" to some memory that the library functions can use
    handle_type my_handle = lib_create_handle();

    //do something with that memory
    lib_func_a(my_handle);

    //do something else with the handle that will make it point somewhere else
    //but that's invisible to us from the standpoint of the calling the function and
    //working with the handle
    lib_func_b(my_handle); 

    //do something with new memory chunk, but you don't have to think about the fact
    //that the memory has moved under the hood ... it's still pointed to by the "handle"
    lib_func_c(my_handle);

    //deallocate the handle
    lib_free_handle(my_handle);

    return 0;
}

お役に立てれば、

ジェイソン

15
Jason

これまでに何度も見たことがある簡単な例

int main(int argc, char **argv)

2番目のパラメータには、charへのポインタへのポインタがあります。

ポインター表記(char* c)と配列表記(char c[])は、関数の引数では交換可能です。だからchar *argv[]を書くこともできます。言い換えれば、char *argv[]char **argvは交換可能です。

上記が表すのは、実際には文字シーケンス(起動時にプログラムに与えられるコマンドライン引数)の配列です。

上記の関数シグネチャの詳細については、 この回答 も参照してください。

6
plats1

文字列はダブルポインタの使用例です。文字列自体はポインタなので、文字列を指す必要があるときはいつでもダブルポインタが必要です。

5
drysdam

たとえば、メモリを解放するときにポインタを後でnullに設定するようにします。

void safeFree(void** memory) {
    if (*memory) {
        free(*memory);
        *memory = NULL;
    }
}

この関数を呼び出すときは、ポインタのアドレスを付けて呼び出します。

void* myMemory = someCrazyFunctionThatAllocatesMemory();
safeFree(&myMemory);

myMemoryはNULLに設定され、それを再利用しようとする試みは明らかに間違っています。

5
Jeff Foster

以下は非常に単純なC++の例です。オブジェクトを指すようにポインタを設定する関数を使用する場合は、ポインタへのポインタが必要ですとなります。そうでなければポインタはnullに戻り続ける

(C++の回答ですが、Cでも同じです)

(また、参照のために:Google( "値渡しc ++")= "デフォルトでは、C++の引数は値渡しされます。引数が値渡しされると、引数の値は関数のパラメータにコピーされます。)

そのため、ポインタbname__を文字列aname__に等しく設定します。

#include <iostream>
#include <string>

void Function_1(std::string* a, std::string* b) {
  b = a;
  std::cout << (b == nullptr);  // False
}

void Function_2(std::string* a, std::string** b) {
  *b = a;
  std::cout << (b == nullptr);  // False
}

int main() {
  std::string a("Hello!");
  std::string* b(nullptr);
  std::cout << (b == nullptr);  // True

  Function_1(&a, b);
  std::cout << (b == nullptr);  // True

  Function_2(&a, &b);
  std::cout << (b == nullptr);  // False
}

// Output: 10100

Function_1(&a, b);行で何が起こりますか?

  • &main::a(アドレス)の「値」は、パラメータstd::string* Function_1::aにコピーされます。したがってFunction_1::aは文字列main::aへのポインタ(つまりそのメモリアドレス)です。

  • main::bの「値」(メモリ内のアドレス)は、パラメータstd::string* Function_1::bにコピーされます。したがって、これらのアドレスのうち2つがメモリ内にあり、両方ともNULLポインタです。行b = a;では、ローカル変数Function_1::bFunction_1::a(= &main::a)と等しくなるように変更されますが、変数main::bは変更されません。 Function_1の呼び出し後、main::bはまだNULLポインターです。

Function_2(&a, &b);行で何が起こりますか?

  • aname__変数の扱いは同じです。関数内で、Function_2::aは文字列main::aのアドレスです。

  • しかし、変数bname__はポインターへのポインターとして渡されています。 &main::bの「値」(ポインターのアドレスmain::b)がstd::string** Function_2::bにコピーされます。したがって、Function_2内では、これを*Function_2::bとして参照しないと、main::bにアクセスして変更されます。そのため、*b = a;行は実際にはmain::b(= Function_2::aのアドレス)と等しいmain::a(アドレス)を設定しています。

オブジェクトやアドレス(ポインタ)であれ、ものを変更するために関数を使いたい場合は、そのものへのポインタを渡す必要があります。あなたがしていること実際にはローカルコピーが作成されるため、(呼び出しスコープ内で)pass-inを変更することはできません。

(パラメータがstd::string& aなどの参照である場合は例外です。通常、これらはconstname__です。一般的に、f(x)を呼び出す場合は、xname__がオブジェクトであれば、fしないと見なすことができます。 = modify xname__。ただし、xname__がポインタの場合は、fmightxname__が指すオブジェクトを変更すると想定します。)

2
jt117

たとえば、連続していないデータへのランダムアクセスが必要な場合です。

p -> [p0, p1, p2, ...]  
p0 -> data1
p1 -> data2

- Cで

T ** p = (T **) malloc(sizeof(T*) * n);
p[0] = (T*) malloc(sizeof(T));
p[1] = (T*) malloc(sizeof(T));

ポインタの配列を指すポインタpを格納します。各ポインタはデータを指します。

sizeof(T)が大きい場合、sizeof(T) * nバイトの連続ブロック(mallocを使用)を割り当てることは不可能かもしれません。

2
log0

私が絶えず使用していることの1つは、オブジェクトの配列があり、それらを異なるフィールドで検索(バイナリ検索)する必要があるときです。
元の配列を保持しています...

int num_objects;
OBJECT *original_array = malloc(sizeof(OBJECT)*num_objects);

次に、オブジェクトへのソートされたポインタの配列を作ります。

int compare_object_by_name( const void *v1, const void *v2 ) {
  OBJECT *o1 = *(OBJECT **)v1;
  OBJECT *o2 = *(OBJECT **)v2;
  return (strcmp(o1->name, o2->name);
}

OBJECT **object_ptrs_by_name = malloc(sizeof(OBJECT *)*num_objects);
  int i = 0;
  for( ; i<num_objects; i++)
    object_ptrs_by_name[i] = original_array+i;
  qsort(object_ptrs_by_name, num_objects, sizeof(OBJECT *), compare_object_by_name);

必要なだけソートポインタ配列を作成してから、ソートポインタ配列でバイナリ検索を使用して、必要なオブジェクトにアクセスすることができます。オブジェクトの元の配列はソートされないままになりますが、各ポインタ配列はそれらの指定されたフィールドによってソートされます。

2
DavidMFrey

なぜ二重ポインタ?

目的は、関数を使用して、studentAが指すものを変更することです。

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


typedef struct Person{
    char * name;
} Person; 

/**
 * we need a ponter to a pointer, example: &studentA
 */
void change(Person ** x, Person * y){
    *x = y; // since x is a pointer to a pointer, we access its value: a pointer to a Person struct.
}

void dontChange(Person * x, Person * y){
    x = y;
}

int main()
{

    Person * studentA = (Person *)malloc(sizeof(Person));
    studentA->name = "brian";

    Person * studentB = (Person *)malloc(sizeof(Person));
    studentB->name = "erich";

    /**
     * we could have done the job as simple as this!
     * but we need more work if we want to use a function to do the job!
     */
    // studentA = studentB;

    printf("1. studentA = %s (not changed)\n", studentA->name);

    dontChange(studentA, studentB);
    printf("2. studentA = %s (not changed)\n", studentA->name);

    change(&studentA, studentB);
    printf("3. studentA = %s (changed!)\n", studentA->name);

    return 0;
}

/**
 * OUTPUT:
 * 1. studentA = brian (not changed)
 * 2. studentA = brian (not changed)
 * 3. studentA = erich (changed!)
 */
1

パーティーに少し遅れましたが、うまくいけば誰かの助けになります。

C配列では、常にスタックにメモリを割り当てます。したがって、実行が現在のブロックの最後に達すると、スタックに割り当てられたメモリが自動的に解放されるため、関数は(非静的)配列を返すことができません。 2次元配列(つまり、行列)を扱い、行列を変更して返すことができるいくつかの関数を実装する場合、それは本当に迷惑です。これを実現するために、ポインターツーポインターを使用して、動的に割り当てられたメモリでマトリックスを実装できます。

/* Initializes a matrix */
float** init_matrix(int num_rows, int num_cols){
    // Allocate memory for num_rows float-pointers
    float** A = calloc(num_rows, sizeof(double*));
    // return NULL if the memory couldn't allocated
    if(A == NULL) return NULL;
    // For each float-pointer (row) allocate memory for num_cols floats
    for(int i = 0; i < num_rows; i++){
        A[i] = calloc(num_cols, sizeof(double));
        // return NULL if the memory couldn't allocated
        // and free the already allocated memory
        if(A[i] == NULL){
            for(int j = 0; j < i; j++){
                free(A[j]);
            }
            free(A);
            return NULL;
        }
    }
    return A;
} 

以下に例を示します。

 float**        float*            float
             -------------       ---------------------------------------------------------
   A ------> |   A[0]    | ----> | A[0][0] | A[0][1] | A[0][2] | ........ | A[0][cols-1] |
             | --------- |       ---------------------------------------------------------
             |   A[1]    | ----> | A[1][0] | A[1][1] | A[1][2] | ........ | A[1][cols-1] |
             | --------- |       ---------------------------------------------------------
             |     .     |                                    .
             |     .     |                                    .
             |     .     |                                    .
             | --------- |       ---------------------------------------------------------
             |   A[i]    | ----> | A[i][0] | A[i][1] | A[i][2] | ........ | A[i][cols-1] |
             | --------- |       ---------------------------------------------------------
             |     .     |                                    .
             |     .     |                                    .
             |     .     |                                    .
             | --------- |       ---------------------------------------------------------
             | A[rows-1] | ----> | A[rows-1][0] | A[rows-1][1] | ... | A[rows-1][cols-1] |
             -------------       ---------------------------------------------------------

Float-pointer-to-float-pointer Aは、要素が浮動小数点ポインタであるメモリブロックの最初の要素A [0]を指します。これらのフロートポインターは、行列の行として想像できます。それが、すべてのフロートポインターがnum_colsフロート要素にメモリを割り当てる理由です。さらに、A [i]はi番目の行を指します。つまり、A [i]はA [i] [0]を指します。これは、i番目の行のメモリブロックの最初のfloat要素です。最後に、A [i] [j]を使用して、i番目の行とj番目の列の要素に簡単にアクセスできます。

使用法を示す完全な例は次のとおりです。

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

/* Initializes a matrix */
float** init_matrix(int num_rows, int num_cols){
    // Allocate memory for num_rows float-pointers
    float** matrix = calloc(num_rows, sizeof(double*));
    // return NULL if the memory couldn't allocated
    if(matrix == NULL) return NULL;
    // For each float-pointer (row) allocate memory for num_cols
    // floats
    for(int i = 0; i < num_rows; i++){
        matrix[i] = calloc(num_cols, sizeof(double));
        // return NULL if the memory couldn't allocated
        // and free the already allocated memory
        if(matrix[i] == NULL){
            for(int j = 0; j < i; j++){
                free(matrix[j]);
            }
            free(matrix);
            return NULL;
        }
    }
    return matrix;
}

/* Fills the matrix with random float-numbers between -1 and 1 */
void randn_fill_matrix(float** matrix, int rows, int cols){
    for (int i = 0; i < rows; ++i){
        for (int j = 0; j < cols; ++j){
            matrix[i][j] = (float) Rand()/Rand_MAX*2.0f-1.0f;
        }
    }
}


/* Frees the memory allocated by the matrix */
void free_matrix(float** matrix, int rows, int cols){
    for(int i = 0; i < rows; i++){
        free(matrix[i]);
    }
    free(matrix);
}

/* Outputs the matrix to the console */
void print_matrix(float** matrix, int rows, int cols){
    for(int i = 0; i < rows; i++){
        for(int j = 0; j < cols; j++){
            printf(" %- f ", matrix[i][j]);
        }
        printf("\n");
    }
}


int main(){
    srand(time(NULL));
    int m = 3, n = 3;
    float** A = init_matrix(m, n);
    randn_fill_matrix(A, m, n);
    print_matrix(A, m, n);
    free_matrix(A, m, n);
    return 0;
}
1
joni

変更変数の値と変更ポインタの値の比較:

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

void changeA(int (*a))
{
  (*a) = 10;
}

void changeP(int *(*P))
{
  (*P) = malloc(sizeof((*P)));
}

int main(void)
{
  int A = 0;

  printf("orig. A = %d\n", A);
  changeA(&A);
  printf("modi. A = %d\n", A);

  /*************************/

  int *P = NULL;

  printf("orig. P = %p\n", P);
  changeP(&P);
  printf("modi. P = %p\n", P);

  free(P);

  return EXIT_SUCCESS;
}

これは、ポインタが呼び出された関数によって変更されたときに、ポインタの値が返されないようにするのに役立ちました(単一リンクリストで使用)。

OLD(悪い):

int *func(int *P)
{
  ...
  return P;
}

int main(void)
{
  int *pointer;
  pointer = func(pointer);
  ...
}    

NEW(より良い):

void func(int **pointer)
{
  ...
}

int main(void)
{
  int *pointer;
  func(&pointer);
  ...
}    
0
Sany

私は仕事のために何かをプログラムしている間、今日私は二重ポインタを使った、それで私たちがそれらを使わなければならなかった理由に答えることができる(それが私が実際に二重ポインタを使わなければならなかったのは初めてだ)いくつかの構造体のメンバーであるバッファに含まれるフレームのリアルタイムエンコーディングを扱う必要がありました。エンコーダでは、これらの構造の1つへのポインタを使用しなければなりませんでした。問題は、私たちのポインタが他のスレッドの他の構造体を指すように変更されていたことです。現在の構造体をエンコーダで使用するには、別のスレッドで変更されていたポインタを指すために、ダブルポインタを使用する必要がありました。少なくとも私たちにとって、このアプローチを取らなければならなかったことは最初は明白ではありませんでした。たくさんの住所が印刷されました:))。

アプリケーションの他の場所で変更されているポインタを操作するときは、二重ポインタを使用する必要があります。あなたが戻ってあなたに宛てたハードウェアを扱うとき、あなたは二重のポインタが絶対必要であると思うかもしれません。

0
Axenie Ionut

次の例では、ダブルポインターがどのように機能するかについての洞察または直感を与えます。

1) try to understand the following statements
   char **str ;

   a) str is of type char ** whose value is an address of another pointer.
   b) *str is of type char * whose value is an address of variable or (it is a string itself).
   c) **str is of type char ,gives the value stored, in this case a character.

以下はあなたが理解するためにあなたが上記の点(a、b、c)に関係することができるコードです

str = (char **)malloc(sizeof(char *) *2); // here i am assigning the mem for two char *
       str[0]=(char *)"abcdefghij"; // assignin the value
       str[1]=(char *)"xyzlmnopqr"; 

ここで値、すなわち配列内の文字列を表示するには、単にポイントbを見てください。文字列の場合、値とアドレスは同じなので、再度参照解除する必要はありません。

cout<<*str<<endl;   // abcdefghij;

次の文字列を出力するには、次に示すように、* strからstrへの1つの参照解除(*)から出て、次にインクリメントします。

str++;

文字列を印刷する

cout<<*str<<endl;        //xyzlmnopqr

文字列内の文字だけを表示するには、c)を参照してください。

cout<<**str<<endl;  // prints the first character i.e "a"

文字列の次の文字、すなわち "b"を1つの間接参照演算子から出力し、** strから* strに移動して* str ++を実行するようになりました。

*str++;

今文字を印刷

cout<<**str<<endl;  // prints the second character i.e "b"

2つの配列( "abcdefghij"、 "xylmnopqr")はアドレスのインクリメントで同じことが行われると連続したメモリブロックに格納されるので、2つの文字列のすべての文字が表示されます。

0
Abhishek D K

うまくいけば、次の例はポインタとダブルポインタ、それらの違いと一般的なシナリオでの使用法に関するいくつかの概念を明確にするでしょう。

    int* setptr(int *x)
    {
        printf("%u\n",&x);
        x=malloc(sizeof(int));
        *x=1;
        return x;
    }

In the above function setptr we can manipulate x either
1. by taking fn arg as int *x , doing  malloc and setting value of x and return x 
Or
    2. By taking arg as int ** and malloc and then set  **x value to some value.
Note: we cant set any general pointer directly without doing  malloc.Pointer indicates that it is a type of variable which can hold address of any data type.Now either we define a variable and give reference to it or we declare a pointer(int *x=NULL) and allocate some memory to it inside the called function where we pass x or a reference to it .. In either case we need to have address of a memory in the  pointer and in the case pointer initially points  to NULL or it is defined like int *x where it points  to any random address then we need to assign a valid memory address to pointer 

    1. either we need to allocate memory to it by malloc

    int *x=NULL means its address is 0.
    Now we need to either o following
    1.



    void main()
        {
            int *x;
            x=malloc
            *x=some_val;
        }
        Or
        void main()
        {
            int *x
            Fn(x);
        }

        void Fn(int **x)
        {
            *x=malloc;
            **x=5;
        }
        OR
        int * Fn(int *x)
        {
            x=malloc();
            *x=4;
            Return x;
        }


        2. Or we need to point it to a valid memory like a defined variable inside the function where pointer is defined.


        OR
        int main()
        {
            int a;
            int *x=&a;
            Fn(x);
            printf("%d",*x);
        }
        void Fn(int *x)
        {
            *x=2;
        }


     in both cases value pointed by x is changed inside fn

    But suppose if we do like


    int main()
    {
        int *x=NULL;
        printf("%u\n",sizeof(x));
        printf("%u\n",&x);
        x=setptr(x);
        //*x=2;
        printf("%d\n",*x);
        return 0;
    }

/* output
4
1
*/

#include<stdio.h>
void setptr(int *x)
{
    printf("inside setptr\n");
    printf("x=%u\n",x);
    printf("&x=%u\n",&x);
    //x=malloc(sizeof(int));
    *x=1;
    //return x;
}
int main()
{
    int *x=NULL;
    printf("x=%u\n",x);
    printf("&x=%u\n",&x);
    int a;
    x=&a;
    printf("x=%u\n",x);
    printf("&a=%u\n",&a);
    printf("&x=%u\n",&x);
    setptr(x);
    printf("inside main again\n");

    //*x=2;
    printf("x=%u\n",x);
    printf("&x=%u\n",&x);
    printf("*x=%d\n",*x);
    printf("a=%d\n",a);
    return 0;
}
0
Peter_pk

bhavuk Mathurが示しているようなダブルポインタの適用は間違っているようです。以下の例は有効なものです。

void func(char **str)
{
     strcpy(str[0],"second");
}

int main(){

    char **str;
    str = (char **)malloc(sizeof(char*)*1); // allocate 1 char* or string
    str[0] = (char *)malloc(sizeof(char)*10);      // allocate 10 character
    strcpy(str[0],"first");            // assign the string
    printf("%s\n",*str);
    func(str);            
    printf("%s\n",*str);           
    free(str[0]); 
    free(str);
}
0
Abhishek D K

二重ポインタの適用の1つは、変更が反映されるように文字列を更新することです。

#include <iostream>
#include <cstring>  // for using strcpy
using namespace std;

void change(char **temp)
{
   strcpy(temp[0],"new");
   strcpy(temp[1],"value");
}

int main()
{
   char **str;
   str = (char **)malloc(sizeof(char *)*3);
   str[0]=(char *)malloc(10);
   str[1]=(char *)malloc(10);
   strcpy(str[0],"old");
   strcpy(str[1],"name");
   char **temp = str;  // always use the temporary variable
   while(*temp!=NULL)
{
    cout<<*temp<<endl;
    temp++;
}
temp = str;   // making it point it to the original head because we have changed the address in while loop above
change(str);
while(*temp!=NULL)
{
   cout<<*temp<<endl;
   temp++;
}

free(temp);
free(str[0]);
free(str[1]);
free(str);
0
Abhishek D K