confusione di sintassi con array e puntatori

Sep 01 2020

Considera quanto segue:

typedef struct node
{
    int number;
    struct node *left;
    struct node *right;
} node;


node  test[511];
node  *test1 = malloc(511 * sizeof(node));
node  (*test2)[511] = malloc(511 * sizeof(node));

è corretto quanto segue:

  1. test è un array di 511 nodi. possiamo accedere a ogni nodo con test [i] dove i è il nodo che vogliamo
  2. test1 è un puntatore in cui mallociamo lo spazio per 511 nodi. possiamo accedere a ogni nodo con test1 [i]

quindi test e test1 sono fondamentalmente uguali tranne che test è sullo stack e test1 sull'heap?

test2 sembra un puntatore a un array. malloc alloca 511 * sizeof (nodo) a ciascun puntatore o all'intero array? se il secondo allora possiamo accedere a ciascun nodo con test2 [i] e se il primo può fare quanto segue:

node  (*test2)[511] = malloc(sizeof(node));

e quindi accedere a ciascun nodo con test2 [i]?

in generale qual è la differenza tra * test1 e (* test2)?

Risposte

1 P__JsupportswomeninPoland Sep 01 2020 at 22:10

La differenza è che test1ci si riferisce al file node. test2si riferisce alla matrice di 511 nodeelementi.

test1 + 1farà riferimento nodeall'oggetto successivo

test2 + 1farà riferimento al prossimo array di 511 nodeoggetti

quindi test e test1 sono fondamentalmente uguali tranne che test è sullo stack e test1 sull'heap?

Nessuno è un array che può decadere in puntatore, un altro è un puntatore che fa riferimento a un oggetto di tipo node

Quando si mallocmemorizza è meglio usare gli oggetti invece dei tipi

node  test[511];
node  *test1 = malloc(511 * sizeof(*test1));
node  (*test2)[511] = malloc(511 * sizeof(*test2));
{
    int number;
    struct node *left;
    struct node *right;
} node;

int main(void)
{
    node  test[511];
    node  *test1;
    node  (*test2)[511];

    printf("sizeof test = %zu\n", sizeof(test));
    printf("sizeof *test1 = %zu\n", sizeof(*test1));
    printf("sizeof *test2 = %zu\n", sizeof(*test2));
}
typedef struct node
{
    int number;
    struct node *left;
    struct node *right;
} node;

int main(void)
{
    node  test[511];
    node  *test1;
    node  (*test2)[511];

    printf("sizeof test = %zu\n", sizeof(test));
    printf("sizeof *test1 = %zu\n", sizeof(*test1));
    printf("sizeof *test2 = %zu\n", sizeof(*test2));
}
1 VladfromMoscow Sep 01 2020 at 22:22

in generale qual è la differenza tra * test1 e (* test2)?

In questa dichiarazione

node  *test1 = malloc(511 * sizeof(node));

viene dichiarato un puntatore a un oggetto del tipo node. Quindi dereferenziando il puntatore come *test1si otterrà un oggetto del tipo node. Cioè avrai accesso al primo oggetto dell'array allocato dinamicamente.

In questa dichiarazione

node  (*test2)[511] = malloc(511 * sizeof(node));

viene dichiarato un puntatore a un oggetto del tipo node[511]. Questo è l'oggetto appuntito ha il tipo di matrice node[511]. Dereferenziando il puntatore si otterrà la matrice puntata allocata dinamicamente.

Quindi sizeof( *test1 )è uguale a sizeof( node ). Mentre sizeof( *test2 )è uguale a sizeof( node[511] )quello è lo stesso di 511 * sizeof( node ).

Per accedere al primo elemento dell'array allocato utilizzando il puntatore test2è necessario prima dereferenziarlo per ottenere l'array puntato che a sua volta utilizzato in un'espressione con l'operatore di accesso ai membri -> viene convertito implicitamente in puntatore al suo primo elemento.

Ecco un programma dimostrativo.

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

typedef struct node
{
    int number;
    struct node *left;
    struct node *right;
} node;

int main(void) 
{
    node  *test1 = malloc(511 * sizeof(node));
    node  (*test2)[511] = malloc(511 * sizeof(node));
    
    printf( "sizeof( *test1 ) = %zu\n", sizeof( *test1 ) ); 
    printf( "sizeof( *test2 ) = %zu\n", sizeof( *test2 ) ); 
    
    ( *test1 ).number  = 10;
    ( **test2 ).number = 20;
    
    printf( "test1->number = %d\n", test1->number );
    printf( "( *test2 )->number = %d\n", ( *test2 )->number );
    
    free( test1 );
    free( test2 );
    
    return 0;
}

L'output del programma è

sizeof( *test1 ) = 24
sizeof( *test2 ) = 12264
test1->number = 10
( *test2 )->number = 20

Il puntatore test2potrebbe anche essere inizializzato, ad esempio, nel modo seguente

node  test[511];
node  (*test2)[511] = &test;

Mentre il puntatore test1potrebbe essere inizializzato come

 node *test1 = test;
1 anastaciu Sep 01 2020 at 22:30

