2015-12-13 33 views
12

Finora ci ho pensato, ma dopo aver appreso che il compilatore potrebbe eseguire il rilievo dei dati per allinearlo ai requisiti di architettura, ad esempio, sono in dubbio. Quindi mi chiedo se uno char[4][3] abbia lo stesso layout di memoria di char[12]. Il compilatore può inserire il padding dopo la parte char[3] per renderlo allineato in modo che l'intero array impieghi effettivamente 16 byte?È garantito che il tipo T [x] [y] abbia lo stesso layout di memoria di T [x * y] in C?

La storia di fondo che una funzione di una biblioteca prende un mucchio di stringhe di lunghezza fissa in un parametro char* quindi si aspetta un buffer continuo senza paddig, e la lunghezza della stringa può essere dispari. Quindi ho pensato di dichiarare un array char[N_STRINGS][STRING_LENGTH], quindi popolarlo comodamente e passarlo alla funzione assegnandolo a char*. Finora sembra funzionare. Ma non sono sicuro che questa soluzione sia portatile.

+4

L'array viene assegnato in modo contiguo. Non ci può essere alcun padding tra gli elementi dell'array IMHO. – ameyCU

+4

Gli array C devono essere contigui, senza padding tra gli elementi dell'array. Quindi né 'char [4] [3]' né 'char [12]' potrebbe contenere padding, e 'sizeof' sarà' 12 * sizeof (char) 'per entrambi. –

+3

