2012-02-21 13 views
7

Desidero assegnare un valore (ad esempio 2345) a una posizione di memoria (ad esempio 0X12AED567). Può essere fatto?Possiamo assegnare un valore ad una posizione di memoria data?

In altre parole, come posso implementare la seguente funzione?

void AssignValToPointer(uint32_t pointer, int value) 
{ 

} 
+7

'* (int *) 0x12AED567 = 2345' Ma è meglio sapere cosa stai facendo ... – Mysticial

+0

meglio non provare a meno che non si sia sicuri che la memoria non sia utilizzata –

+1

Non solo la memoria non deve essere utilizzata per altri scopi , ma deve essere mappato. Su * nix almeno, se provi a scrivere su una pagina non mappata, la tua app segura e si blocca. –

risposta

2

La risposta dipende da alcuni fattori. Il tuo programma è in esecuzione in un moderno sistema operativo?

In caso affermativo, tentare di accedere a un'area di memoria che non è mappata causerà un SIGSEGV. Per ottenere ciò, è necessario utilizzare una funzione specifica del sistema per mappare la regione di memoria che contiene questo indirizzo esatto prima di tentare di accedervi.

+0

Si sta ipotizzando che ciò avvenga su un sistema specifico. Per quanto ne so, SIGSEGV è qualcosa che accade solo nei sistemi operativi basati su POSIX. – Lundin

+1

Non presumo un sistema operativo specifico. Tuttavia, presumo che un sistema operativo moderno implementa la segnalazione POSIX, o almeno usi la stessa terminologia per la sua segnalazione, il che è vero per Windows. Questa è la ragione per la domanda in prima linea. – jweyrich

+0

http://en.wikipedia.org/wiki/List_of_real-time_operating_systems. Esiste un mondo intero al di fuori del settore della programmazione per PC. – Lundin

12

Il fatto che tu stia facendo questa domanda indica che sei sopra la testa. Ma qui si va:

*(int *)0x12AED567 = 2345; 
+0

Hai provato? – jweyrich

+0

@jweyrich Sì. L'ho eseguito sulla CPU ATMEGA32U4 sulla mia scheda Teensy. Non ha fatto molto però. Perché lo chiedi? –

+0

Ho usato uno 0 invece di una O maiuscola come primo carattere del numero esadecimale nel mio programma di test. E 'quello di cui eri preoccupato? –

1

Basta trattare la posizione di memoria come un puntatore

int* pMemory = OX12AED567; 
*pMemory = 2345; 

Nota: Questo funziona solo se quella posizione di memoria è accessibile e scrivibile dal programma. Scrivere in una posizione di memoria arbitraria come questa è intrinsecamente pericoloso.

+0

Ciò causerebbe un 'SIGSEGV' se questa regione di memoria non è mappata. – jweyrich

+1

se l'indirizzo non è compreso nell'intervallo della memoria di processo, non si arresta? – Gopinath

+1

@Gopinath dipende dall'implementazione, ma in generale sì si bloccherà. – JaredPar

0

con la clausola che non è portatile o cassetta di sicurezza (a tutti):

*((int *)0x12AED567) = 2345; 
2

Per quanto riguarda la C è interessato, questo è un comportamento indefinito. Il mio suggerimento seguente è anche un comportamento indefinito, ma evita tutti i problemi basati sui tipi e sull'aliasing: Usa caratteri.

int a = get_value(); 
char const * const p = (const char * const)&a; 
char * q = (char *)0x12345; 

memcpy(q, p, sizeof(int)); 

In alternativa, è possibile accedere direttamente byte q[i]. (Questa è la parte che è UB: il puntatore q non è stato ottenuto come indirizzo-di un oggetto reale o come risultato di una funzione di allocazione.A volte questo è OK, ad esempio se stai scrivendo un programma indipendente che funziona in modalità reale e accede all'hardware grafico, è possibile scrivere direttamente nella memoria grafica tramite un indirizzo ben noto e codificato.)

+2

Oh, whoops, OK. Sarà necessaria una modifica banale, la modifica. –

0

Hai indicato che l'indirizzo è un indirizzo fisico e che il tuo codice è in esecuzione in un processo.

Quindi, se siete

  1. nel tipo di sistema operativo di alto livello, per esempio Linux, dovresti ottenere una mappatura nello spazio degli indirizzi fisico. In Linux,/dev/mem lo fa per te.

  2. all'interno del kernel o senza sistema operativo e con una MMU, è necessario tradurre l'indirizzo fisico in un indirizzo virtuale. Nel kernel di Linux, phys_to_virt() lo fa per te. Presumo, nel kernel, che questo indirizzo sia sempre mappato.

  3. all'interno del kernel o senza sistema operativo e senza MMU, si scrive direttamente a quell'indirizzo. Non c'è nessuna mappatura da considerare.

Ora, si dispone di una mappatura valida o dell'indirizzo fisico stesso, che si passa alla funzione.

void AssignValToPointer(uint32_t pointer, int value) 
{ 
    * ((volatile int *) pointer) = value; 
} 

Si potrebbe desiderare di aggiungere la parola chiave volatile, come il compilatore potrebbe ottimizzare il write-operazione via se non si legge da quella posizione dopo (probabile caso in cui la scrittura su un registro di un hardware mappato in memoria).

Si potrebbe anche voler usare il tipo di dati uintptr_t invece di uint32_t per il puntatore.

1

C99 progetto di norma

È probabile che questo non è possibile senza un comportamento implementazione definita.

proposito calchi come:

*(uint32_t *)0x12AED567 = 2345; 

il C99 N1256 standard draft "6.3.2.3 puntatori" dice:

5 Un numero intero può essere convertito in qualsiasi tipo di puntatore. Ad eccezione di quanto specificato in precedenza, il risultato è definito dall'implementazione, potrebbe non essere allineato correttamente, potrebbe non puntare a un'entità del tipo di riferimento e potrebbe essere una rappresentazione trap. 56)

attuazione GCC

documenti GCC sua int all'attuazione puntatore: https://gcc.gnu.org/onlinedocs/gcc-5.4.0/gcc/Arrays-and-pointers-implementation.html#Arrays-and-pointers-implementation

Un cast da intero a puntatore scarta bit più significativi se la rappresentazione puntatore è inferiore il tipo intero, si estende in base alla signness del tipo intero se la rappresentazione del puntatore è maggiore del tipo intero, altrimenti i bit rimangono invariati.

così il cast funzionerà come previsto per questa implementazione. Mi aspetto che altri compilatori facciano cose simili.

Problemi correlati