2009-02-24 10 views
13

Ho avuto un articolo, ma l'ho perso. Ha mostrato e descritto un paio di trucchetti C/C++ che le persone dovrebbero fare attenzione. Uno di loro mi ha interessato, ma ora che sto cercando di replicarlo non sono in grado di farlo compilare.C/C++ cambiando il valore di un const

Il concetto era che è possibile cambiare per caso il valore di un const in C/C++

E 'stato qualcosa di simile:

const int a = 3;   // I promise I won't change a 
const int *ptr_to_a = &a; // I still promise I won't change a 
int *ptr; 
ptr = ptr_to_a; 

(*ptr) = 5;    // I'm a liar; a is now 5 

ho voluto mostrare ad un amico, ma ora mi manca un passaggio. Qualcuno sa cosa manca per iniziare a compilare e lavorare?

ATM Sto ricevendo la conversione non valida da da 'const int *' a 'int *' ma quando ho letto l'articolo ho provato e ha funzionato benissimo.

+0

Ho inviato un programma completo e ho spiegato che è g ++ che lo sta bloccando, gcc consente questo comportamento. – sfossen

+2

Anche se è stato compilato. è un comportamento indefinito. Potrebbe far esplodere il tuo computer, far crashare il tuo programma o far volare via demoni dai demoni. O, ovviamente, potrebbe sembrare funzionare. Per adesso. Sulla tua macchina. – jalf

risposta

35

è necessario gettare via la costanza:

linux ~ $ cat constTest.c 
#include <stdio.h> 


void modA(int *x) 
{ 
     *x = 7; 
} 


int main(void) 
{ 

     const int a = 3; // I promisse i won't change a 
     int *ptr; 
     ptr = (int*)(&a); 

     printf("A=%d\n", a); 
     *ptr = 5; // I'm a liar, a is now 5 
     printf("A=%d\n", a); 

     *((int*)(&a)) = 6; 
     printf("A=%d\n", a); 

     modA((int*)(&a)); 
     printf("A=%d\n", a); 

     return 0; 
} 
linux ~ $ gcc constTest.c -o constTest 
linux ~ $ ./constTest 
A=3 
A=5 
A=6 
A=7 
linux ~ $ g++ constTest.c -o constTest 
linux ~ $ ./constTest 
A=3 
A=3 
A=3 
A=3 

anche la risposta comune non funziona in g ++ 4.1.2

linux ~ $ cat constTest2.cpp 
#include <iostream> 
using namespace std; 
int main(void) 
{ 
     const int a = 3; // I promisse i won't change a 
     int *ptr; 
     ptr = const_cast<int*>(&a); 

     cout << "A=" << a << endl; 
     *ptr = 5; // I'm a liar, a is now 5 
     cout << "A=" << a << endl; 

     return 0; 
} 
linux ~ $ g++ constTest2.cpp -o constTest2 
linux ~ $ ./constTest2 
A=3 
A=3 
linux ~ $ 

btw .. questo non è mai consigliato ... ho scoperto che g ++ non permette che questo accada .. quindi che può essere il problema che stanno vivendo.

+0

provato ma non funziona: \ la a manterrà lo stesso valore e (* ptr) avrà un altro valore – fmsf

+0

usando g ++ su mac btw – fmsf

+0

int 4.1.2 è bloccato in g ++ ma non gcc. – sfossen

4

Hai provato questo?

ptr = const_cast<int *>(ptr_to_a); 

Questo dovrebbe aiutare a compilare, ma non è davvero un incidente a causa del cast.

+0

Sì, lo so, ma penso che sia stato senza quello anche se – fmsf

+0

Sembra che otterrai la risposta giusta, ma avrò un po 'più a lungo nel caso si presentasse una soluzione senza const_cast.Come credo davvero che sia – fmsf

+0

questo non è corretto. – sfossen

0
const int foo = 42; 
const int *pfoo = &foo; 
const void *t = pfoo; 
void *s = &t; // pointer to pointer to int 
int **z = (int **)s; // pointer to int 
**z = 0; 
+0

