2014-09-30 23 views
5

Qualcuno può spiegarmi perché questo codice funziona? Sento che il compilatore non dovrebbe permettermi di fare ciò che ho fatto (spostare un puntatore int in modo che punti a un const int), o in alternativa mi aspetterei almeno un avvertimento del compilatore o un segfault. L'idea di cambiare il valore di una costante sembra così sbagliata.Incremento di una costante in C++

Codice:

#include <iostream> 

using namespace std; 

struct test_struct { 
    int i; 
    const int j; 
}; 

int main() { 
    cout << "Create a struct with int i = 100 and const int j = 101." << endl; 
    test_struct test{100, 101}; 
    cout << test.i << endl; 
    cout << test.j << endl; 
    cout << "Create pointer p and point it to int i." << endl; 
    int* p1 = &test.i; 
    cout << *p1 << endl; 
    cout << "Increment pointer p, which should now be pointing at const int j." << endl; 
    p1++; 
    cout << *p1 << endl; 
    cout << "Dereference p and increment it." << endl; 
    (*p1)++; 
    cout << *p1 << endl; 
    cout << test.j << endl; 
} 

uscita:

Create a struct with int i = 100 and const int j = 101. 
100 
101 
Create pointer p and point it to int i. 
100 
Increment pointer p, which should now be pointing at const int j. 
101 
Dereference p and increment it. 
102 
102 
+0

quale compilatore stai usando? – Creris

+0

Sto usando il compilatore g ++. – rolledback

+0

Un titolo migliore potrebbe essere: Modifica di una costante in una struttura incrementando un puntatore a un membro non const. – Csq

risposta

18

programma Si invoca undefined behavior in due modi, il che significa il comportamento del programma è imprevedibile, comportamento anche apparentemente normale è possibile.

Prima sebbene possiamo trattare i singoli elementi di una struttura come matrici una volta incrementato il puntatore non è più valido dereferenziarlo, non deve nemmeno puntare all'elemento successivo che potrebbe benissimo essere puntato imbottire.

In secondo luogo, il tentativo di modificare un const in anche un comportamento non definito. I draft C++ standard sezione 7.1.6.1il CV-qualificazioni comma che dice:

[...] qualsiasi tentativo di modificare un oggetto const durante la sua vita (3.8) si traduce in un comportamento indefinito.

Possiamo vedere ai fini di puntatori una variabile non array è trattata come una matrice di un elemento, dalla sezione 5.7Operatori additivi che dice:

Ai fini delle presenti operatori, un puntatore a un oggetto nonarray si comporta come un puntatore al primo elemento di un array di lunghezza uno con il tipo dell'oggetto come tipo di elemento.

e inoltre dereferecing uno oltre la fine di un array è un comportamento indefinito, dalla stessa sezione:

Quando viene aggiunto o sottratto da un puntatore, il risultato un'espressione che ha tipo integrale ha il tipo di operando del puntatore. [...] Se l'operando del puntatore e il risultato puntano a elementi dello stesso oggetto matrice o uno dopo l'ultimo elemento dell'oggetto matrice, la valutazione non deve produrre un overflow; in caso contrario, il comportamento non è definito.

possiamo ulteriormente vedere dalla sezione 5.3.1operatori unari che dice:

Il unario * operatore esegue indiretto: l'espressione per cui viene applicato è un puntatore a un tipo di oggetto, o un puntatore a un tipo funzione e il risultato è un Ivalue riferimento all'oggetto o funzione

quando dereferenziamo un puntatore che ci aspettiamo e oggetto che non è garantito avere una volta che siamo passati.

Il The GNU C++ Library ha un più facile accesso spiegazione che dice (sottolineatura mia):

Si può solo dereference un puntatore che punta in un array. Se il tuo puntatore dell'array punta all'esterno dell'array, anche solo a un punto dopo la fine di , e lo storni di default, si verificano le cose spiacevoli di .

+0

Quale bozza? Sarebbe meglio aggiungere la versione della bozza per riferimento futuro. – Csq

+0

OK. Questo ha senso dal momento che il mio professore ha detto qualcosa di molto simile. Immagino di essere più sorpreso che in questo caso, il comportamento indefinito non sia qualcosa di simile a un segfault o p indicante qualche punto strano nella memoria. – rolledback

+0

@Csq ha aggiunto il link alla bozza a cui facevo riferimento. –

1

(Questa risposta è corretta per Visual Studio 2010 - non è sicuro su altri compilatori.)

Ecco il motivo per cui questo è consentito:

Il modificatore const è un'istruzione per il compilatore per impedire all'utente di modificare quella variabile dichiarata. Quando si lavora con questa variabile, il compilatore impedisce di apportare modifiche e richiede di aggiungere il modificatore const ai puntatori associati a quella particolare variabile.

Tuttavia, come tutti gli altri valori delle variabili, risiede in memoria e il compilatore non impedisce specificamente l'accesso o la modifica di tale memoria. Si può accedere e modificare come qualsiasi altro indirizzo di memoria usando un puntatore, se si sceglie di sovvertire le istruzioni del compilatore, come si è fatto nel codice.

Se stai cercando di impedire l'accesso programma per le aree in memoria, è possibile fare riferimento ai seguenti costanti di protezione della memoria per Windows:

http://msdn.microsoft.com/en-us/library/windows/desktop/aa366786(v=vs.85).aspx

+0

_ "Quindi ecco il ragionamento sul perché è possibile farlo." _ Non UB rimane UB? –

+0

Vorrei sottolineare che solo perché puoi farlo non significa che dovresti o che è una buona pratica di codifica. Ha chiesto una spiegazione del perché ha funzionato. – user2076574

+0

Considerare una "pratica di codifica" valida _ con la possibilità più remota, è ovviamente sbagliato (per non dire ridicolo). –

-1

gli elementi di dati nella struttura vengono memorizzati sulla pila in memoria quindi, quando crei un puntatore e lo fai puntare al primo elemento, l'indirizzo che viene memorizzato è la posizione del puntatore dello stack. Quando lo incrementi, il puntatore dello stack aumenta alla successiva posizione sullo stack, ovvero il secondo elemento di dati. quindi potrebbe essere il possibile motivo. Perché altrimenti avrebbe dovuto dare un errore, e inoltre non possiamo trattare una struttura come una matrice. Bt è ancora in grado di puntare all'elemento successivo, possibile solo se consideriamo lo stack creato in memoria.