Come sono organizzati gli operatori in memoria

2

In che modo gli operatori sono organizzati / salvati nella memoria nel contesto di un linguaggio di programmazione. Le procedure / le funzioni vengono salvate da qualche parte e i compilatori manipolano semplicemente le cose per chiamare questi proc ogni volta che gli operatori vengono utilizzati nel programma?

    
posta kishu27 11.09.2012 - 17:25
fonte

5 risposte

12

La maggior parte degli operatori sono solo " zucchero sintattico " per funzioni o procedure, quindi puoi guardarli per lo più allo stesso modo :

  • Potrebbero essere elenchi ordinari di istruzioni della macchina memorizzate in un particolare indirizzo da chiamare come una funzione ordinaria. A volte queste funzioni sono effettivamente chiamabili come normali funzioni se si conosce la sintassi corretta, ma in altre situazioni / linguaggi potrebbe essere che il compilatore lo gestisca completamente internamente senza concedere agli sviluppatori l'accesso alla funzionalità attraverso tutto il lessico e il parser del compilatore.
  • A volte il compilatore può sostituire l'operatore nel punto in cui è chiamato, nella sorgente, per evitare il sovraccarico delle istruzioni della macchina necessarie per una chiamata di funzione, inserendo direttamente il codice macchina (es. inline, in alcuni casi ESATTAMENTE come inlining).
  • A volte potresti lavorare in un interprete / runtime, in cui le funzioni sono memorizzate in un linguaggio intermedio che è "interpretato" dal runtime per il codice macchina corretto, che potrebbe anche essere inline ma che dovrebbe comunque essere interpretato .
  • In alcuni casi, in cui gli operandi dell'operatore sono tipi molto vicini o identici all'istruzione del codice macchina, gli operatori possono essere tradotti direttamente in pochissime dichiarazioni. Quante affermazioni dipenderebbero principalmente dal fatto che gli operandi coinvolti siano attualmente memorizzati in registri o memoria (che è un intero argomento del compilatore da solo).
  • In quasi tutti i casi, ciò che effettivamente accade nel compilatore, nell'interprete o nel runtime dipenderà dai tipi di operandi. Un operatore tra due operandi dello stesso tipo finirà per risolvere l'operatore per quel tipo (che può essere o non essere completamente diverso dallo stesso operatore per diversi tipi). Un operatore tra operandi di diverso tipo può essere implementato direttamente (che è raro) o inoltre chiama operatori di conversione su uno o più degli operandi per trasformarli in tipi che possono essere utilizzati, prima di eseguire effettivamente l'operatore stesso.
risposta data 11.09.2012 - 17:55
fonte
3

Il parser lo converte prima in un albero di sintassi astratto . Il tipo di operatore finisce come nodo e gli operandi finiscono come nodi figli. Il compilatore quindi cammina fondamentalmente l'albero di sintassi astratto e fa cose diverse a seconda dei tipi di operandi.

Il più delle volte, il codice dell'operatore sarà abbastanza semplice da essere in linea. Tuttavia, ci sono anche situazioni in cui è abbastanza complesso da creare una chiamata di funzione. Ad esempio, in C ++ puoi sovrascrivere gli operatori sulle classi creando le tue funzioni per gestirli. Solitamente in linguaggi specifici di dominio interpretati, finisce anche come chiamata di funzione perché è il modo più semplice per farlo quando le prestazioni non sono di primaria importanza.

    
risposta data 11.09.2012 - 18:01
fonte
3

whole file and code that it contains is loaded to the memory. Le espressioni riservate come operatori e comandi sono trattate in modi diversi da ciascun compilatore diverso.

Il compilatore svolge il ruolo più importante in questo caso. I compilatori possono agire in modo diverso a seconda del linguaggio di programmazione e della versione che stai utilizzando.

Per informazioni più dettagliate ecco un bell'articolo da cercare - Operatore (programmazione) - Wiki

    
risposta data 11.09.2012 - 17:34
fonte
1

