Qual è lo scopo degli array in C, quando i puntatori avrebbero potuto svolgere il lavoro?

7

Array e puntatori non sono la stessa cosa in C, anche se sono correlati e possono essere usati allo stesso modo. Finora siamo tutti d'accordo.

Tuttavia, non vedo perché gli array siano stati inclusi in C, quando i puntatori avrebbero potuto svolgere il loro lavoro perfettamente.

Non sto dicendo di rimuovere la notazione dell'array (ad es. a [5] o int a [4] = {0,1,2,3};), che è abbastanza utile e conveniente. Ma potresti avere la stessa notazione che funziona in cima ai puntatori (come è il caso), come misura cosmetica. Quindi la notazione dell'array non è un motivo per avere array, solo la notazione!

L'unica differenza che vedo è che gli array sono puntatori costanti e la dimensione della memoria a cui puntano non può essere modificata. Ma questo può essere ottenuto anche con i puntatori, rendendoli costanti (la memoria non sarebbe di dimensioni fisse, ma non sono sicuro che questo sia un problema).

Quindi, perché non avere solo i puntatori e lasciare che il programmatore decida come deve comportarsi il puntatore (vale a dire, costante, non costante, dimensione fissa, dimensione variabile, ecc.)?

    
posta Daniel Scocco 21.10.2011 - 01:16
fonte

6 risposte

13

Gli array sono memoria contigua creata nello stack. Non si può garantire memoria dello stack contigua senza questo zucchero sintattico, e anche se fosse possibile, si dovrebbe allocare un puntatore separato per poter eseguire l'aritmetica del puntatore (a meno che non si voglia fare *(&foo + x) , che io Non sono sicuro, ma potrebbe violare la semantica del valore di l, ma è almeno abbastanza imbarazzante, e urlerei per qualche tipo di zucchero sintattico). Dal punto di vista del design, è anche una forma di incapsulamento, dal momento che è possibile fare riferimento alla raccolta con un singolo identificatore (che altrimenti richiederebbe un puntatore separato). E anche se potessi allocarli in modo contiguo e assegnare un puntatore separato per farli riferimento, avresti entrambi %codice%... che costringe una buona dose di creatività mentre cresce la tua collezione, quindi potresti pensare di semplificare %codice% ..., che sembra proprio un array ma è più difficile da mantenere.

    
risposta data 21.10.2011 - 02:13
fonte
12

La notazione delle matrici è comoda, più facile da leggere e meno incline agli errori. Fornisce un formalismo sui puntatori. Potrebbe essere zucchero sintattico, ma tutti abbiamo bisogno di un po 'di dolcezza una volta ogni tanto, vero?

Come per tutte le astrazioni, si rinuncia a una certa flessibilità per la comodità fornita dall'astrazione.

    
risposta data 21.10.2011 - 01:20
fonte
9

Sono sorpreso che nessuno abbia ancora commentato niente sugli array multidimensionali.

Se hai una matrice composta da "puntatori annidati" (ad esempio int **p ), quello che hai in ogni "riga" (dimensione esterna) è un puntatore che punta al primo elemento di quella riga, quindi l'accesso a un valore richiede due accessi alla memoria. Inoltre, la memoria richiesta è sizeof(*int)*n + n*m*sizeof(int) .

Nello scenario della serie bidimensionale int p[n][m] , l'accesso a un elemento richiede solo un accesso alla memoria, poiché l'indirizzo della riga viene calcolato anziché alzato; e la memoria richiesta è solo n*m*sizeof(int) .

Un altro punto in cui un array non può essere sostituito da un puntatore si trova all'interno delle strutture.

struct s {
    int[2];
    float;
}

non è definitivamente uguale a

struct s {
   *int;
   float;
}

la dimensione dell'array è importante lì, ei puntatori non hanno quell'informazione.

Quindi sì, gli array unidimensionali e i singoli puntatori sono prevalentemente intercambiabili, ma le loro somiglianze finiscono qui.

    
risposta data 21.10.2011 - 11:07
fonte
2

Perché non dovrei essere in grado di utilizzare gli array per i tipi di valore?

int a[4] = {0,1,2,3};

    
risposta data 21.10.2011 - 01:21
fonte
1

Come gestiresti piattaforme, come l'8031 senza memoria esterna, che non supportano malloc o alloca ? Forse ti stai dimenticando che C non è solo per il grande ferro ma è anche per i controller degli ascensori e i tostapane.

    
risposta data 21.10.2011 - 03:08
fonte
1

In C, la notazione degli array all'interno delle espressioni è sempre semplicemente aritmetica del puntatore. Tutti gli usi di un identificatore di matrice in un'espressione vengono immediatamente convertiti da "matrice di T" a "puntatore a T" e il valore viene convertito in un puntatore al primo elemento dell'array. La notazione di matrice (ad esempio a[1][2] ) viene sempre espansa nell'aritmetica del puntatore (ad esempio *(*(t+1)+2) ).

Tuttavia la notazione dell'array nelle dichiarazioni e definizioni è un'altra cosa. Un dichiaratore di array descrive un tipo "array of T " in cui i valori di questo tipo sono sequenze di elementi di tipo T . Una definizione di un oggetto array consiste nell'utilizzare una notazione di array comoda e facilmente comprensibile per allocare la quantità appropriata di memoria per la matrice di oggetti desiderata in modo tale che l'identificatore di array si riferisca a questa memoria senza che assomigli ad un puntatore. In effetti la notazione dell'array in una dichiarazione o definizione è una macro che genera un'espressione usando sizeof() e aritmetica e, nel caso di una definizione, l'equivalente di alloca() per gli array auto o il suo equivalente nel linker per gli array globali, e facendo tutto questo in fase di compilazione (eccetto per gli array di lunghezza variabile C99).

L'uso della notazione degli array nelle espressioni e l'uso dei tipi di array non è così strettamente connesso, sebbene sia tradizione e idioma usare la notazione dell'array nelle espressioni per fare riferimento allo storage in oggetti dichiarati e / o definiti come array. È altrettanto facile utilizzare la notazione degli array con un tipo di puntatore per rendere il puntatore aritmetico più pulito e più significativo. Infatti in C un'espressione della forma e1[e2] è esattamente equivalente all'espressione *((e1)+(e2)) . La solita conversione binaria viene applicata ai due operandi e il risultato è sempre un lvalue . Poiché l'operatore di riferimento indiretto ( * ) deve avere un puntatore come suo operando, uno di e1 e e2 deve essere un puntatore e l'altro deve essere un numero intero, ma non importa quale poiché la conversione unaria iniziale per qualsiasi "matrice di T" è convertirla in "puntatore a T". La notazione delle matrici nelle espressioni è in effetti una macro del compilatore (livello del linguaggio) per generare espressioni aritmetiche del puntatore.

Quindi C davvero funziona già come suggerisci ma stai confondendo l'uso della notazione in due contesti molto separati.

    
risposta data 13.06.2017 - 00:16
fonte

Leggi altre domande sui tag