Vedere anche [Gli array C possono contenere spaziatura tra gli elementi?] (Http://stackoverflow.com/questions/1066681/can-c-arrays-contain-padding-in-between-elements) –

risposta

3

Quello a cui ti riferisci come tipi non sono tipi. Il tipo T che hai citato nel titolo sarebbe (in questo caso) un puntatore a un carattere.

Sei corretto che quando si tratta di strutture, l'allineamento è un fattore che può portare all'aggiunta di padding, il che può significare che la tua struct prende più byte di quanto sembri.

Detto questo, quando si assegna un array, l'array sarà contiguo in memoria. Ricordare che quando si esegue l'indicizzazione in un array, array[3] equivale a *(array + 3).

Ad esempio, il seguente programma dovrebbe stampare 12:

#include <stdio.h> 

int main() { 
    char array[4][3]; 
    printf("%zu", sizeof(array)); 
    return 0; 
} 
+1

Gli array sono * sempre * contigui nella memoria, questa è l'intera idea. –

+3

Il 'T' nel titolo è un tipo, non una variabile. Il resto della domanda usa 'char' per' T'. – interjay

+0

@jens Buon punto, il mio uso di "staticamente" non era necessario e più probabilmente ingannare il lettore nel considerare una dicotomia inesistente. – fvgs

-4

rigor una matrice 2-D è un array di puntatori ad array 1-D. In generale non puoi assumere più di quello.

sarei del parere che, se si desidera un blocco contiguo di di qualsiasi tipo allora dichiarano un blocco 1D contigue, piuttosto che sperando in qualsiasi layout particolare dal compilatore o runtime.

Ora un compilatore probabilmente allocherà un blocco contiguo per un array 2-D quando conosce in anticipo le dimensioni (cioè sono costanti al momento della compilazione), ma non è l'interpretazione rigorosa.

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

Questo char **argv è un array di puntatori a char puntatori.

Nella programmazione più generale è possibile ad es. malloc() ogni riga in un array 2D separatamente e lo scambio di riga è semplice come scambiare i valori con quei puntatori. Ad esempio:

char **array = NULL ; 

array = malloc(2 * sizeof(char *)) ; 

array[0] = malloc(24) ; 

array[1] = malloc(11) ; 

strcpy(array[0], "first") ; 
strcpy(array[1], "second") ; 

printf("%s\n%s\n", array[0], array[1]) ; 

/* swap the rows */ 

char *t = array[0] ; 
array[0] = array[1] ; 
array[1] = t ; 

printf("%s\n%s\n", array[0], array[1]) ; 

free(array[0]) ; 
free(array[1]) ; 
free(array) ; 
+0

Data la natura della domanda, è importante notare qui che l'array di caratteri dato da 'array [0]' non è contiguo con l'array di caratteri dato da 'array [1]'. Inoltre, non è consigliabile allocare dinamicamente gli array se possono essere allocati staticamente. – fvgs

+8

"_ La matrice 2-D è una matrice di puntatori agli array 1-D." No, non lo è! Gli array _ ** non sono ** _ puntatori. – edmz

+3

Non promuovere matrici pseudo 2D, ovvero puntatori a puntatori. Questo è molto sbagliato e dovrebbe essere davvero bandito da tutti i libri e dai corsi introduttivi. –

10

Un array di M elementi di tipo A ha tutti i suoi elementi in posizioni contigue nella memoria, senza byte di riempimento affatto. Questo fatto non dipende dalla natura di A.

Ora, se A è il tipo "matrice di elementi N di tipo T", quindi ogni elemento nell'array di tipo T avrà, nuovamente, N posizioni contigue in memoria. Tutti questi blocchi di oggetti N di tipo T sono, inoltre, memorizzati in posizioni contigue.

Quindi, il risultato è l'esistenza in memoria di elementi M * N di tipo T, memorizzati in posizioni contigue.

L'elemento [i][j] della matrice è memorizzato nella posizione i*N+j.

8

Consideriamo

T array[size]; 
array[0]; // 1 

1 è formalmente definito come:

La definizione di operatore pedice [] è che E1[E2] è identico a (*((E1)+(E2)))

al punto 6.5 .2.1, clausola 2 tratta dalla bozza C N1570 standard. Quando viene applicato agli array multidimensionali, «matrice i cui elementi sono array», si ha:

Se E è un array n-dimensionale (n ≥ 2) con dimensioni i × j × ... × k, allora E (utilizzato come diverso da un lvalue) viene convertito in un puntatore a una (n - 1) -dimensionale matrice di dimensioni j × . . . × k.

Pertanto, dato E = T array[i][j] e S = array[i][j], S viene prima convertito in un puntatore a una matrice unidimensionale di dimensione j, ovvero T (*ptr)[j] = &array[i].

Se l'operatore * unario viene applicata a questo puntatore esplicitamente o implicitamente come risultato di indicizzazione di, il risultato è il riferimento (n - 1) -dimensionale matrice, che è essa stessa convertito in un puntatore se usato come diverso da un lvalue.

e questa regola si applica in modo ricorsivo. Possiamo concludere che, per fare ciò, l'array n-dimensional deve essere allocato in modo contiguo.

Da ciò deriva che gli array vengono memorizzati in ordine di riga maggiore (l'ultimo indice varia più velocemente).

in termini di layout logico.

Poiché char [12] deve essere memorizzato in modo contiguo e quindi deve essere char [3][4] e poiché hanno lo stesso allineamento, dovrebbero essere compatibili, nonostante siano tipi tecnicamente diversi.

+1

Il punto centrale del mio dubbio è: Le implementazioni sono libere di eseguire il * fine * di la matrice per arrotondare fino all'allineamento? Se la risposta è sì, immagino che 'sizeof (char [3]) == 4' su una CPU allineata a una parola, che rende più semplice l'indirizzamento di elementi di una matrice' char [4] [3] '. Ciò significa che i sub-array contengono un riempimento implicito mentre se si utilizza 'char [12]' l'implementazione è necessaria per comprimere i dati senza riempimento. – Calmarius

+0

Se stiamo parlando di allineamento, '_Alignof (char [N])' deve essere '_Alignof (char)', che è 1. Poiché 'sizeof (char [3]) == 3', devi ripensare alle tue domande poiché il punto di partenza non è corretto. – edmz

+0

@black: Dato 'struct x {char arr [3] [4]; }; struct y {char arr [4] [3]; }; 'lo standard garantisce che' _Alignof (struct x) 'e' _Alignof (struct y) 'sarebbe uguale? – supercat