省略前瞻性声明(原型)

时间:2014-05-28 04:02:56

标签: c function

我在第14课就着名的“学习C艰难的方式”在线课程。

在该课程中,它在C中引入了前向声明的概念。 代码示例中有两个前向声明。其中一个可以被注释掉,代码仍然编译,但另一个不能被注释掉。对我来说,他们看起来同样重要。

这是代码。它只是打印出所有字符及其十六进制代码,如果它们来自字母表,否则它会跳过它们。

两个编译器输出位于代码的底部。 有人可以解释为什么一个错误而另一个错误?

#include <stdio.h>
#include <ctype.h>

// forward declarations
int can_print_it(char ch);       //NOT OK to skip(??)
void print_letters(char arg[]);  //OK to skip(??)

void print_arguments(int argc, char *argv[])
{
    int i = 0;

    for(i = 0; i < argc; i++) {
        print_letters(argv[i]);
    }
}

void print_letters(char arg[])
{
    int i = 0;

    for(i = 0; arg[i] != '\0'; i++) {
        char ch = arg[i];

        if(can_print_it(ch)) {
            printf("'%c' == %d ", ch, ch);
        }
    }

    printf("\n");
}

int can_print_it(char ch)
{
    return isalpha(ch) || isblank(ch);
}


int main(int argc, char *argv[])
{
    print_arguments(argc, argv);
    return 0;
}

如果我注释掉第一个前向声明(仅限第一个),就会发生这种情况:

cc -Wall -g    ex14.c   -o ex14
ex14.c: In function ‘print_letters’:
ex14.c:24:9: warning: implicit declaration of function ‘can_print_it’ [-Wimplicit-function-declaration]
ex14.c: At top level:
ex14.c:32:5: error: conflicting types for ‘can_print_it’
ex14.c:33:1: note: an argument type that has a default promotion can’t match an empty parameter name list declaration
ex14.c:24:12: note: previous implicit declaration of ‘can_print_it’ was here
make[1]: *** [ex14] Error 1
make[1]: Leaving directory `/home/andrew/c_tutorials/lesson14/ex14_original'
make: *** [all] Error 2

如果我注释掉第二个声明(仅限第二个声明),则会发生这种情况:

cc -Wall -g    ex14.c   -o ex14
ex14.c: In function ‘print_arguments’:
ex14.c:13:9: warning: implicit declaration of function ‘print_letters’ [-Wimplicit-function-declaration]
ex14.c: At top level:
ex14.c:17:6: warning: conflicting types for ‘print_letters’ [enabled by default]
ex14.c:13:9: note: previous implicit declaration of ‘print_letters’ was here
make[1]: Leaving directory `/home/andrew/c_tutorials/lesson14/ex14_original'

2 个答案:

答案 0 :(得分:23)

好的编译器暗示了为什么会这样。这里至关重要的是:

ex14.c:32:5: error: conflicting types for ‘can_print_it’
ex14.c:33:1: note: an argument type that has a default promotion can’t match an empty parameter name list declaration

can_print_it的参数有一个默认促销,因此它不能有隐式声明。很好的阅读就在这里:Default argument promotions in C function calls。基本上,can_print_itchar)的参数类型与隐式声明一起使用是非法的。要使其有效,您需要使用适当的类型,char它是int。对于其他类型,您可以查看链接的问题和答案。

print_letters没有这样的参数,它的参数是指针类型。

旁注:正如人们所看到的,有3个错误的答案,让人感到困惑。隐式声明不经常使用,可能很棘手。 IMO一般,或至少是实际应用,不鼓励使用它们。然而,它们完全合法。

答案 1 :(得分:6)

您提供了一个函数原型,以便编译器知道在代码中第一次遇到该函数时该怎么做。具体来说,如果它没有其他信息,编译器将

  • 假设返回值为int
  • 宣传论点:
    • “整数类型”到int(例如,char变为int
    • float提升为double
    • 指针成为int
    • 的指针

问题在于,当您将char转换为int时,有效字节可能最终偏离(例如)您认为存储它的3个字节 - 因为像0x33这样的值可能会存储为0x00000033。根据机器的体系结构,这将导致问题。

指针也是如此。指针“always”具有相同的大小,并且总是指向对象的第一个字节(这并不总是正确的......我们中的一些人记得“近”和“远”指针,而不是怀旧)。因此,即使编译器可能认为它正在传递指向int的指针,后续解释(通过未声明的函数)作为指向char的指针也是如此。不会引起问题。

当编译器认为第二个函数返回void时,你的第二个函数被声明为int并不重要,因为你从未使用它的返回值(它没有)作业或表达。因此即使编译器有点混乱,这只会产生警告,而不是错误。由于参数是一个指针,因此促销规则不会再造成冲突。

那就是说 - 在使用之前总是声明你的函数原型是个好主意;通常,您应该打开所有编译器警告,并改进代码,直到它编译时没有警告或错误。