Perché le matrici C non tengono traccia della loro lunghezza?


77

Qual è stato il ragionamento alla base della non memorizzazione esplicita della lunghezza di un array con un array in C?

Per come la vedo io, ci sono ragioni schiaccianti per farlo, ma non molti a supporto dello standard (C89). Per esempio:

  1. La disponibilità della lunghezza in un buffer può impedire il sovraccarico del buffer.
  2. Uno stile Java arr.lengthè chiaro ed evita al programmatore di mantenere molte ints nello stack se si occupa di più array
  3. I parametri di funzione diventano più convincenti.

Ma forse la ragione più motivante, secondo me, è che di solito nessuno spazio viene salvato senza mantenere la lunghezza. Mi azzarderei a dire che la maggior parte degli usi degli array implica allocazione dinamica. È vero, ci possono essere alcuni casi in cui le persone usano un array allocato nello stack, ma questa è solo una chiamata di funzione *: lo stack può gestire 4 o 8 byte in più.

Poiché il gestore heap deve tenere traccia della dimensione del blocco libero utilizzata dall'array allocato in modo dinamico, perché non rendere utilizzabili tali informazioni (e aggiungere la regola aggiuntiva, verificata al momento della compilazione, che non si può manipolare esplicitamente la lunghezza a meno che non si vorrebbe piace spararsi al piede).

L'unica cosa che mi viene in mente sul lato opposto è che nessun inseguimento lunghezza può aver fatto compilatori più semplice, ma non che molto più semplice.

* Tecnicamente, si potrebbe scrivere una sorta di funzione ricorsiva con un array con memorizzazione automatica, e in questo caso (molto elaborato) l'archiviazione della lunghezza può effettivamente comportare un maggiore utilizzo dello spazio.


6
Suppongo che si possa sostenere che quando C includeva l'utilizzo di struct come parametro e restituisce tipi di valore, avrebbe dovuto includere lo zucchero sintattico per i "vettori" (o qualunque nome), che sotto sarebbe stato strutturato con lunghezza e array o puntatore a array . Il supporto a livello di linguaggio per questo costrutto comune (anche se passato come argomenti separati e non una singola struttura) avrebbe salvato innumerevoli bug e semplificato anche la libreria standard.
hyde,

3
Potresti anche scoprire perché Pascal non è il mio linguaggio di programmazione preferito Sezione 2.1 per essere perspicace.

34
Mentre tutte le altre risposte hanno alcuni punti interessanti, penso che la linea di fondo sia che C è stato scritto in modo che i programmatori di linguaggio assembly possano scrivere il codice più facilmente e renderlo portatile. Con questo in mente, avere una lunghezza di array memorizzata CON un array automaticamente sarebbe stato un fastidio e non un difetto (come avrebbero avuto altri bei desideri di rivestimento di caramelle). Al giorno d'oggi queste funzionalità sembrano interessanti, ma a quei tempi era spesso difficile spremere un altro byte di programma o dati nel sistema. Uno spreco di memoria avrebbe fortemente limitato l'adozione di C.
Dunk,

6
La parte reale della tua risposta ha già ricevuto molte risposte nel modo in cui avrei dovuto, ma posso estrarre un punto diverso: "Perché non è malloc()possibile richiedere le dimensioni di un'area ed in modo portatile?" Questa è una cosa che mi fa meravigliare più volte.
glglgl,

5
Votazione per riaprire. C'è qualche motivo da qualche parte, anche se è semplicemente "K&R non ci ha pensato".
Telastyn,

Risposte:


106

Le matrici C tengono traccia della loro lunghezza, poiché la lunghezza della matrice è una proprietà statica:

int xs[42];  /* a 42-element array */

Di solito non è possibile eseguire query su questa lunghezza, ma non è necessario perché è comunque statico: basta dichiarare una macro XS_LENGTHper la lunghezza e il gioco è fatto.

Il problema più importante è che gli array C si degradano implicitamente in puntatori, ad esempio quando vengono passati a una funzione. Questo ha un senso e consente alcuni trucchi di basso livello, ma perde le informazioni sulla lunghezza dell'array. Quindi una domanda migliore sarebbe perché C è stato progettato con questo implicito degrado dei puntatori.

