改变C中的const值

我发现在下面的代码片段中

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

i的价值变化为3.我想知道的是为什么这是允许的。 有什么情况可以变得有用?

这是允许的,因为你通过将它转换为非const指针来推翻ptr1的constness。 这就是演员阵容非常危险的原因。

请注意,某些编译器(如GCC)不允许您像这样抛弃const状态。

你通过玩指针技巧打破了恒定性保证。 这不能保证一直工作,并且几乎可以调用任何行为,具体取决于您抛出它的系统/ OS /编译器。

不要那样做。

或者至少不要这样做,除非你真的知道自己在做什么,甚至理解它并不是最便携的。

“允许”是“被阻止”的反对,但它也与“禁止”相反。 您已经看到,不会阻止修改您的const对象,但这并不意味着它是允许的。

在“允许”的意义上,修改const对象不是“允许的”。 标准没有定义程序的行为(见6.7.3 / 5)。 恰好在您的实现上,在该运行中,您看到了值3.在另一个实现或另一天,您可能会看到不同的结果。

但是,它并没有“被阻止”,因为使用C转换的方式工作,在编译时检测它是一个停顿问题。 在运行时检测它需要在所有内存访问中进行额外检查。 该标准旨在不对实现施加大量开销。

抛弃const的原因是支持的,因为如果你有一个指向非const对象的const指针,那么该语言允许你(在两种意义上)修改该对象。 为此,您需要摆脱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 ,使指针完全不透明,并提供访问字符串数据的function。 但是C并不要求你封装所有东西,它允许你返回指向完全定义类型的指针,并且它希望支持这种const用法来呈现一个“真正”可修改的对象的只读视图。

const真的意思是“只读”。

正如您所知, const对象的值可以改变,但您必须使用不正确的方法来实现它。 在使用这些狡猾的方法时,您可以调用未定义的行为

它的工作原理是因为你已经显式地抛弃了指针的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)); } 

(从有效C ++第3项的第3项中的C ++示例中略微损坏的示例)

如果你要在C ++程序中抛弃constness,请使用更多的C ++样式:

 int *ptr2 = const_cast(ptr1); 

如果你遇到与这种类型的投射有关的问题(你会,你会一直这样),那么你可以通过搜索“const_cast”而不是在阳光下尝试每个组合来快速找到它发生的位置。 此外,它还会帮助我们可能会或可能不会追随你的人。

只有少数情况我可以看到这有用。 他们中的大多数是角落案件。 如果您使用C ++进行开发,我会不惜一切代价避免这种情况。

C演员告诉编译器你知道你在做什么,并且你将确保它最终都能正常工作。 如果你使用它们而不准确理解你正在做什么,你可能会遇到麻烦。

在这种情况下,编译器完全有权将i放入只读内存中,因此这段代码在运行时会崩溃。 或者,它可能会像你看到的那样工作。 标准将此指定为未定义的行为,因此字面上可能会发生任何事情。

C最初是为编写Unix而设计的,故意让程序员在操作数据方面有很大的自由,因为在OS编写中,编写高度特定于实现的代码通常非常有用,这些代码可以完成任何其他环境中不安全的操作。 在常规应用程序代码中,应谨慎进行转换。

并且不要在C ++中使用C风格的强制转换。 C ++有自己的强制转换系列,很容易在代码中搜索,并且通常会指定演员实际上做的更多内容。 在这种特殊情况下,您将使用const_cast ,它可以准确显示您正在做什么并且很容易找到。

因为C知道程序员总是知道他们在做什么,总是做正确的事情。 你甚至可以把它像:

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

它甚至都不会抱怨。

语句const int i = 2; 表示符号/变量i保持值2 ,并且不能使用i更改该值; 但不能保证价值不能通过其他方式改变 (如OP的例子)。