Nei linguaggi compilati, intere espressioni, inclusi gli operatori che contengono, vengono trasformate in codice binario corrispondente al calcolo rappresentato dall'espressione. Un operatore viene tradotto in una sequenza di una o più istruzioni binarie nella CPU, indirizzandolo per eseguire l'operazione richiesta. Ad esempio

a = b + 2;

potrebbe essere convertito in qualcosa del genere:

LOAD  R1, @b
LOAD  R0, #2
ADD   R0, R1
STORE R0, @a

@a e @b rappresentano gli indirizzi delle variabili a e b ; #2 rappresenta il valore di una costante intera 2 .

Se un operatore non corrisponde a una singola istruzione di assemblaggio, un'implementazione del compilatore può scegliere una funzione inline o non inline per implementarla. Ad esempio, in CPU a 8 bit che mancavano di istruzioni di moltiplicazione (e erano a corto di memoria, limitando le opportunità di inlining, ad esempio 6502), la moltiplicazione e la divisione sono state comunemente implementate come subroutine.

Nelle lingue interpretate gli operatori sono memorizzati come parte del codice, sotto forma di una struttura di dati se viene utilizzata la preelaborazione, o nella loro forma testuale nei rari casi in cui non viene utilizzata la preelaborazione.

    
risposta data 11.09.2012 - 17:43
fonte
0

In un linguaggio come C o C ++, i semplici tipi nativi come int, char, float, double nelle architetture complete con hardware in virgola mobile possono in genere diventare istruzioni sequenziali della macchina come le altre istruzioni nel blocco di codice.

In un'architettura che non ha hardware in virgola mobile nativo (o anche inte a 32 bit), le espressioni semplici possono generare chiamate di subroutine per eseguire librerie temporali che integrano le funzionalità di base del dispositivo. Ho fatto un esperimento con alcuni otto bit micros contro un compilatore X86 alcuni anni fa e ho trovato grandi differenze nella dimensione del codice generato. x86 era grande 1/3 e, a differenza delle architetture a 8 bit che avevano molte richieste di runtime per cose semplici come l'aggiunta di interi a 32 e 64 bit, tutto era generato da sequenze di istruzioni.

Spesso le costanti sono inserite con le istruzioni come operandi immediati. In alcune architetture, i dati immediati verranno inseriti nello stack o scritti rispetto a un puntatore allo stack o a un puntatore di base. In un eseguibile, le istruzioni inserite e gli operandi immediati saranno in genere nel segmento di codice. Per dati complessi come array, stringhe e variabili definite dall'utente, se sono costanti, possono essere emessi anche in un segmento di codice, ma ciò che è più comune è che durante il runtime vengono copiati in un segmento di dati inizializzato dal caricatore o il sistema di runtime.

I compilatori dispongono di molti metodi per l'ottimizzazione delle espressioni, quindi se esegui un passo singolo e vedi quelle che sembrano pochissime istruzioni relative a un'espressione, potrebbe significare che le costanti nell'espressione sono state semplificate. C'è anche una tecnica in cui le sottoespressioni comuni vengono valutate una volta prima del primo utilizzo, quindi quando è necessario di nuovo, il risultato parziale è appena usato da una variabile temporanea o da un registro.

Questo gruppo, o Analisi del codice di scambio dello stack, potrebbe essere un'ottima occasione per spiegare cosa sta succedendo se hai ulteriori domande su un blocco di codice sorgente e sulle istruzioni della macchina che genera, in particolare per quanto riguarda le ottimizzazioni del compilatore.

Quando gli operatori o le espressioni vengono utilizzati con tipi di dati complessi come array, stringhe, strutture o classi, potrebbero esserci alcuni casi in cui le operazioni vengono eseguite in sequenza, ma in genere i tipi più complessi richiedono chiamate di funzioni. Un'eccezionale eccezione a questo è quando un compilatore fornisce estensioni OpenMP che eseguono il parallelismo a grana fine su piccoli blocchi di codice che vengono inviati su più core. Per quanto interessante possa essere, i dettagli sono probabilmente fuori portata per questa domanda.

    
risposta data 03.10.2012 - 05:15
fonte

Leggi altre domande sui tag