non funziona, dopo la compilazione e l'esecuzione di foo è ancora 42 – fmsf

+0

non si sta compilando w/ottimizzazioni su, sei tu? – dirkgently

+0

Benché funzionasse bene con VS2005 :(Dovrò aspettare prima di poter mettere le mani su un gcc. – dirkgently

-2

Il passo che ti manca è che non hai bisogno del puntatore int *. La linea:

const int *ptr_to_a = &a; // I still promiss i won't change a; 

dice in realtà non si cambia ptr_to_a, non una. Quindi, se hai cambiato il codice per leggere in questo modo:

const int a = 3; // I promise I won't change a 
const int *ptr_to_a = &a; // I promise I won't change ptr_to_a, not a. 

(*ptr_to_a) = 5; // a is now 5 

a è ora 5. È possibile modificare un bel passaggio ptr_to_a senza alcun preavviso.

MODIFICA:

Quanto sopra non è corretto. Risulta che stavo confondendo un trucco simile con un shared_ptr, in cui è possibile ottenere l'accesso al puntatore raw e modificare il valore dei dati interni senza disattivare alcun avviso. Cioè:

#include <iostream> 
#include <boost/shared_ptr.hpp> 

int main() 
{ 
    const boost::shared_ptr<int>* a = new boost::shared_ptr<int>(new int(3)); 
    *(a->get()) = 5; 
    std::cout << "A is: " << *(a->get()) << std::endl; 

    return 0; 
} 

produrrà 5.

+0

che non viene compilato di sicuro "errore: assegnazione del percorso di sola lettura" – fmsf

+0

No, ptr_to_a è un puntatore a const int, che significa esattamente che prometti di non cambiare A. –

+0

Sì, so che mi sono imbattuto in questo trucco in passato, ma c'è qualche variazione su questo. Sto provando a scavare nel codice e capire la variante I Ho visto: – jasedit

6

indietro nella notte dei tempi, ci paleo-programmatori utilizzati FORTRAN.FORTRAN ha passato tutti i suoi parametri per riferimento e non ha eseguito alcun controllo ortografico. Ciò significava che era abbastanza facile cambiare accidentalmente il valore di anche una costante letterale. Potresti passare "3" a un SUBROUTINE, e sarebbe tornato cambiato, e così ogni volta da quel momento in cui il tuo codice aveva un "3", in realtà si comporterebbe come un valore diverso. Lascia che te lo dica, quelli erano insetti difficili da trovare e risolvere.

+0

La cosa "3" ha reso la mia giornata :) –

8

Nota qualsiasi tentativo di allontanare la costanza non è definito dallo standard. Dal 7.1.5.1 della norma:

Except that any class member declared mutable can be modified, any attempt to modify a const object during its lifetime results in undefined behavior.

E subito dopo questo esempio viene utilizzato:

const int* ciq = new const int (3);  // initialized as required 
int* iq = const_cast<int*>(ciq);  // cast required 
*iq = 4;        // undefined: modifies a const object 

Così, in breve ciò che si vuole fare non è possibile utilizzare standard di C++.

Inoltre quando il compilatore incontra una dichiarazione come

const int a = 3; // I promisse i won't change a 