Un'altra questione è che i puntatori non necessitano di archiviazione tranne l'indirizzo di memoria stesso. C ci consente di trasmettere numeri interi a puntatori, puntatori ad altri puntatori e di trattare i puntatori come se fossero matrici. Nel fare questo, C non è abbastanza folle da fabbricare una certa lunghezza dell'array, ma sembra fidarsi del motto di Spiderman: con grande potenza il programmatore si spera possa adempiere alla grande responsabilità di tenere traccia delle lunghezze e degli overflow.


13
Penso che intendi dire, se non sbaglio, che i compilatori C tengono traccia delle lunghezze statiche dell'array. Ma questo non serve a funzioni che ottengono solo un puntatore.
VF1,

25
@ VF1 sì. Ma l' importante cosa è che gli array e puntatori sono cose diverse in C . Supponendo che non si stia utilizzando alcuna estensione del compilatore, in genere non è possibile passare un array stesso a una funzione, ma è possibile passare un puntatore e indicizzare un puntatore come se fosse un array. Ti stai effettivamente lamentando che i puntatori non hanno lunghezza allegata. Dovresti lamentarti del fatto che le matrici non possono essere passate come argomenti di funzione o che le matrici si degradano implicitamente in puntatori.
amon,

37
"Di solito non è possibile interrogare questa lunghezza" - in realtà è possibile, è la dimensione dell'operatore - sizeof (xs) restituirebbe 168 supponendo che int sia lungo quattro byte. Per ottenere il 42, fai: sizeof (xs) / sizeof (int)
tcrosley,

15
@tcrosley Funziona solo nell'ambito della dichiarazione dell'array - prova a passare xs come parametro a un'altra funzione, quindi guarda quale sizeof (xs) ti dà ...
Gwyn Evans,

26
@GwynEvans di nuovo: i puntatori non sono array. Quindi, se "passi un array come parametro a un'altra funzione", non stai passando un array ma un puntatore. Affermare che sizeof(xs)dove si xstrova un array sarebbe qualcosa di diverso in un altro ambito è palesemente falso, perché il design di C non consente agli array di lasciare il loro ambito. Se sizeof(xs)where xsis a array è diverso da sizeof(xs)where xsis a pointer, non sorprende perché stai confrontando le mele con le arance .
amon,

38

Molto di ciò ha avuto a che fare con i computer disponibili al momento. Non solo il programma compilato doveva essere eseguito su un computer con risorse limitate, ma, forse ancora più importante, il compilatore stesso doveva essere eseguito su queste macchine. All'epoca Thompson sviluppò C, stava usando un PDP-7, con 8k di RAM. Le funzioni linguistiche complesse che non avevano un analogo immediato sul codice macchina effettivo non erano semplicemente incluse nella lingua.

Una lettura attenta della storia di C offre una maggiore comprensione di quanto sopra, ma non era interamente il risultato dei limiti della macchina che avevano:

Inoltre, il linguaggio (C) mostra un notevole potere nel descrivere concetti importanti, ad esempio vettori la cui lunghezza varia in fase di esecuzione, con solo alcune regole e convenzioni di base. ... È interessante confrontare l'approccio di C con quello di due lingue quasi contemporanee, Algol 68 e Pascal [Jensen 74]. Le matrici in Algol 68 hanno limiti fissi o sono "flessibili:" è necessario un considerevole meccanismo sia nella definizione della lingua che nei compilatori, per adattarsi alle matrici flessibili (e non tutti i compilatori le implementano completamente). L'originale Pascal aveva solo dimensioni fisse array e stringhe, e questo si è rivelato limitato [Kernighan 81].

Le matrici C sono intrinsecamente più potenti. L'aggiunta di limiti ad essi limita ciò per cui il programmatore può usarli. Tali restrizioni possono essere utili per i programmatori, ma necessariamente sono anche limitanti.


