Implementazione del supporto di array di lunghezza non fissa in un compilatore

6

Sto pensando di costruire una lingua per microcontrollori PIC. Voglio poter usare matrici di dimensioni non fisse, come questa:

  1. Dichiara la variabile come int[]
  2. Attendi input dalla connessione seriale
  3. Rendi la variabile input lunga

Penso che una funzione del genere sarebbe utile, ma non so come dovrei andare a implementarla in un compilatore che si compila all'assemblaggio. Voglio che gli array vengano memorizzati su indirizzi di registro consecutivi, ovviamente. Dato che sto lavorando con i PIC, questo deve essere molto efficiente in termini di memoria.

Quando qualcuno scrive int[] , non penso che sia una buona idea riservare ancora spazio alla memoria per la variabile, vero? Quindi la matrice avrà una dimensione massima fissa. Ad esempio, quando l'array riceve l'indirizzo di memoria 5-100 e altre variabili ottengono 4 e 101, l'array ha i bordi fissi e non può crescere più di 96 registri. Inoltre, quando prenoterei la memoria dall'inizio, diciamo x byte, e alla fine ho solo bisogno di y byte, sto sprecando x - y byte. Non voglio quello.

Questo significa che l'unica opzione che vedo è inizializzare l'array e riservare spazio nel microcontrollore, al volo. Questo richiederà un po 'di memoria e tempo di esecuzione, ovviamente. Ho pensato a un sistema come questo:

  • Inizializza un array int[ x ] = {int, int} che contiene puntatori all'inizio e alla fine degli array che non sono inizializzati dall'inizio - x sarebbe la quantità massima di matrici (questa è una concessione, ma è migliore di una lunghezza massima per tutti gli array)
  • Salva una variabile c = 0 per indicare il numero di array utilizzati
  • Memorizza i bordi della memoria inizializzata (riservata) in una variabile da qualche parte
  • Quando una matrice ottiene una lunghezza:

    • Metti puntatori all'inizio (il bordo corrente) e fine (il bordo corrente + la lunghezza) nella matrice dal primo punto all'indice c
    • Incremento c

Penso che funzionerebbe (no?), ma ci sono alcuni svantaggi, principalmente riguardanti la memoria: ho bisogno di memorizzare l'array, c e la memoria corrente configura come overhead.

Ci sarebbe un modo migliore per implementare matrici di dimensioni non fisse in una lingua per microcontrollori PIC? I miei requisiti sono:

  • Carico di memoria insufficiente
  • La lunghezza dell'array non deve essere modificata al volo
  • Con il sistema che ho ideato, non è possibile memorizzare valori negli array che non sono ancora stati inizializzati. Se ci fosse un sistema in grado di memorizzare valori in una matrice di lunghezza indefinita, sarebbe un vantaggio
  • I sistemi più veloci (in fase di esecuzione, la compilazione non ha importanza) sono preferibili
posta Keelan 09.05.2013 - 16:32
fonte

2 risposte

3

Probabilmente lo renderà molto più facile su te stesso ed eviterai molti errori utente se permetti che la dichiarazione di array di lunghezza variabile sia ritardata fino a un punto in cui la dimensione è nota. Quindi non devi inventare una sintassi per assegnare la dimensione dell'array e non devi affrontare le differenze nell'ordine in cui gli array sono dichiarati e in cui ottengono le loro dimensioni.

Se il compilatore utilizza uno schema di allocazione basato su stack per le variabili locali (vengono assegnati indirizzi relativi allo stack-frame della loro funzione, come avviene per le piattaforme "grandi"), gli array a lunghezza variabile possono solo essere scolpito fuori dal telaio della pila, se necessario. Solo se ci sono più array di lunghezza variabile in una funzione hai bisogno di un puntatore aggiuntivo per indicare dove iniziano i dati di ciascuno.

Se il compilatore utilizza uno schema di allocazione fisso (tutte le variabili, inclusi i locali, ricevono un indirizzo fisso (assoluto) dal compilatore / linker), userei un 'array stack' per intagliare gli array a lunghezza variabile da. Come overhead, avresti bisogno di un puntatore globale per indicare dove inizia lo spazio libero correntemente, un puntatore per ogni array a lunghezza variabile per indicare dove si trovano i suoi dati e, in ogni funzione che utilizza array a lunghezza variabile, un modo per ripristinare il globale puntatore al valore che aveva quando si immette la funzione.
L'utente avrebbe anche bisogno di un modo per indicare al compilatore / linker quanta parte di un "array stack" che pensa sarà necessario. Un valore ragionevole potrebbe essere quello di utilizzare tutto ciò che è rimasto dopo aver assegnato tutte le variabili a dimensione fissa e l'overhead di array a lunghezza variabile.

Penso che questo schema usi il minor ammontare di spese generali, sia nello spazio che nel tempo. Lo schema che hai ideato è essenzialmente un'implementazione malloc .

    
risposta data 09.05.2013 - 17:39
fonte
2

Le matrici di lunghezza variabile in C99 sono ancora a dimensione fissa; semplicemente non conosci la dimensione fino al runtime. Non sono dinamici, quindi non puoi dichiarare int foo[] e iniziare ad accedere agli elementi. Non c'è modo di sapere quanto spazio dedicare ad esso e non c'è magia dietro le quinte che riorganizza tutto man mano che l'array cresce.

Nelle versioni precedenti di C, il compilatore eseguiva una singola mossa del puntatore dello stack all'inizio dello scope perché tutte le variabili erano dichiarate in primo piano:

{
  int foo;
  int bar;
  // Stack pointer gets nudged 2 * sizeof(int)
  ... Code ...
}

Ciò che rende possibile l'VLA era una modifica in C99 che consentiva la dichiarazione di variabili ovunque nel codice anziché solo nella parte superiore dello scope:

{
  int foo;  // Stack pointer gets nudged sizeof(int)
   ... Code ...
  int bar;  // Stack pointer gets nudged another sizeof(int)
}

Essere in grado di fare questo significa che per creare un VLA, tutto ciò che deve essere fatto è valutare l'espressione tra parentesi (che può usare variabili dichiarate in precedenza perché il compilatore sa dove trovarle), moltiplicare il risultato per la dimensione del tipo di array e spinge il puntatore dello stack di più di tanto:

{
  size_t size;  // Stack pointer gets nudged sizeof(size_t)
  size = get_number_of_ints_in_input();
   ... Code ...
  int data[size];  // Stack pointer gets nudged size * sizeof(int)
   ... Code ...
}

Se si tiene traccia di quanto è stato spostato il puntatore dello stack, è possibile tornare indietro dove dovrebbe essere ogni volta che si esce dal campo di applicazione.

    
risposta data 09.05.2013 - 18:48
fonte

Leggi altre domande sui tag