指向const int的指针,但仍会修改数据

指向const int的指针,但仍会修改数据,c,pointers,malloc,constants,C,Pointers,Malloc,Constants,输入大小:3 输入[0]:1 输入[1]:2 输入[2]:3 1 2 3 在这里,在这段代码中,我使用const关键字不修改由“arr”指针指向的数据。 如果我使用const关键字,他们为什么会给我输出? 很明显,您已经关闭了所有编译器警告 程序通过将返回int的函数的结果赋给const int*来调用未定义的行为。编译器应该告诉你,也许你忽略了它,但从那时起所有的可能性都消失了 将常量int*传递给scanf。同样,编译器应该警告您,也许您忽略了它,但是所有的可能性都没有 const int*

输入大小:3

输入[0]:1

输入[1]:2

输入[2]:3

1 2 3

在这里,在这段代码中,我使用const关键字不修改由“arr”指针指向的数据。 如果我使用const关键字,他们为什么会给我输出?
很明显,您已经关闭了所有编译器警告

程序通过将返回int的函数的结果赋给const int*来调用未定义的行为。编译器应该告诉你,也许你忽略了它,但从那时起所有的可能性都消失了

将常量int*传递给scanf。同样,编译器应该警告您,也许您忽略了它,但是所有的可能性都没有


const int*不会使指向的对象不可修改。它告诉编译器不要通过指针修改任何内容,仅此而已。malloc返回的存储区域永远都是不可修改的

你刚刚遇到了C允许你射杀你的脚的众多案例中的一个——不管是好是坏

关于您的假设的一些一般性评论:const是您给编译器的保证。所以你必须确保不违反合同。C没有真常数[1]。从语义上讲,const是一个限定符`它允许编译器进行额外的错误检查。但这需要编译器知道参数的类型。这适用于具有适当原型的函数,但通常[2]不适用于具有可变参数变量函数的函数,因为它们的类型在编译时没有给出,在运行时也没有显式可用

实际上,您将一个有问题的see-down指针类型传递给scanf。函数本身不检查,只需要正确的类型。它不能,因为C在运行时不提供对象的类型

现代编译器应该警告printf和scanf的参数类型不匹配。始终为gcc启用警告,至少使用-Wall-Wextra-Wconversions并注意它们

编辑:经过激烈的讨论,我不得不改变主意。出于最初给出的原因,这似乎不是未定义的行为[3]:将常量int*传递给需要int*的scanf

这是因为在首次写入6.5p6之前,func中的对象malloced没有有效类型。在使用int*的scanf中发生的。因此,对象的类型为int-no const。但是,通过常量int*的进一步访问是有效的。6.7.3p6仅在有充分理由的情况下使另一方向的行为未定义

由于函数声明中没有关于预期类型的信息,因此只能对可变函数执行未检测。考虑如下:

scanf("%d",&arr[i]);
在这里,编译器将生成一个警告。变量函数属于C无法检查限定符正确性的情况,这也包括例如volatile。有更多的,有些是相当微妙的

此处未定义行为的唯一情况是传递一个与预期的6.7.6.1p2不同的不兼容的限定指针

建议:启用警告,但不要依赖编译器检测所有缺陷,不仅仅是常量正确性。如果您需要更多的安全性,C语言不是正确的语言。高级语言如Python、java等有很好的原因,C++存在于两者之间。如果在需要的地方使用这些语言,那么C语言中的开放端允许很难完成的事情。总之:了解你的工具

注意:你不应该在C中使用malloc&friends的结果。sizeofchar是无用的。该标准定义为产生1

[1] 由于违反契约是未定义的行为,编译器实际上可以自由地将此类数据存储在只读内存中。例如,对于运行代码并直接从ROM读取某些数据的微控制器来说,这是至关重要的

[2] 现代编译器可以为参数类型解析printf和scanf系列的格式字符串,并警告错误匹配。这要求该字符串是字符串文字而不是变量。这是一个礼貌的编译器写了一个这些函数被广泛使用


[3] 基本上,未定义的行为意味着任何事情都有可能发生——您的计算机可能会跑掉,后台进程可能会出现,或者它可能会工作。但所有这些都不能保证可靠或确定性。所以,下次您启动时,可能会发生其他事情。

好的,但编译器不应该给我输出。它应该给出一个错误……奥拉夫给了你正确的答案。然而,在大多数微控制器上,常量变量属于ROM内存,这是一个真正的只读内存。使用-墙选项,将显示错误。那么为什么它给我输出。。。。?它应该显示错误…@Superlokkus:请告诉我标准要求编译器检测变量函数参数类型的位置!在我的回答中,我详细阐述了我撤回评论的原因。@DevangKubavat:是的,作为一名优秀的开发人员,有很多事情需要了解。即使你可能不会
了解一切,你可以对我使用的术语做一些研究。不管怎样,你真的应该把它放在你的脑海里。如果你继续学习,你可能会注意到我提到的一些问题。你能详细阐述一下是什么导致了未定义的行为吗?@2501你是什么意思?如果您想要一个列表:该列表在C标准附录J中给出,同时提供信息,它是强制性的,因为它只是总结了其他章节中包含的原因。没有理由投反对票!您可以从附件J中列出原因,但我更喜欢实际报价。不要忘记,反对票是匿名的。在辩论中提出它们是徒劳无益的,猜猜是谁否决了唯一的UB是在scanf期望int*时将const int*传递给scanf。对象本身是可写的。一个简单的scanf转换,如user694733 said或const int p*=func&size;int*arr=int*p;将使代码完全合法。将int*指定给const int*在C中有效。错误仅限于scanf。
scanf("%d",&arr[i]);
void f(int *p)
{
    *p = 0;
}

int main(void)
{
    const int *p = ...;
    f(p);
}