2010-09-14 3 views
14

Я считаю, что в следующем фрагменте кодаизменения константное значение в C

const int i = 2; 
const int* ptr1= &i; 
int* ptr2 = (int*)ptr1; 
*ptr2 =3; 

i «s значение изменяется до 3. То, что я мог бы знать, почему это разрешено. Каковы ситуации, когда это может стать полезным?

+13

Потому что C - идеальное воплощение слабого набора текста. «Типы просто для удовольствия, если вам не нравится какой-то тип, просто бросьте его»;) – delnan

+1

Закон Осборна http://www.anvari.org/fortune/Fortune_Cookies/95_osborn-s-law-variables-won- t.html – Jaydee

+2

Потому что C & C++ позволит вам делать все, что угодно, даже если вы хотите сделать что-то действительно, действительно глупо. –

ответ

30

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

Обратите внимание, что некоторые компиляторы, такие как GCC, не позволят вам отбросить статус const таким образом.

+0

+1 хороший ответ. – 2010-09-14 14:03:40

+1

Обратите внимание, что этот код может упасть, если значение const хранится в постоянной памяти. –

+1

Мой gcc (4.4.3) принимает код без каких-либо претензий. Компилятор codepad также принимает его (http://codepad.org/V9Oi65Cf), как и идеон (http://ideone.com/tjCq7) – pmg

12

Вы нарушили гарантию на постоянство, играя указатели. Это не гарантируется, что оно будет работать все время и может вызвать практически любое поведение в зависимости от системы/ОС/компилятора, на которые вы его набрасываете.

Не делайте этого.

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

5

const действительно означает «только для чтения».

Как вы узнали, значение const объектов может измениться, но для этого вам нужно использовать коварные методы. И при использовании этих хитрых методов вы вызываете Неопределенное поведение.

+1

@ Konrad: В C и C++ неопределенное поведение для изменения объекта, содержащего 'const'-qualification в его определении. Эти объекты могут потенциально находиться в постоянной памяти. –

+0

@Bart: вы правы, конечно ... почему-то я проигнорировал, что это было определение переменной. –

2

Это работает, потому что вы явно удалили const оттуда. Хотя ptr1 является указателем на const int, ptr2 является указателем на int, поэтому он является переменным.

Существует очень мало веских причин для этого, но вы можете найти случай, когда он избегает дублирования кода. Например:

const char* letter_at(char* input, int position) 
{ 
    ... stuff ... 
    return &found_char; 
} 

char* editable_letter_at(char* input, int position) 
{ 
    return (char*)(letter_at(input, position)); 
} 

(пример несколько искажены из примера С ++ в пункте 3 Эффективное C++ 3)

2

Если вы собираетесь откинуть константность в программе на C++, пожалуйста, используйте стиль более C++ литья:

int *ptr2 = const_cast<int*>(ptr1); 

Если вы столкнулись с проблемами, связанными с этим типом литья (вы будете, вы всегда делать), то вы можете найти, где это произошло гораздо быстрее, ища «const_cast», а не пытаться каждую комбинацию под солнцем. Кроме того, это поможет другим нашим, кто может или не может прийти за вами.

Есть только несколько ситуаций, когда я мог видеть, что это полезно. Большинство из них - угловые случаи. Я бы избегал этого любой ценой, если вы работаете на C++.

10

«Разрешено» является противостоящим «предотвращено», но это также противоположность «запрещенному». Вы видели, что изменение вашего объекта const не предотвращается, но это не означает, что это разрешено.

Изменение объекта const не допускается «в разрешении». Поведение вашей программы не определяется стандартом (см. 6.7.3/5). Просто случается так, что при реализации в этом запуске вы видите значение 3. В другой реализации или в другой день вы можете увидеть другой результат.

Однако, это не «предотвращено», потому что с тем, как работает C cast, обнаружение его во время компиляции является проблемой остановки. Обнаружение этого во время выполнения требует дополнительных проверок во всех обращениях к памяти. Стандарт разработан, чтобы не налагать много накладных расходов на реализации.

Причина отказа от const поддерживается вообще, потому что, если у вас есть указатель const на неконстантный объект, этот язык позволяет вам (в обоих смыслах) изменять этот объект. Для этого вам нужно избавиться от спецификатора const. Следствием этого является то, что программисты могут также отбрасывать квалификаторы const от указателей на объекты, которые фактически являются константами.

Вот (слегка глупый) пример кода, который отбрасывает константный спецификатор по этой причине:

typedef struct { 
    const char *stringdata; 
    int refcount; 
} atom; 

// returns const, because clients aren't allowed to directly modify atoms, 
// just read them 
const atom *getAtom(const char *s) { 
    atom *a = lookup_in_global_collection_of_atoms(s); 
    if (a == 0) { 
     // error-handling omitted 
     atom *a = malloc(sizeof(atom)); 
     a->stringdata = strdup(s); 
     a->refcount = 1; 
     insert_in_global_collection_of_atoms(a); 
    } else { 
     a->refcount++; 
    } 
    return a; 
} 

// takes const, because that's what the client has 
void derefAtom(const atom *a) { 
    atom *tmp = (atom*)a; 
    --(tmp->refcount); 
    if (tmp->refcount == 0) { 
     remove_from_global_collection_of_atoms(a); 
     free(atom->stringdata); 
     free(atom); 
    } 
} 
void refAtom(const atom *a) { 
    ++(((atom*) a)->refcount); 
} 

Это глупо, потому что лучше дизайн будет пересылать-объявить atom, чтобы сделать указатели на это полностью непрозрачный , и предоставить функцию для доступа к строковым данным. Но C не требует, чтобы вы инкапсулировали все, он позволяет вам возвращать указатели на полностью определенные типы и хочет поддерживать этот тип использования const, чтобы представить просмотр только для объекта, который «действительно» модифицируется.

2

C casts сообщает компилятору, что вы знаете, что делаете, и что вы убедитесь, что все это работает в конце. Если вы используете их, не понимая, что именно вы делаете, вы можете попасть в беду.

В этом случае компилятор полностью в пределах своих прав поместить i в постоянную память, чтобы этот код сработал при запуске. В противном случае он может работать, как вы видели. Стандарт определяет это как неопределенное поведение, поэтому буквально все может произойти.

C был первоначально разработан для написания Unix и преднамеренно дает программисту большую свободу в управлении данными, поскольку в написании ОС часто очень полезно писать высокоспециализированный код, который делает вещи, которые были бы небезопасными в любой другой контекст. В обычном коде приложения литье должно выполняться с осторожностью.

И не используйте C-style cast в C++. C++ имеет собственное семейство отливок, которые легко найти в коде, и которые часто указывают больше, что делает актерский процесс. В этом конкретном случае вы должны использовать const_cast, который показывает, что вы делаете, и его легко найти.

2

Потому что C знает, что программисты всегда знают, что они делают, и всегда поступают правильно. Вы можете даже отливать его как:

void* ptr2 = (int(*)(void(*)(char*), int[])) ptr1; 
(*(int*)ptr2) = 3; 

, и он даже не пожалуется.

1

Оператор const int i = 2; означает, что символ/переменная i имеет значение 2 и значение не может быть изменено с помощью i; но нет гарантии, что значение не может быть изменено каким-либо другим способом (как в примере OP).

Смежные вопросы