Perché la dimensione di un array fa parte del suo tipo?


14

Durante la lettura del libro C ++ Primer, mi sono imbattuto in questa affermazione: "Il numero di elementi in un array fa parte del tipo di array". Quindi volevo scoprirlo usando il seguente codice:

#include<iostream>

int main()
{
    char Array1[]{'H', 'e', 'l', 'p'};
    char Array2[]{'P', 'l', 'e', 'a', 's', 'e'};

    std::cout<<typeid(Array1).name()<<std::endl;        //prints  A4_c
    std::cout<<typeid(Array2).name()<<std::endl;        //prints  A6_c

    return 0;
}

È interessante notare che il risultato di typeid sui due array ha mostrato che sono in qualche modo diversi.

  • Cosa succede dietro le quinte?
  • Perché è necessario che l'array abbia un tipo che ne includa le dimensioni? È solo perché le sue dimensioni non dovrebbero cambiare?
  • In che modo ciò influirà sul confronto tra array?

Voglio solo essere in grado di comprendere profondamente il concetto.


3
Non è strettamente necessario includere informazioni sulla dimensione nel tipo, ma è utile
byxor

Ogni tutorial sugli array spiegherà (1). Non sono sicuro di cosa intendi per (3), poiché non esiste un modo integrato per confrontare le matrici.
HolyBlackCat

Risposte:


20

Cosa succede dietro le quinte?

Un allocazione non dinamica è, per definizione, un contenitore a dimensione fissa di elementi omogenei. Una matrice di Nelementi di tipo Tè disposta in memoria come una sequenza contigua di Noggetti di tipo T.


Perché è necessario che l'array abbia un tipo che includa la sua dimensione?

Non credo che sia "necessario" che il tipo di un array includa le sue dimensioni - è un dato di fatto, è possibile utilizzare un puntatore per fare riferimento a una sequenza contigua di Toggetti. Tale puntatore perderebbe le informazioni sulla dimensione dell'array.

È, tuttavia, una cosa utile da avere. Migliora la sicurezza dei tipi e codifica informazioni utili in fase di compilazione che possono essere utilizzate in più modi. Ad esempio, è possibile utilizzare riferimenti ad array per sovraccaricare array di dimensioni diverse

void foo(int(&array)[4]) { /* ... */ }
void foo(int(&array)[8]) { /* ... */ }

o per capire la dimensione di un array come espressione costante

template <typename T, std::size_t N>
constexpr auto sizeOf(const T(&array)[N]) { return N; }

In che modo ciò influirà sul confronto tra array?

In realtà no.

Non è possibile confrontare matrici di tipo C nello stesso modo in cui si confronterebbero due numeri (ad es. intOggetti). Dovresti scrivere una sorta di confronto lessicografico e decidere cosa significa per raccolte di dimensioni diverse. std::vector<T>prevede che e la stessa logica possa essere applicata agli array.


Bonus: C ++ 11 e versioni successive forniscono std::arrayun wrapper attorno a un array in stile C con un'interfaccia simile a un contenitore. Dovrebbe essere preferito agli array in stile C poiché è più coerente con altri contenitori (ad es. std::vector<T>) E supporta anche confronti lessicografici pronti all'uso .


2
"Dovresti scrivere una sorta di confronto lessicografico e decidere cosa significa per raccolte di dimensioni diverse." È possibile utilizzare std::equal(tramite std::begine std::endquali sono definiti per gli array). In tal caso, matrici di dimensioni diverse non sono uguali.
Stu,

3
Potrebbe valere la pena notare che in base all'intervallo per i loop il cui intervallo è un array è necessario leggere le dimensioni dell'array al momento della compilazione: è un po 'più sottile poiché (per fortuna!) In questo esempio non si scrive mai il tipo di array, ma sembra venire molto più del sovraccarico in base alle dimensioni.
Milo Brandt,

8

La quantità di spazio allocata a un oggetto quando lo si crea dipende interamente dal suo tipo. L'allocazione di cui sto parlando non è allocazioni da newo malloc, ma lo spazio allocato in modo da poter eseguire il costruttore e inizializzare l'oggetto.

Se hai una struttura definita come (ad esempio)

struct A { char a, b; }; //sizeof(A) == 2, ie an A needs 2 bytes of space

Quindi quando costruisci l'oggetto:

A a{'a', 'b'};

Puoi pensare al processo di costruzione dell'oggetto come a un processo:

  • Allocare 2 byte di spazio (nello stack, ma dove non importa per questo esempio)
  • Esegui il costruttore dell'oggetto (in questo caso copia 'a'e 'b'sull'oggetto)

È importante notare che i 2 byte di spazio necessari sono interamente determinati dal tipo di oggetto, gli argomenti della funzione non contano. Quindi, per un array il processo è lo stesso, tranne ora che la quantità di spazio necessaria dipende dal numero di elementi nell'array.

char a[] = {'a'}; //need space for 1 element
char b[] = {'a', 'b', 'c', 'd', 'e'}; //need space for 5 elements

Quindi i tipi di ae bdevono riflettere il fatto che ha abisogno di spazio sufficiente per 1 carattere e ha bbisogno di spazio sufficiente per 5 caratteri. Ciò significa che la dimensione di questi array non può cambiare improvvisamente, una volta creato un array a 5 elementi è sempre un array a 5 elementi. Per avere oggetti simili a "array" in cui le dimensioni possono variare, è necessario l'allocazione dinamica della memoria, che il libro dovrebbe essere coperto ad un certo punto.


0

È per un motivo interno per la libreria di runtime. Se si considerano le seguenti dichiarazioni, ad esempio:

unsigned int i;
unsigned int *iPtr;
unsigned int *iPtrArr[2];
unsigned int **iPtrHandle;

Quindi diventa chiaro quale sia il problema: ad esempio, l'indirizzamento di unsigned int *deve riguardare sizeof operatoro l'indirizzamento di unsigned int.

C'è una spiegazione più dettagliata per il resto di ciò che vedi qui, ma è in gran parte una ricapitolazione di ciò che è stato trattato nel linguaggio di programmazione C, 2a edizione di Kernighan e Ritchie riguardo al programma che stampa il testo in chiaro del tipo dichiarato corda.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.