2009-02-24 3 views
13

У меня была статья, но я ее потерял. Он показал и описал пару трюков на C/C++, которые должны быть осторожны. Один из них интересовал меня, но теперь, когда я пытаюсь воспроизвести его, я не могу его компилировать.C/C++ изменение значения константы

Концепция заключалась в том, что можно изменить случайно ценность const в C/C++

Это было что-то вроде этого:

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 

Я хотел показать другу, но теперь я пропускаю шаг. Кто-нибудь знает, чего не хватает, чтобы начать компиляцию и работу?

ATM Я получаю неверное преобразование из 'const int *' в 'int *', но когда я прочитал статью, которую я пробовал, и она отлично работала.

+0

Я отправил полную программу и объяснил, что это g ++, которая ее блокирует, gcc допускает такое поведение. – sfossen

+2

Даже если вы его скомпилируете. это неопределенное поведение. Это может взорвать ваш компьютер, свернуть вашу программу или заставить демонов вылететь из вашего носа. Или, конечно, может показаться, что он работает. На данный момент. На вашей машине. – jalf

ответ

35

вам нужно откинуть константность:

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 

также общий ответ не работает в г ++ 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 ~ $ 

кстати .. это никогда не рекомендуется ... Я обнаружил, что г ++ не позволяет этому случиться .. так что может быть вопрос вы испытывают.

+0

попытался, но он не работает: \ a сохранит то же значение и (* ptr) получит другое значение – fmsf

+0

, используя g ++ на mac btw – fmsf

+0

int 4.1.2 он заблокирован в g ++, но не gcc. – sfossen

4

Вы попробовали?

ptr = const_cast<int *>(ptr_to_a); 

Это должно помочь скомпилировать его, но это не случайно случайно.

+0

Да, что я знаю, но я думаю, что это было без этого, хотя – fmsf

+0

Похоже, вы получите правильный ответ, но я буду немного дольше, если появится решение без const_cast.Поскольку я действительно верю, что это – fmsf

+0

это неверно. – sfossen

0

Вы, вероятно, хотите использовать const_cast:

int *ptr = const_cast<int*>(ptr_to_a); 

Я не 100% уверен, что это будет работать, хотя, я немного ржавый на C/C++ :-)

Некоторые readup для const_cast: http://msdn.microsoft.com/en-us/library/bz6at95h(VS.80).aspx

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

не работает, после компиляции и запуска foo все равно 42 – fmsf

+0

Вы не компилируете w/optimizations on, не так ли? – dirkgently

+0

Он отлично работает на моем конце, хотя с VS2005 :(Надо будет ждать, прежде чем я смогу положить руки на gcc. – dirkgently

-2

Шаг, который вам не хватает, вам не нужен указатель int *. Линия:

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

фактически говорит, что вы не измените ptr_to_a, а не a. Так что если вы изменили код для чтения, как это:

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 

теперь 5. Вы можете изменить через ptr_to_a без какого-либо предупреждения.

EDIT:

Вышеуказанное неверно. Оказывается, я сбивал с толку подобный трюк с shared_ptr, в котором вы можете получить доступ к необработанному указателю и изменить внутреннее значение данных, не отпуская никаких предупреждений. То есть:

#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; 
} 

будет производить 5.

+0

, который не скомпилирован наверняка «ошибка: назначение только для чтения» – fmsf

+0

Нет, ptr_to_a - это указатель на const int, что означает, что вы обещаете не менять a. –

+0

Да, я знаю, что раньше я сталкивался с этим трюком, но это некоторые вариации. Я пытаюсь выкопать код и выяснить вариант I – jasedit

6

назад в туманах времени, мы палео-программисты использовали FORTRAN.FORTRAN передал все свои параметры по ссылке и не выполнял никаких проверок типов. Это означало, что было довольно легко случайно изменить значение даже постоянной константы. Вы можете передать «3» в SUBROUTINE, и он вернется с изменением, и поэтому каждый раз с того момента, когда ваш код имеет «3», он фактически будет действовать как другое значение. Позвольте мне сказать вам, это были жесткие ошибки, чтобы найти и исправить.

+0

«3» вещь сделала мой день :) –

8

