我有类似以下代码的等价物:
const int* const n = new int;
printf("input: ");
scanf("%d", n);
delete n;
现在,由于n是指向一个常量整数的指针,所以这应该是不起作用的(我期望会有编译器错误)。然而,这似乎可以正常工作,甚至将输入的值存储到*n中。
我想知道,为什么这不会给我一个错误;为什么它会工作?难道scanf不应该无法更改*n的值吗?
scanf的原型是:
int scanf ( const char * format, ... );
省略号表示它可以接受可变数量的参数。C(和C ++)没有办法检查这些参数的有效性。这就是为什么如果您在这里传递double的地址甚至是double变量本身,您不会收到错误。程序员需要验证传递了正确的参数。
scanf
几乎没有类型安全性,它会快乐地执行你告诉它的操作。这是因为在 C 语言中实现可变参数列表的方式。它们期望类型与您告诉它的类型相同。
因此,如果您给 scanf
提供不匹配的转换说明符,就会引发未定义的行为,这将在运行时发生。同样,编译器可能无法确定传递的指针是否为 const type* const
类型。一个好的编译器可能会在发现一些奇怪的东西时发出诊断,但并不一定要这样做。
由于大多数未定义行为都发生在运行时,因此通常由程序员负责了解各种形式的未定义行为并避免它们。
scanf
和可变参数列表,因为它们缺乏类型安全等问题。 - Lundinconst
。拥有一个指向非常量对象的 const 指针是完全可以的。 - Lundin&
是 scanf
试图修改非常量 int
对象而不是常量(且非 int
)指针对象的原因。 - user1084944由于scanf
是可变参数的,因此几乎可以传入任何内容,因此它应该也可以编译通过。
虽然n
是指向const int
的指针,但它指向的对象实际上并不是一个const对象。因此,修改该int
对象(例如使用const_cast
将指针转换为int*
)是良好定义的行为。
最后,fscanf
的标准文档如下:
将首个跟在格式参数后且还没有接收到转换结果的参数所指向的对象中放置转换结果。如果该对象没有相应的类型,或者无法将转换结果表示为对象,则其行为是未定义的。
指针实际上是指向适当类型的对象;总之,我认为这是良好定义的(但令人困惑)行为。
编译器可以发出警告,但不会阻止您犯这样的错误,这是未定义行为,const
只是用作指示符,它不能防止编译。而且它能正常工作是因为指针并不真正是const
,尽管传递const
指针是未定义行为,但这并不是真正的情况。
scanf
的可变参数本质。@pcarter 没有。 - cubuspl42int
确实是非 const 的。 - Johannes Schaub - litbint *
与const int *
是不同的类型。据我所知,它们具有相同的表示,并且允许重新解释彼此的字节(从C++11开始,严格来说,它们添加了别名例外)。但它取决于详细描述中的printf函数。如果它们需要精确的类型匹配,并且没有余地,那么就是未定义的行为(虽然如果失败了,我会非常惊讶)。 - Johannes Schaub - litb好的,scanf
不会区分传递的参数,但如果启用警告编译器将会提示 -
warning: writing into constant object (argument 2) [-Wformat]
const
对象。我们有一个(常量)指向const int的指针,但它指向一个非const对象,因此修改引用是可以的。 - user1084944
const
,因为没有一个参数可以是const
,而且对于格式字符串还有特殊的检查。我认为很有可能也有这种检查。但我不太确定,因为我几乎从不使用scanf()
。 - Iharob Al Asimiunsigned int
传递给printf()
,其中使用了"%f"
格式说明符。 - Iharob Al Asimi