Quindi sono teste test1fondamentalmente la stessa cosa tranne che testè in pila e test1in pila?

Sì, parlando liberamente, possiamo dirlo. Con una dichiarazione di non responsabilità, questi sono tipi diversi.

test2sembra un puntatore a un array. non mallocassegnare 511 * sizeof(node)ad ogni puntatore o per l'intera matrice?

Di nuovo, parlando liberamente, possiamo dire a ciascun puntatore, che in questo caso è anche l'intero array, poiché allochi solo 1 blocco di 511 nodi.

Questo è un puntatore all'array di 511, in quanto tale dovresti assegnargli solo blocchi di memoria multipli di 511 * sizeof(node). Potresti assegnargli qualcosa come:

node (*test2)[511] = malloc(sizeof(node) * 511 * 5);

In tal caso avresti un array di 5 node (*test2)[511]. Puoi equipararlo a node test2[5][511]come la notazione di accesso è la stessa.

Se il secondo, possiamo accedere a ciascun nodo con test2[i]e se il primo può fare quanto segue:

node  (*test2)[511] = malloc(sizeof(node));

e poi accedi a ogni nodo con test2[i]?

Questa allocazione non è corretta. test2[0]punta al primo blocco di 511 nodi, in particolare al primo elemento di ogni blocco di 511 nodi, test2[1]punta al primo elemento del successivo blocco di 511 nodi, non è possibile utilizzare questo puntatore per accedere a singoli nodi, ad eccezione di ogni primo nodo di ogni blocco di 511 nodi.

L'accesso ai singoli nodi deve essere effettuato, ad esempio test2[0][1], per il secondo nodo (indice 1) del primo blocco di 511 nodi.

Quindi, ancora una volta, l'allocazione deve essere di blocchi di multipli di 511 * sizeof(node).

In generale qual è la differenza tra *test1e (*test2)?

Questo è tutto, test1è un puntatore a node, test2è un puntatore a un array di 511 nodi. Le spiegazioni precedenti dovrebbero fare la differenza rilevata.

test1viene utilizzato per accedere a qualsiasi membro di ciascun blocco dell'array di nodi, test2viene utilizzato per accedere a ciascun blocco di 511 nodi.

SouravGhosh Sep 01 2020 at 22:09

è corretto quanto segue:

  1. testè un array di 511 nodi. possiamo accedere a ogni nodo con test[i]dove si itrova il nodo che vogliamo

  1. test1è un puntatore in cui mallocspaziamo per 511 nodi. possiamo accedere a ogni nodo contest1[i]

Sì.

così sono teste test1fondamentalmente gli stessi tranne che test è sullo stack e test1 sull'heap?

Non esiste il concetto di stack o heap nello standard C, tuttavia, a parte questo, non sono la stessa cosa. testè un array ed test1è un puntatore, sono tipi completamente diversi.

arfneto Sep 02 2020 at 01:02

Ho preso il tuo codice e ho aggiunto anche una quarta alternativa. E posterò un programma e una discussione che possono essere utili per renderlo un po 'più chiaro.

Credo che questa linea

    Node*   test1 = malloc(_SIZE_ * sizeof(Node));

è la chiave per capire come le cose possono confondersi. malloc()restituisce solo un puntatore a un'area delle dimensioni dell'argomento. In effetti queste 2 linee sono simili

   int* example =  malloc(511);
   Node*   test1 = malloc(_SIZE_ * sizeof(Node));

e illustra perché le persone di C ++ rendono obbligatorio il cast di un tipo per la restituzione di malloc()like in

   int*  example =  (int*) malloc(511);
   Node*   test1 = (Node*) malloc(_SIZE_ * sizeof(Node));

rende le cose più chiare, hanno detto. E io credo. In questo modo vediamo che test1è solo un puntatore a Nodee può avvertirci che forse le cose stanno andando male, o potrebbe non essere come ci aspettavamo: non fa differenza il numero di byte allocati, sarà un file NODE*. Un puntatore a un'area.

torna alla roba di prova [123] qui

test1 come

    Node*   test1 = malloc(_SIZE_ * sizeof(Node));

test1è solo un puntatore a Node. malloc()assegnerà felicemente quanti byte come valuta dall'argomento. Anche meno della dimensione di uno Node, e il programma potrebbe bloccarsi molto velocemente ... o 511 byte, non facendo alcuna differenza pratica nel test ma portandolo a questo argomento in SO :)

test

    #define _SIZE_ 16
    Node    test[_SIZE_];

test è solo un array di Node

typedef struct node
{
    int number;
    struct node* left;
    struct node* right;
}   Node;

test2

Node    (*test2)[_SIZE_] = malloc(_SIZE_ * sizeof(Node));

Questo non è visto di frequente perché non è flessibile: test2è un puntatore a un array di [_SIZE_]elementi di Node. Una cosa proprio come test. In effetti mostrerò di seguito che è perfettamente corretto scrivere

Node    test[_SIZE_];
Node    (*test2)[_SIZE_] = &test;

perché questa è solo la definizione della cosa a cui test2punta, ma poiché _SIZE_il noto in fase di compilazione è usato raramente. Invece abbiamo cose molto più flessibili come il familiare

