我在第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'
答案 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_it
(char
)的参数类型与隐式声明一起使用是非法的。要使其有效,您需要使用适当的类型,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
并不重要,因为你从未使用它的返回值(它没有)作业或表达。因此即使编译器有点混乱,这只会产生警告,而不是错误。由于参数是一个指针,因此促销规则不会再造成冲突。
那就是说 - 在使用之前总是声明你的函数原型是个好主意;通常,您应该打开所有编译器警告,并改进代码,直到它编译时没有警告或错误。