C 为什么这并没有给出编译错误 #包括 int main() { int i=10; int*常数p=&i; foo&p; printf(“%d\n”,*p); } 无效foo(整数**p) { int j=11; *p=&j; printf(“%d\n”,**p); }
C 为什么这并没有给出编译错误 #包括 int main() { int i=10; int*常数p=&i; foo&p; printf(“%d\n”,*p); } 无效foo(整数**p) { int j=11; *p=&j; printf(“%d\n”,**p); },c,C,p是指向变量x的常量指针,不能指向其他变量。但是为什么我们不在这里得到错误,输出是11 11?此代码没有违反约束,因此您可以从编译器得到的唯一结果是警告,例如gcc会给您: constptr.c:在函数“main”中: constptr.c:6:9:警告:函数“foo”的隐式声明[-Wimplicit函数声明] foo&p; ^ 施工图c:在顶层: constptr.c:9:10:警告:“foo”的类型冲突 无效foo(整数**p) ^ constptr.c:6:9:注意:前面的“foo”隐式声
p
是指向变量x的常量指针,不能指向其他变量。但是为什么我们不在这里得到错误,输出是11 11
?此代码没有违反约束,因此您可以从编译器得到的唯一结果是警告,例如gcc
会给您:
constptr.c:在函数“main”中:
constptr.c:6:9:警告:函数“foo”的隐式声明[-Wimplicit函数声明]
foo&p;
^
施工图c:在顶层:
constptr.c:9:10:警告:“foo”的类型冲突
无效foo(整数**p)
^
constptr.c:6:9:注意:前面的“foo”隐式声明在这里
foo&p;
^
如果仔细查看这些警告,您会发现它是关于一个隐式声明的:foo()
在使用之前没有原型,这是新的C标准所不允许的,但编译器仍然支持它的向后兼容性。在这种情况下,编译器假定原型是intfoo(int)
。这就是下一个警告的原因(对于foo
,类型冲突)
如果您正确地介绍了这样的原型:
#include <stdio.h>
int main()
{
int i = 10;
int *const p = &i;
foo(&p);
printf("%d\n", *p);
}
void foo(int **p)
{
int j = 11;
*p = &j;
printf("%d\n", **p);
}
因此,现在编译器会警告您,转换会删除const
。C不会强制您编写正确的代码,但无论如何您都应该这样做——警告告诉您您的代码不正确,可能会调用未定义的行为(这里就是这种情况)
尽管与您的问题无关,但您的代码包含了一个更糟糕的未定义行为:您在
main()
(printf()
行)中的最后一行取消引用了一个指针,该指针现在指向自动存储持续时间的对象(aka:local variablej
)这已经超出了范围,因此不再存在了!。编译器不太可能警告您这一点,但它仍然是desaster的配方。所以,在编写C代码时一定要非常小心
在此添加一条非常通用的建议:习惯于完全定义的“现代”编程语言(如Java、C#等)的人经常会问这样的问题:您的代码要么是正确的(并已定义),要么是错误的,如果是错误的,则会出现编译错误或运行时异常这不是C的工作方式在C中,任何遵循C语法且不违反语言约束的代码都可以编译,许多错误只会导致未定义的行为(这意味着在执行该代码时可能发生任何事情)。这意味着C“信任”程序员去做正确的事情——优点是可以从C源文件创建相当高效的本机语言代码,缺点是你要自己负责确保你的代码实际上是正确的。一个最好的做法是总是启用C编译器给你的任何警告(gcc的一个好设置是,例如,
-std=c11-Wall-Wextra-pedantic
),并且总是修复出现的任何警告。这段代码没有违反约束,因此你唯一能从编译器得到的是警告,例如,gcc
为您提供:
constptr.c:在函数“main”中:
constptr.c:6:9:警告:函数“foo”的隐式声明[-Wimplicit函数声明]
foo&p;
^
施工图c:在顶层:
constptr.c:9:10:警告:“foo”的类型冲突
无效foo(整数**p)
^
constptr.c:6:9:注意:前面的“foo”隐式声明在这里
foo&p;
^
如果仔细查看这些警告,您会发现它是关于一个隐式声明的:foo()
在使用之前没有原型,这是新的C标准所不允许的,但编译器仍然支持它的向后兼容性。在这种情况下,编译器假定原型是intfoo(int)
。这就是下一个警告的原因(对于foo
,类型冲突)
如果您正确地介绍了这样的原型:
#include <stdio.h>
int main()
{
int i = 10;
int *const p = &i;
foo(&p);
printf("%d\n", *p);
}
void foo(int **p)
{
int j = 11;
*p = &j;
printf("%d\n", **p);
}
因此,现在编译器会警告您,转换会删除const
。C不会强制您编写正确的代码,但无论如何您都应该这样做——警告告诉您您的代码不正确,可能会调用未定义的行为(这里就是这种情况)
尽管与您的问题无关,但您的代码包含了一个更糟糕的未定义行为:您在
main()
(printf()
行)中的最后一行取消引用了一个指针,该指针现在指向自动存储持续时间的对象(aka:local variablej
)这已经超出了范围,因此不再存在了!。编译器不太可能警告您这一点,但它仍然是desaster的配方。所以,在编写C代码时一定要非常小心
在此添加一条非常通用的建议:习惯于完全定义的“现代”编程语言(如Java、C#等)的人经常会问这样的问题:您的代码要么是正确的(并已定义),要么是错误的,如果是错误的,则会出现编译错误或运行时异常这不是C的工作方式在C中,任何遵循C语法且不违反语言约束的代码都可以编译,许多错误只会导致未定义的行为(这意味着在执行该代码时可能发生任何事情)。这意味着C“信任”程序员去做正确的事情——优点是可以从C源文件创建相当高效的本机语言代码,缺点是你要自己负责确保你的代码实际上是正确的。
void foo3(int **p);
int main()
{
int i = 10;
int *const p = &i;
foo3(&p); // passing int * const * to parameter of type int ** discards const qualifiers
printf("%d\n", *p);
}
void foo3(int **p)
{
int j = 11;
*p = &j;
printf("%d\n", **p);
}