C / C++ изменение значения константы



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



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

1164   17  

17 ответов:

Вам нужно отбросить постоянство:

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

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

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

Просто предположение, но общий вопрос заключается в том, почему нельзя преобразовать 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, не используя какой-либо типовой код. Вам придется использовать либо приведение в стиле C, либо C++-style const_cast, чтобы удалить const-ness. Любая другая попытка сделать это приведет к ошибке компилятора где-то.

Обратите внимание, что любая попытка отбросить постоянство не определяется стандартом. Из пункта 7.1.5.1 стандарта:

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

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

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)

Назад в туманах времени, мы палео-программисты используют Фортран. FORTRAN передавал все свои параметры по ссылке, и не делал никакой проверки типа. Это означало, что было довольно легко случайно изменить значение даже буквальной константы. Вы могли бы передать "3" подпрограмме, и она возвращалась бы измененной, и поэтому каждый раз с тех пор, когда ваш код имел "3", он фактически действовал бы как другое значение. Позвольте мне сказать вам, что это были трудные ошибки, чтобы найти и исправить.

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

ptr = const_cast<int *>(ptr_to_a);

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

В 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 никогда не может использоваться в качестве длины в объявлении массива.

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

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

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

  1. Числовой литерал (константа ) напр. 2, 10.53

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

  3. Константа перечисления

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

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

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

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

Я не уверен на 100%, что это сработает, хотя я немного заржавел в C / C++: -)

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

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;

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

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

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

Некоторые из этих ответов указывают на то, что компилятор может оптимизировать переменную '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 должно действительно проиллюстрировать, насколько это глупо.
#include<iostream>
int main( void )
{
   int i = 3;    
   const int *pi = &i;
   int *pj = (int*)&i;
    *pj = 4;

   getchar(); 
   return 0;  
}

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

const int x=5; 

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

*(int *)&x=7;

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

Я искал, как конвертировать между conts, и я нашел этот http://www.possibility.com/Cpp/const.html может быть, это кому-то пригодится. :)

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

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

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

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

void main(void)
{
    static const int x = 5;
    int *p = (int *)x;
    *p = 99;                //here it will trigger the fault at run time
}
#include<stdio.h>
#include<stdlib.h>

int main(void) {
    const int a = 1; //a is constant
    fprintf(stdout,"%d\n",a);//prints 1
    int* a_ptr = &a;
    *a_ptr = 4;//memory leak in c(value of a changed)
    fprintf(stdout,"%d",a);//prints 4
return 0;
}

Шаг, который вы пропускаете, заключается в том, что вам не нужен указатель 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. Вы можете изменить a через ptr_to_a без какого-либо предупреждения.

Правка:

Вышесказанное неверно. Оказывается, я перепутал подобный трюк с 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.

Comments

    Ничего не найдено.