In che modo C conosce i limiti di un array multidimensionale?

0

Provenendo da Python, se C non ha limiti di matrice, come fa a sapere dove inizia [1]?

int a[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
b = a[1][1];
    
posta Philip Ridout 12.02.2017 - 10:36
fonte

4 risposte

5

C conosce il limite di un array in tempo di compilazione , al contrario di Python che conosce il limite di un array in runtime .

Il compilatore fondamentalmente riscrive l'accesso dell'array nelle operazioni del puntatore. Sa al momento della compilazione che la dimensione di una prima dimensione dell'array è int[3] , cioè sizeof(int)*3 . Quindi:

b = a[x][y];

Viene sostanzialmente riscritto in

b = *(a + x * 3 + y);

Quindi la conoscenza della dimensione (il numero 3) è codificata nel codice, anche se le dimensioni non sono esplicitamente disponibili in fase di runtime. Probabilmente, gli array sono puramente un costrutto in fase di compilazione in C.

    
risposta data 12.02.2017 - 13:17
fonte
1

C memorizza il tuo array 2D come un blocco contiguo di 9 numeri interi:

1 2 3 4 5 6 7 8 9 

C utilizza il tipo di tempo di compilazione e le informazioni sulle dimensioni per generare l'aritmetica del puntatore per accedere agli elementi.

Nel tuo esempio a[i] è un puntatore all'inizio della riga i -th. Sapendo che a è un array 2D di int , e sapendo che l'ultima dimensione ha una dimensione di 3 , può determinare che a[i] è un array di 3 int a partire da i*3 th intero in a.

1 2 3 4 5 6  | 7 8 9 |
             ^
             Start of a[2]

Allo stesso modo, per accedere a a[i][j] il compilatore genererà il codice che usa il 3*i+j th intero in a.

Tuttavia C ritiene che il programmatore sappia cosa sta facendo. Non controllerà i limiti. Se i sarebbe 5 e j 120, il codice verrebbe eseguito alla cieca, causando comportamenti indefiniti come ad esempio corruzione della memoria, core dump e altre cose brutte.

Quindi se i limiti sono importanti per il tuo algoritmo, dovresti utilizzare limiti fissi o passare i limiti come parametro aggiuntivo alla tua funzione.

Se dovessi usare C ++ anziché C, potresti usare i vettori, che sono matrici dinamiche che ne conoscono le dimensioni (ma sarebbe comunque compito tuo controllare i limiti)

    
risposta data 12.02.2017 - 11:26
fonte
0

C sa perché conosce la dimensione di un int . Quindi array[1] inizia una dimensione int in aumento da array[0] . Lo stesso vale per array[1][1]

| size of an int |  
01011101 01101110 10...  
^                 ^  
array[0]          array[1]  

Si noti che la dimensione di un int dipende dalla piattaforma, questo era solo un esempio.

    
risposta data 12.02.2017 - 10:53
fonte
0

C sa dove si trova il successivo spazio di archiviazione per un determinato tipo per l'imballaggio al momento della dichiarazione, quindi se si dispone di un array di interi a 16 bit l'indice "successivo" in un array a dimensione singola sarà, di solito, due byte indirizzo più alto ma su alcune macchine e con alcune impostazioni del compilatore potrebbero essere i primi 2 byte della successiva parola a 64 bit.
Per un array 2D il successivo nell'altro senso normalmente sarebbe la lunghezza della singola, inferiore, dimensione come dichiarato volte il passo per i membri adiacenti dell'array di ordine inferiore, possibilmente più qualche padding . Per un array 3D, il prossimo elemento nella 3a Dimensione dovrebbe normalmente essere la dimensione dell'array 2D nell'ordine inferiore, possibilmente ancora più un padding.

La buona notizia è che il compilatore si prende cura di questo se si utilizzano i normali meccanismi di indicizzazione, il male è che, poiché il meccanismo non è specificato nella specifica ANSI, spetta al produttore del compilatore e come questo è fatto e / o controllato. Se si decide di provare ad accedere direttamente ai dati in tali strutture, è probabile che si verifichino problemi importanti. Come risultato di questa ambiguità di memorizzare tali dati o di scambiarli tra programmi che sono eventualmente compilati con un compilatore diverso, o anche lo stesso compilatore ma con impostazioni diverse , su una singola macchina diventa un incubo e dati problemi come la dimensione dei componenti di base e l'endianness che scambiano dati tra macchine o persino tempi lo diventano ancora di più.

Questo è il motivo per cui le buone specifiche di archiviazione e scambio dei dati sono così prolisse e spesso includono la necessità di impacchettare i dati in strutture specifiche.

Ho avuto un problema diversi anni fa in cui i dati venivano ricevuti come bitfield lungo 24 bit (in un'applicazione di telecomunicazioni), ma sulla macchina Solaris che usavamo, con il compilatore predefinito, anziché essere lunghi 3 byte questa struttura era 192 byte. Questo perché, per la velocità, persino un int: 1 è stato memorizzato in una parola a 64 bit. Diverse ore di ricerche sulle impostazioni del compilatore hanno mostrato che il controllo di questa modifica tra le versioni del compilatore e alla fine passavamo a gcc. Consulta il manuale di gcc per gli attributi di tipo qui per ulteriori informazioni.

Tieni presente che C non ha di per sé alcun limite di array e non tutti i compilatori controllano le dimensioni dell'array. Di conseguenza se un array è definito come int MyArray[8][12][20]; in un modulo ma un altro lo mostra come extern int MyArray[]; , il secondo modulo imposterà felicemente MyArray[50000] = 0 con i problemi risultanti.

    
risposta data 12.02.2017 - 11:58
fonte

Leggi altre domande sui tag