我在以下代码片段中找到了
const int i = 2;
const int* ptr1= &i;
int* ptr2 = (int*)ptr1;
*ptr2 =3;
i
的值变为3.我想知道的是为什么允许这样做。这可能会有什么帮助?
答案 0 :(得分:30)
这是允许的,因为你通过将它转换为非const指针来推翻ptr1的constness。这就是演员阵容非常危险的原因。
请注意,某些编译器(如GCC)不允许您像这样抛弃const状态。
答案 1 :(得分:12)
你通过玩指针技巧打破了恒定性保证。这不能保证一直工作,并且几乎可以调用任何行为,具体取决于您抛出它的系统/ OS /编译器。
不要那样做。
或者至少不要这样做,除非你真的知道自己在做什么,甚至理解它并不是最便携的。
答案 2 :(得分:10)
“允许”是“被阻止”的反对,但它也与“被禁止”相反。你已经看到修改你的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
,使指针完全不透明,并提供访问字符串数据的功能。但是C并不要求你封装所有内容,它允许你返回指向完全定义类型的指针,并且它希望支持这种const用法来呈现一个“真正”可修改的对象的只读视图。 / p>
答案 3 :(得分:5)
const
的确意味着“只读”。
正如您所知,const
对象的值可能会发生变化,但您必须使用不正确的方法来执行此操作。在使用这些不正确的方法时,您可以调用未定义的行为。
答案 4 :(得分: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));
}
(从有效C ++第3项的第3项中的C ++示例中略微损坏的示例)
答案 5 :(得分:2)
如果你要在C ++程序中抛弃constness,请使用更多的C ++样式:
int *ptr2 = const_cast<int*>(ptr1);
如果你遇到与这种类型的演员相关的问题(你会,你会这样做),那么你可以通过搜索“const_cast”而不是在阳光下尝试每个组合来快速找到它发生的位置。此外,它还可以帮助我们可能会或可能不会追随你的人。
只有少数几种情况我认为这有帮助。他们中的大多数是角落案件。如果你用C ++开发,我会不惜一切代价避免这种情况。
答案 6 :(得分:2)
C演员告诉编译器你知道自己在做什么,并确保它最终都能正常工作。如果你使用它们而不准确理解你正在做什么,你可能会遇到麻烦。
在这种情况下,编译器完全有权将i
放入只读内存中,因此运行时此代码会崩溃。或者,它可能会像你看到的那样工作。标准将此指定为未定义的行为,因此字面上可能会发生任何事情。
C最初是为编写Unix而设计的,故意让程序员在操作数据方面有很大的自由,因为在OS编写中,编写高度特定于实现的代码通常非常有用,这些代码可以执行任何不安全的操作。其他背景。在常规应用程序代码中,应谨慎进行转换。
不要在C ++中使用C风格的强制转换。 C ++有自己的强制转换系列,很容易在代码中搜索,并且通常会指定演员实际执行的更多内容。在这种特殊情况下,您可以使用const_cast
,它可以准确显示您正在做什么并且很容易找到。
答案 7 :(得分:2)
因为C知道程序员总是知道他们在做什么,总是做正确的事情。你甚至可以把它像:
void* ptr2 = (int(*)(void(*)(char*), int[])) ptr1;
(*(int*)ptr2) = 3;
它甚至都不会抱怨。
答案 8 :(得分:1)
语句const int i = 2;
表示符号/变量i
包含值2
,无法使用i
更改值;但不能保证价值不能通过其他方式改变(如OP的例子中所示)。