#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;
}
输入尺寸:3
输入[0]:1
输入[1]:2
输入[2]:3
1 2 3
答案 0 :(得分:7)
你刚遇到许多案例中的一个,C允许你射击你的脚 - 无论好坏。
关于您的假设的一些一般性评论:const
是您给予编译器的保证。所以你必须确保不违反合同。 C没有真正的常数[1]。语义const
是一个限定符,它允许编译器进行额外的错误检查。但是这需要编译器知道参数的类型。这对于具有正确原型的函数来说是正确的,但是(通常[2])不适用于具有可变数量的参数(&#34;可变参数函数&#34;)的函数,因为它们的类型不是在编译时给出(并且在运行时没有明确提供)。
在
scanf("%d",&arr[i]);
您实际上将有问题的(见下文)指针类型传递给scanf
。函数本身不检查,但只是期望正确的类型。它不能,因为C在运行时不提供对象的类型。
现代编译器应警告printf
和scanf
的参数类型不匹配。始终启用警告(对于gcc至少使用-Wall -Wextra -Wconversions
)并注意它们。
编辑:经过激烈的讨论,我不得不改变主意。由于最初给出的原因,它似乎是不未定义的行为[3]:将const int *
传递给期望scanf
的{{1}}。
这是因为int *
中的对象malloc
在第一次写入(6.5p6)之前没有有效类型。使用func
在scanf
中发生这种情况。因此,对象的类型为int *
- 无int
。但是,您通过const
进一步访问是有效的。 6.7.3p6只使另一方向的行为不明确(有充分理由)。
只有可变函数才能通过未检测到,因为没有关于函数声明中可用的预期类型的信息。像这样的东西:
const int *
这里编译器会生成一个警告。变量函数属于C无法检查限定符正确性的情况(这也包括例如void f(int *p)
{
*p = 0;
}
int main(void)
{
const int *p = ...;
f(p);
}
)。还有更多,有些非常微妙。
此处唯一未定义行为的情况是传递一个不兼容的不同限定指针(6.7.6.1p2)。
Recomendation:启用警告,但不要依赖编译器检测所有缺陷(不仅仅是const-correctness)。如果您需要更加安全,C语言不是正确的语言。有很好的理由存在像Python,Java等高级语言(C ++介于两者之间)。 OTOH在C语言中的开放式结尾允许在需要时在这些语言中很难完成(如果有的话)。总之:了解你的工具。
注意:您不应该投射volatile
&amp;的结果。 C中的朋友。malloc
没用。它由标准定义为sizeof(char)
。
[1]由于违反合同是未定义的行为,编译器实际上可以将这些数据存储在只读存储器中。例如,这对于运行代码并直接从ROM读取一些数据的微控制器至关重要。
[2]现代编译器可以为参数类型解析1
和printf
系列的格式字符串,并警告错配。但是,这需要此字符串为字符串文字(不是变量)。这是编译器编写的一种礼节,这些函数被广泛使用。
[3]基本上未定义的行为意味着任何事情都可能发生 - 您的计算机可能会逃跑,可能会出现鼻守护进程,或者它可能会起作用。但所有都不能保证可靠或确定性。所以下次你开始其他事情可能会发生。
答案 1 :(得分:1)
很明显,您已关闭所有编译器警告。
您的程序通过将返回int的函数的结果赋值给const int *来调用未定义的行为。编译器应该告诉你,也许你忽略了它,但从那时起所有的几率都没有了。
将const int *传递给scanf。同样,编译器应该已经警告过你,也许你会忽略它,但是所有的可能性都是关闭的。
const int *不会使对象指向不可修改。它告诉编译器不要让你通过那个指针修改任何东西,这就是全部。 malloc返回的存储区域永远不可修改。