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

Dev*_*vat 0 c malloc pointers const

#include<stdio.h>
#include<stdlib.h>
int *func(int *);
int main(void)
{
        int i,size;
        const int *arr=func(&size);
        for(i=0;i<size;i++)
        {
                printf("Enter a[%d] : ",i);
                scanf("%d",&arr[i]);
        }

        for(i=0;i<size;i++)
        {
                printf("%d\t",arr[i]);
        }
        return 0;
}
int *func(int *psize)
{
        int *p;
        printf("Enter the size: ");
        scanf("%d",psize);

        p=(int *)malloc(*psize *sizeof(int));
        return p;
}
Run Code Online (Sandbox Code Playgroud)

输入大小:3

输入[0]:1

输入[1]:2

输入[2]:3

1 2 3

  • 这里,在这段代码中,我使用const关键字不修改'arr'指针指向的数据.
  • 如果我使用const关键字,为什么它给我输出?

too*_*ite 7

你刚刚遇到很多情况中的一个,C允许你射击你的脚 - 无论好坏.

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

在

scanf("%d",&arr[i]);
Run Code Online (Sandbox Code Playgroud)

您实际上将一个有问题的(见下文)指针类型传递给scanf.函数本身不检查,但只是期望正确的类型.它不能,因为C在运行时不提供对象的类型.

一个现代编译器应该警告参数类型不匹配for printf和scanf.始终启用警告(至少使用gcc -Wall -Wextra -Wconversions)并注意它们.

编辑:经过激烈的讨论,我不得不改变主意.由于最初给出的原因,它似乎不是未定义的行为[3]:传递const int *给scanf期望的a int *.

这是因为在第一次写入(6.5p6)之前,malloced in 对象func没有有效类型.这发生在使用.因此,对象具有类型- 否.但是,您通过a进一步访问是有效的.6.7.3p6仅使另一方向未定义行为(有充分理由).scanfint *intconstconst int *

只有可变函数才能通过未检测到,因为没有关于函数声明中可用的预期类型的​​信息.像这样的东西:

void f(int *p)
{
    *p = 0;
}

int main(void)
{
    const int *p = ...;
    f(p);
}
Run Code Online (Sandbox Code Playgroud)

这里编译器会生成一个警告.变量函数属于C无法检查限定符正确性的情况(这也包括例如volatile).有更多,有些是非常微妙的.

这里唯一未定义行为的情况是传递一个不兼容的不同限定指针(6.7.6.1p2).

Recomendation:启用警告,但不要依赖编译器检测所有缺陷(不仅仅是const-correctness).如果您需要更加安全,C语言不是正确的语言.有很好的理由存在像Python,Java等高级语言(C++介于两者之间).OTOH在C语言中的开放式结尾允许在需要时在这些语言中很难完成(如果有的话).无论如何:了解你的工具.


注意:你不应该把malloc朋友的结果投在C中.并且sizeof(char)没用.它由产量标准定义1.


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

[2]现代编译器可以解析参数类型的格式字符串printf和字符串,scanf并警告错配.但是,这需要此字符串为字符串文字(不是变量).这是编译器编写的一种礼节,这些功能被广泛使用.

[3]基本上未定义的行为意味着任何事情都可能发生 - 您的计算机可能会逃跑,可能会出现鼻守护进程,或者它可能会起作用.但所有都不能保证可靠或确定性.所以下次你开始其他事情可能会发生.

  • 你能详细说明(赞成论据)导致未定义行为的原因是什么? (2认同)