Примечание. Любая попытка отбросить констел не определена стандартом. От 7.1.5.1 стандарта:

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

И сразу после этого примера используется:

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 

Так короче говоря, что вы хотите сделать, это не возможно, используя стандартный C++.

Далее, когда компилятор встречает заявление как

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

это бесплатно заменить любое вхождение в «а» с 3 (фактически делает то же самое, как #define a 3)

12

только предположение, но общий вопрос заключается в том, почему нельзя преобразовать int** в const int**, который поначалу кажется разумным (в конце концов, вы просто добавляете const, что обычно нормально). Причина заключается в том, что если вы могли бы сделать это, вы можете случайно изменить 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 

Это очень не интуитивный результат, но единственный способ убедиться, что вы не можете изменить const объект этот случай (обратите внимание на то, что нет типов) - это сделать строку 3 ошибкой.

Вы только разрешено добавлять const без гипса на первом уровне косвенности:

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

В C++ это невозможно изменить const объекта без использования приведения типа какого-то. Для удаления const вы должны использовать либо C-стиль, либо C++-стиль const_cast. Любая другая попытка сделать это приведет к ошибке компилятора.

+2

Время от времени я снова работаю над этим рассуждением. У меня всегда болит голова. –

+1

@Michael: phew - Тогда я не один. Мой тоже! –

0

В статье вы смотрите на, возможно, говорили о разнице между

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

и

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

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

Или так я recall-- у меня нет ничего, кроме Java инструментов здесь, поэтому не могу проверить :)

-1

Некоторые из этих ответов указывают на то, что компилятор может оптимизировать переменную 'a', поскольку она объявлена ​​const. Если вы действительно хотите, чтобы иметь возможность изменить значение a, то вам необходимо пометить его как 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 

Конечно, объявить что-то, как const volatile действительно должны показать, насколько глупо это.

+0

Я не думаю, что слово («изменчивое») означает, что вы думаете, что это значит. Это не имеет никакого отношения к тому, можете ли вы изменить значение, а скорее застраховать, чтобы чтение или запись в исходном коде выполнялись как чтение, так и запись в исполняемом файле, один на один. Другими словами, никакого кэширования нет. –

+0

Вот что я получаю. Компилятор увидит const и оптимизирует значение. Если вы заявляете, что это изменчиво, оно не может, поскольку, как вы сказали, «нет кеширования». –

+0

Это неправильно проголосовали, у плаката есть точка. –

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

    getchar(); 
    return 0; 
} 
2

В языке C++ с использованием 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 */ 

В С константной переменной может быть изменено через указатель; однако это неопределенное поведение. Константная переменная никогда не может использоваться как длина в объявлении массива.

В C++, если переменная const инициализируется чистым константным выражением, то ее значение не может быть изменено посредством указателя даже после попытки изменения, иначе переменная const может быть изменена посредством указателя.

Чистый интеграл Const переменного может быть использован в качестве длины в объявлении массива, если его значение больше 0.

Чистой константа состоит из следующих операндов.

  1. Числовой литерал (постоянный), например. 2, 10,53

  2. символьная константа определяется директивой #define

  3. Перечисление постоянной

  4. Чистый Const переменной т.е. константной переменной, которая сама инициализирован с чистым константным выражением.

  5. Неконстантные переменные или изменчивые переменные не допускаются.

0

мы можем изменить константное значение переменного с помощью следующего кода:

const int x=5; 

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

*(int *)&x=7; 

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

И что вы будете делать, когда печатает 'Новое значение x = 5'? – MSalters

+0

его выход на codepad.org равен x = 5 Новое значение x = 5 – Mak

0

Я проверил приведенный ниже код и успешно изменяет постоянные переменные-члены.

#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; 
} 

Выход консоли:

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 . . . 

Здесь гандикап, вы должны определить, как много указателей, как число постоянных членов переменных у вас есть.

0

это создаст ошибку времени выполнения. Потому что int is статические. Необработанное исключение. Место записи нарушения доступа 0x00035834.

void main(void) 
{ 
    static const int x = 5; 
    int *p = (int *)x; 
    *p = 99;    //here it will trigger the fault at run time 
} 
Смежные вопросы