为什么这不会给出编译错误

时间:2017-09-26 07:11:09

标签: c

#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);
}

p是指向变量x的常量指针,不能指向其他变量。但为什么我们不在这里得到错误,输出是11 11

2 个答案:

答案 0 :(得分:8)

此代码不违反约束,因此编译器唯一可以预期的是警告,例如: gcc为您提供:

constptr.c: In function ‘main’:
constptr.c:6:9: warning: implicit declaration of function ‘foo’ [-Wimplicit-function-declaration]
         foo(&p);
         ^
constptr.c: At top level:
constptr.c:9:10: warning: conflicting types for ‘foo’
     void foo(int **p)
          ^
constptr.c:6:9: note: previous implicit declaration of ‘foo’ was here
         foo(&p);
         ^

如果您仔细查看这些警告,就会发现隐式声明foo()在使用之前没有原型,新的C标准不允许这样做,但编译器仍然支持它的向后兼容性。在这种情况下,编译器假定原型是int foo(int)。这就是下一次警告的原因( foo 的冲突类型)。

如果你正确地介绍了这样的原型:

#include <stdio.h>

void foo(int **p);

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);
}

您会收到预期的警告:

constptr.c: In function ‘main’:
constptr.c:7:13: warning: passing argument 1 of ‘foo’ discards ‘const’ qualifier from pointer target type
         foo(&p);
             ^
constptr.c:2:10: note: expected ‘int **’ but argument is of type ‘int * const*’
     void foo(int **p);
          ^

所以现在编译器警告你关于丢弃const的转换。 C不会强迫您编写正确的代码,但是无论如何都应该 - 警告告诉您代码不正确并且可能会调用未定义的行为(就像这里的情况一样)。

虽然与您的问题无关,但您的代码包含一个更糟糕的未定义行为的情况:main()中的最后一行(printf()行)取消引用指针现在指向一个自动存储持续时间的对象(又名:局部变量j)已经超出范围,因此不再存在!你的编译器不太可能警告你这个,它仍然是desaster的秘诀。因此,在编写C代码时要始终非常小心。

在这里添加一个非常通用的建议:像这样的问题经常被过去常见的人们问过。#34;现代&#34; 完全定义的编程语言(例如Java,C#等等):您的代码是正确的(和定义的)或错误的,如果它是错误的,您会得到编译错误或运行时异常。 这不是C的工作方式!在C中,任何符合C语法并且不违反语言约束的代码都可以编译并且很多错误只会导致未定义的行为(这意味着在执行该代码时可能会发生任何事情)。这意味着C&#34;信任&#34;程序员做正确的事 - 优点是可以从C源文件创建非常有效的本机机器语言代码,缺点是你自己负责确保你的代码实际上是正确的。开始时的最佳做法是始终启用C编译器为您提供的任何警告(gcc的良好设置将是例如-std=c11 -Wall -Wextra -pedantic)并始终修复出现的任何警告。

答案 1 :(得分:2)

除了“修改一些const”之外,代码as-is实际上应该使用错误或警告进行编译,因为foo的调用没有“正向声明”,它允许编译器采用不同的原型(即int foo(int)),而不是实际定义(即int foo(int**);这可能会导致“foo的冲突类型” - 警告/错误或类似情况。)

修复此问题后,使用以下代码,您至少应该收到编译器警告(如果没有,请打开警告或获得更好的编译器):

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);
}

因此,您会因修改常量值而得到未定义的行为(即使编译器没有给您“错误”)。

BTW:访问指向生命周期结束的(本地)对象的指针(与使用*p = &j和main中的printf一样)是未定义的行为。