4
Questo praticamente inchioda la domanda originale. Questo e il fatto che C venisse tenuto deliberatamente "leggero tocco" quando si trattava di verificare cosa stesse facendo il programmatore, al fine di renderlo attraente per la scrittura di sistemi operativi.
ClickRick,

5
Ottimo collegamento, hanno anche cambiato esplicitamente la memorizzazione della lunghezza delle stringhe per usare un delimitatore to avoid the limitation on the length of a string caused by holding the count in an 8- or 9-bit slot, and partly because maintaining the count seemed, in our experience, less convenient than using a terminator- beh tanto per quello :-)
Voo

5
Le matrici non terminate si adattano anche all'approccio bare metal di C. Ricorda che il libro K&R C ha meno di 300 pagine con un tutorial di lingua, un riferimento e un elenco delle chiamate standard. Il mio libro di O'Reilly Regex è quasi il doppio di K&R C.
Michael Shopsin

22

Ai tempi della creazione di C e 4 byte in più di spazio per ogni stringa, non importa quanto breve sarebbe stato un vero spreco!

C'è un altro problema: ricorda che C non è orientato agli oggetti, quindi se fai il prefisso lunghezza tutte le stringhe, dovrebbe essere definito come un tipo intrinseco del compilatore, non un char*. Se fosse un tipo speciale, non saresti in grado di confrontare una stringa con una stringa costante, ovvero:

String x = "hello";
if (strcmp(x, "hello") == 0) 
  exit;

dovrebbe avere particolari dettagli del compilatore per convertire quella stringa statica in una stringa o avere funzioni di stringa diverse per tenere conto del prefisso lunghezza.

Penso che alla fine, però, non hanno semplicemente scelto il prefisso di lunghezza a differenza di come dice Pascal.


10
Anche il controllo dei limiti richiede tempo. Triviale nei termini di oggi, ma qualcosa a cui la gente ha prestato attenzione quando si è preoccupato di circa 4 byte.
Gort il robot,

18
@StevenBurnap: non è così banale anche oggi se sei in un ciclo interno che supera ogni pixel di un'immagine da 200 MB. In generale, se stai scrivendo C vuoi andare veloce , e non vuoi perdere tempo in un inutile controllo associato ad ogni iterazione quando il tuo forciclo era già impostato per rispettare i confini.
Matteo Italia,

4
@ VF1 "back in the day" potrebbe benissimo essere stato di due byte (DEC PDP / 11 chiunque?)
ClickRick

7
Non è solo "back in the day". Il per il software a cui C è destinato come "linguaggio assembly portatile" come kernals del sistema operativo, driver di dispositivo, software in tempo reale incorporato ecc. Ecc. sprecare mezza dozzina di istruzioni sul controllo dei limiti è importante e, in molti casi, devi essere "fuori dai limiti" (come potresti scrivere un debugger se non potessi accedere in modo casuale ad un altro archivio di programmi?).
James Anderson,

3
Questo è in realtà un argomento piuttosto debole considerando che BCPL ha contato a lungo gli argomenti. Proprio come Pascal però era limitato a 1 parola, quindi generalmente solo 8 o 9 bit, il che era un po 'limitante (preclude anche la possibilità di condividere parti di stringhe, anche se quell'ottimizzazione era probabilmente troppo avanzata per il tempo). E dichiarare una stringa come una struttura con una lunghezza seguita dall'array non avrebbe davvero bisogno del supporto speciale del compilatore ..
Voo

11

In C, qualsiasi sottoinsieme contiguo di un array è anche un array e può essere utilizzato come tale. Questo vale sia per le operazioni di lettura che di scrittura. Questa proprietà non verrebbe mantenuta se la dimensione fosse archiviata in modo esplicito.


6
"Il design sarebbe diverso" non è un motivo per cui il design è diverso.
VF1,

7
@ VF1: hai mai programmato in Pascal standard? La capacità di C di essere ragionevolmente flessibile con le matrici è stata un enorme miglioramento rispetto all'assemblaggio (nessuna sicurezza) e alla prima generazione di linguaggi typesafe (sicurezza eccessiva dei caratteri, compresi i limiti esatti dell'array)
MSalters,