int main(int argc, char** argv);

E introducendo test3

Node** test3;

Ecco test3un puntatore a un array di puntatori a Node, e questo è il modo utile, come ogni C o C ++ o qualsiasi programma conosce. Cerchiamo di riempirlo

    Node** test3 = (Node**)malloc(sizeof(Node*) * _SIZE_);
    for (int i = 0; i < _SIZE_; i += 1)
    {
        test3[i] = (Node*)malloc(sizeof(Node));
        test3[i]->number = 1000 + i;
    };

Ora test3punta a un'area di _SIZE_volte il sizeof()di un puntatore a NODE. E andiamo nell'area e impostiamo i singoli riferimenti a un reale NODE, ognuno. E mettiamo un valore nel numero membro di ogni nodo in modo da poterlo stampare in seguito nel programma di esempio.

  • Qual è la differenza? Ora possiamo iterare sui nodi proprio come facciamo ancora e ancoraargv[i]
  • Che cosa manca? Le informazioni sulla dimensione. Questo è il motivo per cui abbiamo argcin ogni programma. Potremmo scrivere
// now to iterate over Nodes: should be as familiar as
typedef struct 
{
    int     nodec;
    Node**  nodev;

}   NodeArray;

così familiare ... E possiamo NodeArraysignorare matrici iterabili di strutture, proprio come gli argomenti della riga di comando ...

output dell'esempio

sizeof(test) = 384
sizeof(test1) = 8
sizeof(test2) = 8

test is Node[_SIZE_]. Values are
     0     1     2     3     4     5     6     7     8     9    10    11    12    13    14    15

test2 is a pointer to Node[_SIZE_]. So we can assign &test to it

Done. Now the values of test2:
     0     1     2     3     4     5     6     7     8     9    10    11    12    13    14    15

test2 restored. Now set up from 500
   500   501   502   503   504   505   506   507   508   509   510   511   512   513   514   515

test1 is just a pointer to Node. Let's set it to 300
*test1 is 300

test3 is an array of pointers to Node, set up from 1000:
  1000  1001  1002  1003  1004  1005  1006  1007  1008  1009  1010  1011  1012  1013  1014  1015

Codice d'esempio

#define _SIZE_ 16
#include <stdio.h>
#include <stdlib.h>

typedef struct node
{
    int number;
    struct node* left;
    struct node* right;
}   Node;

int main(void)
{
    Node    test[_SIZE_];
    Node*   test1 = malloc(_SIZE_ * sizeof(Node));
    int*    example = malloc(511); // no meaning
    Node    (*test2)[_SIZE_] = malloc(_SIZE_ * sizeof(Node));
    // test2 points to Node[_SIZE_]
    for (int i = 0; i < _SIZE_; i += 1) test[i].number = i;

    printf("sizeof(test) = %zd\n", sizeof(test));
    printf("sizeof(test1) = %zd\n", sizeof(test1));
    printf("sizeof(test2) = %zd\n", sizeof(test2));

    // test is an array of Node
    printf("\ntest is Node[_SIZE_]. Values are \n");
    for (int i = 0; i < _SIZE_; i += 1)
        printf("%6d", test[i].number);
    printf("\n");

    // test2 points to an array of Node
    printf("\ntest2 is a pointer to Node[_SIZE_]. So we can assign &test to it\n");
    void* save = test2; // or it will leak
    test2 = &test;
    printf("\nDone. Now the values of test2:\n");
    for (int i = 0; i < _SIZE_; i += 1)
        printf("%6d", (*test2)[i].number);
    printf("\n");

    test2 = save; // restored
    printf("\ntest2 restored. Now set up from 500\n");
    for (int i = 0; i < _SIZE_; i += 1) (*test2)[i].number = 500 + i;
    for (int i = 0; i < _SIZE_; i += 1)
        printf("%6d", (*test2)[i].number);
    printf("\n");

    // test1 is just a pointer to node
    printf("\ntest1 is just a pointer to Node. Let's set it to 300\n");
    test1->number = 300;
    printf("*test1 is %d\n", test1->number);

    // now to iterate over Nodes: should be as familiar as
    typedef struct 
    {
        int     nodec;
        Node**  nodev;

    }   NodeArray;

    //Node** test3;
    Node** test3 = (Node**)malloc(sizeof(Node*) * _SIZE_);
    for (int i = 0; i < _SIZE_; i += 1)
    {
        test3[i] = (Node*)malloc(sizeof(Node));
        test3[i]->number = 1000 + i;
    };
    // test3 is an array of Node
    printf("\ntest3 is an array of pointers to Node, set up from 1000:\n");
    for (int i = 0; i < _SIZE_; i += 1)
        printf("%6d", test3[i]->number);
    printf("\n");

    // now free() all this
    // test is static
    free(test1); // test1 is Node*
    // test2 is Node (*)[]
    free(test2);
    // test3 is a pointer to an array of pointers...
    for (int i = 0; i < _SIZE_; i += 1) free(test3[i]);
    // all gone
    test3 = NULL; // invalidate it
    printf("\n");
    return 0;
};