è libero di sostituire qualsiasi occorrenza di 'a' con 3 (facendo in modo efficace la stessa cosa di #define a 3)

12

Solo una supposizione, ma una domanda comune è perché non è possibile convertire uno int** in uno const int**, che a prima vista sembra ragionevole (dopotutto, si sta solo aggiungendo uno const, che normalmente è ok). La ragione è che se si potesse fare questo, si potrebbe accidentalmente modificare un oggetto const:

const int x = 3; 
int *px; 
const int **ppx = &px; // ERROR: conversion from 'int**' to 'const int**' 
*ppx = &x; // ok, assigning 'const int*' to 'const int*' 
*px = 4; // oops, just modified a const object 

E 'un risultato molto non-intuitivo, ma l'unico modo per assicurarsi che non è possibile modificare un oggetto const in questo caso (notare come non ci sono i typecasts) è rendere la linea 3 un errore.

ti è permesso solo per aggiungere const senza un cast al primo livello di indirezione:

int * const *ppx = &px; // this is ok 
*ppx = &x;    // but now this is an error because *ppx is 'const' 

In C++, non è possibile modificare un oggetto const senza utilizzare un typecast di qualche tipo. Dovrai utilizzare uno stile C o uno stile C++ const_cast per rimuovere const -ness. Qualsiasi altro tentativo di farlo risulterà in un errore del compilatore da qualche parte.

+2

Ogni tanto, riesco ancora a lavorare su questo ragionamento. Mi fa sempre male alla testa. –

+1

@ Michael: phew - Non sono solo, allora. Anche il mio! –

0

L'articolo che stavi guardando potrebbe essere stato parlando della differenza tra

const int *pciCantChangeTarget; 
const int ci = 37; 
pciCantChangeTarget = &ci; // works fine 
*pciCantChangeTarget = 3; // compile error 

e

int nFirst = 1; 
int const *cpiCantChangePointerValue = &nFirst; 
int nSecond = 968; 

*pciCantChangePointerValue = 402; // works 
cpiCantChangePointerValue = &ci; // compile error 

O almeno così recall-- non ho nulla, ma strumenti Java qui, quindi non posso testare :)

-1

Alcune di queste risposte indicano che il compilatore può ottimizzare la variabile 'a' poiché è dichiarata const. Se davvero si vuole essere in grado di modificare il valore di a allora avete bisogno di segnare come volatile

const volatile int a = 3; // I promise i won't change a 
    int *ptr = (int *)&a; 
    (*ptr) = 5; // I'm a liar, a is now 5 

Naturalmente, dichiarando qualcosa come const volatile dovrebbe davvero illustrano quanto sia stupido questo è.

+0

Non penso che la parola ("volatile") significhi cosa tu pensi che significhi. Non ha nulla a che fare con la possibilità di modificare il valore, ma piuttosto assicura che una lettura o scrittura nel codice sorgente sia fatta una lettura o una scrittura nell'eseguibile, uno per uno. In altre parole, nessuna memorizzazione nella cache. –

+0

Questo è quello che stavo ottenendo. Il compilatore vedrà const e ottimizzerà il valore. Se invece lo dichiari volatile, non può, dal momento che come hai detto "no caching". –

+0

Questo è errato, la locandina ha un punto. –

0
#include<iostream> 
int main(void) 
{ 
    int i = 3;  
    const int *pi = &i; 
    int *pj = (int*)&i; 
    *pj = 4; 

    getchar(); 
    return 0; 
} 
2

In C++, utilizzando Microsoft Visual Studio-2008

const int a = 3; /* I promisse i won't change a */ 
int * ptr1 = const_cast<int*> (&a); 
*ptr1 = 5; /* I'm a liar, a is now 5 . It's not okay. */ 
cout << "a = " << a << "\n"; /* prints 3 */ 
int arr1[a]; /* arr1 is an array of 3 ints */ 

int temp = 2; 
/* or, const volatile int temp = 2; */ 
const int b = temp + 1; /* I promisse i won't change b */ 
int * ptr2 = const_cast<int*> (&b); 
*ptr2 = 5; /* I'm a liar, b is now 5 . It's okay. */ 
cout << "b = " << b << "\n"; /* prints 5 */ 
//int arr2[b]; /* Compilation error */ 

In C, una variabile const può essere modificato attraverso il suo puntatore; tuttavia è un comportamento indefinito. Una variabile const non può mai essere utilizzata come lunghezza in una dichiarazione di array.

In C++, se una variabile const è inizializzata con un'espressione di pura costante, il suo valore non può essere modificato tramite il puntatore nemmeno dopo aver provato a modificarlo, altrimenti una variabile const può essere modificata tramite il puntatore.