5
Questa capacità di suddividere un array è davvero un argomento importante per il design C89.

Gli hacker della vecchia scuola di Fortran possono anche usare bene questa proprietà (anche se richiede il passaggio della porzione a un array in Fortran). Confuso e doloroso da programmare o eseguire il debug, ma veloce ed elegante quando si lavora.
dmckee,

3
Esiste un'interessante alternativa di progettazione che consente il taglio: non conservare la lunghezza lungo le matrici. Per qualsiasi puntatore a un array, memorizzare la lunghezza con il puntatore. (Quando si dispone solo di un array C reale, la dimensione è una costante di tempo di compilazione e disponibile per il compilatore.) Richiede più spazio, ma consente lo slicing mantenendo la lunghezza. Rust fa questo per i &[T]tipi, per esempio.

8

Il problema più grande di avere le matrici taggate con la loro lunghezza non è tanto lo spazio necessario per memorizzare quella lunghezza, né la domanda su come dovrebbe essere memorizzata (l'uso di un byte aggiuntivo per le matrici corte in genere non sarebbe discutibile, né l'utilizzo di quattro byte extra per array lunghi, ma potrebbe essere l'utilizzo di quattro byte anche per array brevi). Un problema molto più grande è che dato un codice come:

void ClearTwoElements(int *ptr)
{
  ptr[-2] = 0;
  ptr[2] = 0;
}
void blah(void)
{
  static int foo[10] = {1,2,3,4,5,6,7,8,9,10};
  ClearTwoElements(foo+2);
  ClearTwoElements(foo+7);
  ClearTwoElements(foo+1);
  ClearTwoElements(foo+8);
}

l'unico modo in cui il codice sarebbe in grado di accettare la prima chiamata ClearTwoElementsma di rifiutare la seconda sarebbe che il ClearTwoElementsmetodo riceva informazioni sufficienti per sapere che in ogni caso stava ricevendo un riferimento a una parte dell'array foooltre a sapere quale parte. Questo in genere raddoppierebbe il costo del passaggio dei parametri del puntatore. Inoltre, se ogni array fosse preceduto da un puntatore a un indirizzo appena oltre la fine (il formato più efficiente per la convalida), il codice ottimizzato per ClearTwoElementsdiventerebbe probabilmente qualcosa del tipo:

void ClearTwoElements(int *ptr)
{
  int* array_end = ARRAY_END(ptr);
  if ((array_end - ARRAY_BASE(ptr)) < 10 ||
      (ARRAY_BASE(ptr)+4) <= ADDRESS(ptr) ||          
      (array_end - 4) < ADDRESS(ptr)))
    trap();
  *(ADDRESS(ptr) - 4) = 0;
  *(ADDRESS(ptr) + 4) = 0;
}

Si noti che un chiamante di metodo potrebbe, in generale, passare perfettamente un puntatore all'inizio dell'array o l'ultimo elemento a un metodo; solo se il metodo tenta di accedere ad elementi che escono dall'array pass-in tali puntatori potrebbero causare problemi. Di conseguenza, un metodo chiamato dovrebbe prima assicurarsi che l'array fosse abbastanza grande da consentire all'aritmetica del puntatore di convalidare i suoi argomenti non andrà oltre i limiti, quindi eseguire alcuni calcoli del puntatore per convalidare gli argomenti. Il tempo impiegato in tale convalida probabilmente supererebbe il costo speso per qualsiasi lavoro reale. Inoltre, il metodo potrebbe probabilmente essere più efficiente se fosse scritto e chiamato:

void ClearTwoElements(int arr[], int index)
{
  arr[index-2] = 0;
  arr[index+2] = 0;
}
void blah(void)
{
  static int foo[10] = {1,2,3,4,5,6,7,8,9,10};
  ClearTwoElements(foo,2);
  ClearTwoElements(foo,7);
  ClearTwoElements(foo,1);
  ClearTwoElements(foo,8);
}

Il concetto di un tipo che combina qualcosa per identificare un oggetto con qualcosa per identificare un suo pezzo è valido. Un puntatore in stile C è più veloce, tuttavia, se non è necessario eseguire la convalida.