Una variabile const integrante puro può essere utilizzato come lunghezza in una dichiarazione di matrice, se il valore è maggiore di 0.

pura espressione costante è costituito dai seguenti operandi.

  1. Un valore letterale numerico (costante) ad es. 2, 10.53

  2. Una costante simbolica definita dalla direttiva #define

  3. Un enumerazione costante

  4. Una variabile const puro cioè una variabile const che è esso stesso inizializzati con pura espressione costante.

  5. Non sono consentite variabili non costanti o variabili volatili.

0

possiamo cambiare il valore della variabile const dal seguente codice:

const int x=5; 

printf("\nValue of x=%d",x); 

*(int *)&x=7; 

printf("\nNew value of x=%d",x); 
+0

E cosa fai quando stampa 'Nuovo valore di x = 5'? – MSalters

+0

il suo output su codepad.org è Valore di x = 5 Nuovo valore di x = 5 – Mak

0

Ho testato il codice seguente e modifica con successo le variabili membro costante.

#include <iostream> 

class A 
{ 
    private: 
     int * pc1; // These must stay on the top of the constant member variables. 
     int * pc2; // Because, they must be initialized first 
     int * pc3; // in the constructor initialization list. 
    public: 
     A() : c1(0), c2(0), c3(0), v1(0), v2(0), v3(0) {} 
     A(const A & other) 
      : pc1 (const_cast<int*>(&other.c1)), 
       pc2 (const_cast<int*>(&other.c2)), 
       pc3 (const_cast<int*>(&other.c3)), 
       c1 (*pc1), 
       c2 (*pc2), 
       c3 (*pc3), 
       v1 (other.v1), 
       v2 (other.v2), 
       v3 (other.v3) 
     { 
     } 
     A(int c11, int c22, int c33, int v11, int v22, int v33) : c1(c11), c2(c22), c3(c33), v1(v11), v2(v22), v3(v33) 
     { 
     } 
     const A & operator=(const A & Rhs) 
     { 
      pc1  = const_cast<int*>(&c1); 
      pc2  = const_cast<int*>(&c2), 
      pc3  = const_cast<int*>(&c3), 
      *pc1 = *const_cast<int*>(&Rhs.c1); 
      *pc2 = *const_cast<int*>(&Rhs.c2); 
      *pc3 = *const_cast<int*>(&Rhs.c3); 
      v1  = Rhs.v1; 
      v2  = Rhs.v2; 
      v3  = Rhs.v3; 
      return *this; 
     } 
     const int c1; 
     const int c2; 
     const int c3; 
     int v1; 
     int v2; 
     int v3; 
}; 

std::wostream & operator<<(std::wostream & os, const A & a) 
{ 
    os << a.c1 << '\t' << a.c2 << '\t' << a.c3 << '\t' << a.v1 << '\t' << a.v2 << '\t' << a.v3 << std::endl; 
    return os; 
} 

int wmain(int argc, wchar_t *argv[], wchar_t *envp[]) 
{ 
    A ObjA(10, 20, 30, 11, 22, 33); 
    A ObjB(40, 50, 60, 44, 55, 66); 
    A ObjC(70, 80, 90, 77, 88, 99); 
    A ObjD(ObjA); 
    ObjB = ObjC; 
    std::wcout << ObjA << ObjB << ObjC << ObjD; 

    system("pause"); 
    return 0; 
} 

L'uscita della console è:

10  20  30  11  22  33 
70  80  90  77  88  99 
70  80  90  77  88  99 
10  20  30  11  22  33 
Press any key to continue . . . 

Qui, l'handicap è, è necessario definire il maggior numero di indicatori come il numero di variabili membro costanti che avete.

0

ciò creerà un errore di runtime. Perché il int è statico. Eccezione non gestita. Posizione di scrittura della violazione di accesso 0x00035834.

void main(void) 
{ 
    static const int x = 5; 
    int *p = (int *)x; 
    *p = 99;    //here it will trigger the fault at run time 
} 
Problemi correlati