Se le matrici avessero dimensioni di runtime, il puntatore all'array sarebbe sostanzialmente diverso dal puntatore a un elemento dell'array. Gli ultimi potrebbero non essere direttamente convertibili in precedenti (senza creare un nuovo array). []la sintassi potrebbe ancora esistere per i puntatori, ma sarebbe diversa rispetto a questi ipotetici array "reali", e il problema che descrivi probabilmente non esisterebbe.
hyde,

@hyde: la domanda è se l'aritmetica dovrebbe essere consentita sui puntatori il cui indirizzo base dell'oggetto è sconosciuto. Inoltre, ho dimenticato un'altra difficoltà: le matrici all'interno delle strutture. Pensandoci, non sono sicuro che ci sarebbe qualcuno con un tipo di puntatore che potrebbe puntare a un array memorizzato all'interno di una struttura, senza richiedere a ciascun puntatore di includere non solo l'indirizzo del puntatore stesso, ma anche legale superiore e inferiore gamme a cui può accedere.
supercat,

Punto di intersezione. Penso che questo riduca ancora alla risposta di Amon, però.
VF1,

La domanda si pone sugli array. Il puntatore è l'indirizzo di memoria e non cambierebbe con la premessa della domanda, per quanto riguarda l'intenzione. Le matrici otterrebbero lunghezza, i puntatori rimarrebbero invariati (eccetto il puntatore all'array dovrebbe essere un tipo nuovo, distinto, unico, molto simile al puntatore alla struttura).
hyde,

@hyde: se si cambiasse sufficientemente la semantica della lingua, potrebbe essere possibile che le matrici includano una lunghezza associata, sebbene le matrici memorizzate all'interno delle strutture possano presentare delle difficoltà. Con la semantica così come sono, il controllo dei limiti dell'array sarebbe utile solo se lo stesso controllo fosse applicato ai puntatori agli elementi dell'array.
supercat,

7

Una delle differenze fondamentali tra C e la maggior parte delle altre lingue di terza generazione, e tutte le lingue più recenti di cui sono a conoscenza, è che C non è stato progettato per rendere la vita più facile o più sicura per il programmatore. È stato progettato con l'aspettativa che il programmatore sapesse cosa stavano facendo e voleva fare esattamente e solo quello. Non fa nulla "dietro le quinte", quindi non si ottengono sorprese. Anche l'ottimizzazione a livello di compilatore è facoltativa (a meno che non si utilizzi un compilatore Microsoft).

Se un programmatore vuole scrivere limiti controllando nel proprio codice, C rende abbastanza semplice farlo, ma il programmatore deve scegliere di pagare il prezzo corrispondente in termini di spazio, complessità e prestazioni. Anche se non lo uso con rabbia da molti anni, lo uso ancora quando insegno alla programmazione per comprendere il concetto di processo decisionale basato sui vincoli. Fondamentalmente, ciò significa che puoi scegliere di fare tutto quello che vuoi, ma ogni decisione che prendi ha un prezzo di cui devi essere consapevole. Questo diventa ancora più importante quando inizi a dire agli altri cosa vuoi che facciano i loro programmi.


3
C non era tanto "progettato" quanto si è evoluto. Inizialmente, una dichiarazione come int f[5];non sarebbe stata creata fcome un array di cinque elementi; invece, era equivalente a int CANT_ACCESS_BY_NAME[5]; int *f = CANT_ACCESS_BY_NAME;. La precedente dichiarazione potrebbe essere elaborata senza che il compilatore debba veramente "capire" i tempi dell'array; doveva semplicemente emettere una direttiva assembler per allocare spazio e poteva quindi dimenticare che fnon aveva mai avuto nulla a che fare con un array. Da ciò derivano i comportamenti incoerenti dei tipi di array.
supercat

1
Si scopre che nessun programmatore sa cosa sta facendo nella misura richiesta da C.
CodesInChaos

7

Risposta breve:

Poiché C è un linguaggio di programmazione di basso livello , si aspetta che tu ti occupi di questi problemi da solo, ma questo aggiunge una maggiore flessibilità esattamente nel modo in cui lo implementi.

C ha un concetto in fase di compilazione di un array che viene inizializzato con una lunghezza ma in fase di esecuzione l'intera cosa viene semplicemente memorizzata come un singolo puntatore all'inizio dei dati. Se si desidera passare la lunghezza dell'array a una funzione insieme all'array, lo si fa da soli:

retval = my_func(my_array, my_array_length);

Oppure potresti usare una struttura con un puntatore e una lunghezza o qualsiasi altra soluzione.

Un linguaggio di livello superiore lo farebbe per te come parte del suo tipo di array. In C ti viene data la responsabilità di farlo da solo, ma anche la flessibilità di scegliere come farlo. E se tutto il codice che stai scrivendo conosce già la lunghezza dell'array, non è necessario passare la lunghezza come variabile.

L'ovvio svantaggio è che senza limiti intrinseci che controllano gli array passati come puntatori è possibile creare un codice pericoloso, ma questa è la natura dei linguaggi di sistema / livello basso e il compromesso che danno.


1
+1 "E se tutto il codice che stai scrivendo conosce già la lunghezza dell'array, non è necessario passare la lunghezza come variabile."
林果皞

Se solo la struttura pointer + length fosse stata inserita nel linguaggio e nella libreria standard. Così tante falle di sicurezza avrebbero potuto essere evitate.
CodesInChaos

Quindi non sarebbe davvero C. Ci sono altre lingue che lo fanno. C ti porta a un livello basso.
thomasrutter,

C è stato inventato come linguaggio di programmazione di basso livello e molti dialetti supportano ancora la programmazione di basso livello, ma molti scrittori di compilatori preferiscono dialetti che non possono essere chiamati linguaggi di basso livello. Consentono e richiedono persino una sintassi di basso livello, ma poi cercano di inferire costrutti di livello superiore il cui comportamento potrebbe non corrispondere alla semantica implicita dalla sintassi.
Supercat,

5

Il problema dell'archiviazione aggiuntiva è un problema, ma a mio avviso un problema minore. Dopotutto, la maggior parte delle volte dovrai comunque tenere traccia della lunghezza, anche se Amon ha sottolineato che spesso può essere tracciato staticamente.

Un problema più grande è dove memorizzare la lunghezza e quanto tempo per farlo. Non esiste un posto che funzioni in tutte le situazioni. Si potrebbe dire semplicemente di archiviare la lunghezza nella memoria appena prima dei dati. Cosa succede se l'array non punta alla memoria, ma qualcosa di simile a un buffer UART?

Lasciare fuori la lunghezza consente al programmatore di creare le proprie astrazioni per la situazione appropriata e ci sono molte librerie già pronte disponibili per il caso generale. La vera domanda è: perché quelle astrazioni non vengono utilizzate in applicazioni sensibili alla sicurezza?


1
You might say just store the length in the memory just before the data. What if the array isn't pointing to memory, but something like a UART buffer?Potresti spiegarlo un po 'di più? Anche qualcosa che potrebbe accadere troppo spesso o è solo un caso raro?
Mahdi,

Se lo avessi progettato, un argomento di funzione scritto come T[]non sarebbe equivalente T*ma piuttosto passa una tupla di puntatore e dimensione alla funzione. Gli array di dimensioni fisse potrebbero decadere in una tale porzione di array, anziché decadere in puntatori come fanno in C. Il vantaggio principale di questo approccio non è che è sicuro da solo, ma è una convenzione su cui tutto, inclusa la libreria standard, può costruire.
CodesInChaos

1

Dallo sviluppo del linguaggio C :

Le strutture, a quanto pare, dovrebbero mappare in modo intuitivo sulla memoria della macchina, ma in una struttura che contiene un array, non c'era un buon posto per riporre il puntatore contenente la base dell'array, né un modo conveniente per organizzare che fosse inizializzato. Ad esempio, le voci di directory dei primi sistemi Unix potrebbero essere descritte in C come
struct {
    int inumber;
    char    name[14];
};
Volevo che la struttura non solo caratterizzasse un oggetto astratto, ma descrivesse anche una raccolta di bit che potevano essere letti da una directory. Dove potrebbe il compilatore nascondere il puntatore namerichiesto dalla semantica? Anche se le strutture fossero pensate in modo più astratto e lo spazio per i puntatori potesse essere nascosto in qualche modo, come potrei gestire il problema tecnico di inizializzare correttamente questi puntatori durante l'allocazione di un oggetto complicato, forse uno che specificava strutture con array contenenti strutture a profondità arbitraria?

La soluzione ha costituito il salto cruciale nella catena evolutiva tra BCPL senza tipizzazione e tipizzata C. Ha eliminato la materializzazione del puntatore in memoria e ha invece causato la creazione del puntatore quando il nome dell'array è menzionato in un'espressione. La regola, che sopravvive nella C odierna, è che i valori di tipo array vengono convertiti, quando compaiono in espressioni, in puntatori al primo degli oggetti che compongono l'array.

Quel passaggio affronta il motivo per cui le espressioni di array decadono in puntatori nella maggior parte dei casi, ma lo stesso ragionamento si applica al motivo per cui la lunghezza dell'array non è memorizzata con l'array stesso; se vuoi un mapping uno a uno tra la definizione del tipo e la sua rappresentazione in memoria (come ha fatto Ritchie), allora non c'è un buon posto per archiviare quei metadati.

Inoltre, pensa alle matrici multidimensionali; dove memorizzeresti i metadati di lunghezza per ogni dimensione in modo tale da poter ancora percorrere l'array con qualcosa del genere

T *p = &a[0][0];

for ( size_t i = 0; i < rows; i++ )
  for ( size_t j = 0; j < cols; j++ )
    do_something_with( *p++ );

-2

La domanda presuppone che ci siano array in C. Non ce ne sono. Le cose che si chiamano array sono solo uno zucchero sintattico per operazioni su sequenze continue di dati e aritmetica dei puntatori.

Il codice seguente copia alcuni dati da src a dst in blocchi di dimensioni int non sapendo che in realtà è una stringa di caratteri.

char src[] = "Hello, world";
char dst[1024];
int *my_array = src; /* What? Compiler warning, but the code is valid. */
int *other_array = dst;
int i;
for (i = 0; i <= sizeof(src)/sizeof(int); i++)
    other_array[i] = my_array[i]; /* Oh well, we've copied some extra bytes */
printf("%s\n", dst);

Perché C è così semplificato da non avere array adeguati? Non conosco la risposta corretta a questa nuova domanda. Ma alcune persone spesso affermano che C è solo (in qualche modo) più leggibile e assemblatore portatile.


2
Non credo che tu abbia risposto alla domanda.
Robert Harvey,

2
Quello che hai detto è vero, ma la persona che chiede vuole sapere perché è così.

9
Ricorda, uno dei soprannomi per C è "assembly portatile". Mentre le versioni più recenti dello standard hanno aggiunto concetti di livello superiore, al suo interno, sono costituiti da semplici costrutti di basso livello e istruzioni comuni nella maggior parte delle macchine non banali. Ciò guida la maggior parte delle decisioni di progettazione prese nella lingua. Le uniche variabili che esistono in fase di esecuzione sono numeri interi, float e puntatori. Le istruzioni includono aritmetica, confronti e salti. Praticamente tutto il resto è un sottile strato costruito sopra a quello.

8
È sbagliato dire che C non ha array, considerando come non si possa davvero generare lo stesso binario con altri costrutti (beh, almeno non se si considera l'uso di #define per determinare le dimensioni dell'array). Le matrici in C sono "sequenze continue di dati", nulla di zuccherino al riguardo. Usare i puntatori come fossero matrici è lo zucchero sintattico qui (anziché l'aritmetica puntatore esplicita), non le matrici stesse.
hyde,

2
Sì, si consideri questo codice: struct Foo { int arr[10]; }. arrè un array, non un puntatore.
Gort il robot il
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.