在C中获取“相互冲突的函数types”,为什么?

我使用下面的代码:

char dest[5]; char src[5] = "test"; printf("String: %s\n", do_something(dest, src)); char *do_something(char *dest, const char *src) { return dest; } 

do_something的实现在这里并不重要。 当我尝试编译上述我得到这两个exception:

错误:'do_something'的冲突types(在printf调用时)
错误:以前的“do_something”的隐式声明在这里(原型行)

为什么?

在声明它之前,你正试图调用do_something。 你需要在你的printf行之前添加一个函数原型:

 char* do_something(char*, const char*); 

或者您需要将函数定义移到printf行的上方。 声明之前,您不能使用函数。

在“经典”C语言(C89 / 90)中,当你调用一个未声明的函数时,C假定它返回一个int并且试图从实际参数的types派生它的参数types(不,它不假设它没有参数,正如有人build议)。

在你的具体例子中,编译器会查看do_something(dest, src)调用并隐式地派生do_something的声明。 后者看起来如下

 int do_something(char *, char *) 

不过,在后面的代码中,你显式声明了do_something as

 char *do_something(char *, const char *) 

正如你所看到的,这些声明是彼此不同的。 这是编译器不喜欢的。

您必须在使用之前声明该function。 如果函数名称出现在声明之前,C编译器将遵循一定的规则并自行声明。 如果这是错的,你会得到这个错误。

您有两个select:(1)在使用之前对其进行定义,或者(2)在不执行的情况下使用前向声明。 例如:

 char *do_something(char *dest, const char *src); 

注意最后的分号。

在使用之前,你没有声明它。

你需要类似的东西

 char *do_something(char *, const char *); 

在printf之前。

例:

 #include <stdio.h> char *do_something(char *, const char *); char dest[5]; char src[5] = "test"; int main () { printf("String: %s\n", do_something(dest, src)); return 0; } char *do_something(char *dest, const char *src) { return dest; } 

或者,你可以把整个do_something函数放在printf之前。

C戒律#3:

 K&R #3 Thou shalt always prototype your functions or else the C compiler will extract vengence. 

http://www.ee.ryerson.ca:8080/~elf/hack/God.vs.K+R.html

再看一遍:

 char dest[5]; char src[5] = "test"; printf("String: %s\n", do_something(dest, src)); 

关注这一行:

 printf("String: %s\n", do_something(dest, src)); 

你可以清楚的看到do_something函数没有被声明!

如果你再看一点,

 printf("String: %s\n", do_something(dest, src)); char *do_something(char *dest, const char *src) { return dest; } 

你会看到你使用它之后声明了这个函数。

您需要使用以下代码修改此部分:

 char *do_something(char *dest, const char *src) { return dest; } printf("String: %s\n", do_something(dest, src)); 

干杯;)

如果在使用函数之前没有给出函数的原型,那么C假定它接受任意数量的参数并返回一个int。 所以当你第一次尝试使用do_something时,这是编译器正在寻找的函数的types。 这样做应该会产生一个关于“隐式函数声明”的警告。

所以在你的情况下,当你实际上在稍后声明函数的时候,C不允许函数重载,所以它很小心,因为它已经声明了两个具有不同原型但具有相同名称的函数。

简短的回答:在尝试使用它之前声明函数。

ACfunction声明背景资料

在C语言中,函数声明不像其他语言那样工作:C编译器本身不会在文件中向后和向前search,从您调用的地方查找函数的声明,并且不会扫描文件多次找出关系:编译器只能从文件中自上而下地向前扫描一次 。 将函数调用连接到函数声明是链接器作业的一部分,只有将文件编译成原始汇编指令才能完成。

这意味着,当编译器通过文件向前扫描时,编译器第一次遇到一个函数的名字,有两件事情必须是这样的:它要么是看到函数声明本身,在这种情况下,编译器知道到底是什么函数,它作为参数需要什么types,以及它返回的是什么types – 或者是对函数的调用,编译器必须猜测函数最终将如何声明。

(还有第三个选项,名称用在函数原型中,但是现在我们将忽略它,因为如果您首先看到这个问题,那么您可能不会使用原型。)

历史课

在C的早期,编译器必须猜测types的事实并不是一个真正的问题:所有types都差不多是相同的 – 几乎所有东西都是int或者指针,它们是相同的大小。 (事实上​​,在B语言之前的C语言中,根本没有types;一切都只是一个int或指针,它的types完全取决于你如何使用它!)所以编译器可以安全地猜测任何types的行为函数只是基于传递的参数的数量:如果你传递了两个参数,编译器会把两件事情推到调用堆栈上,大概被调用者声明了两个参数,这些参数都是一致的。 如果你只传递了一个参数,但是函数期望是两个,那么它仍然可以工作,而第二个参数只是被忽略/垃圾。 如果你传递了三个参数,并且函数期望了两个参数,那么它仍然可以进行sorting工作,第三个参数将被函数的局部variables忽略和压低。 (一些旧的C代码仍然期望这些不匹配的参数规则也能起作用。)

但是让编译器让你将任何东西传递给任何东西并不是devise编程语言的好方法。 它在早期运行良好,因为早期的C程序员大多是向导,他们不知道错误的types是通过函数传递的,即使错误的types,总会有一些工具比如lint ,检查你的C代码,并警告你这样的事情。

快进到今天,我们不太一样。 C已经成长起来了,很多人都在编程,他们不是巫师,为了适应他们(并且容纳所有经常使用lint的人),编译器已经承担了许多以前的functionlint – 特别是他们检查您的代码,以确保它是types安全的部分。 早期的C编译器会让你写int foo = "hello"; 它只会轻率地把指针指向整数,这是由你来确保你没有做任何愚蠢的事情。 现代C编译器大声抱怨,当你的types错误,这是一件好事。

types冲突

那么,这就是函数声明中那个神秘的冲突types错误呢? 正如我上面所说,C编译器仍然需要知道猜测什么名字意味着他们第一次看到这个名字,通过文件向前浏览:他们可以知道它是什么意思,如果它是一个实际的函数声明本身(或函数“原型”,稍后会有更多的介绍),但是如果仅仅是函数的调用,他们就不得不猜测 。 可悲的是,猜测常常是错误的。

当编译器看到你对do_something()调用时,它看着它是如何被调用的,结论是do_something()最终会被声明如下:

 int do_something(char arg1[], char arg2[]) { ... } 

为什么会这样呢? 因为这就是你所说的 ! (有些C编译器可能会得出这样的结论: int do_something(int arg1, int arg2) ,或者简单地说int do_something(...) ,两者都你想要的更远 ,但重要的一点是,不pipe怎样编译器猜测的types,它猜测他们不同于你的实际function使用。)

稍后,当编译器在文件中向前扫描时,会看到char *do_something(char *, char *) 实际声明。 这个函数声明甚至不接近编译器猜测的声明,这意味着编译器编译调用的那一行是编译错误的,程序不能正常工作。 所以它正确地打印一个错误,告诉你,你的代码不会像写入一样工作。

你可能想知道,“为什么它假设我返回一个int ? 好吧,它假定这种types是因为没有任何相反的信息: printf()可以在其variables参数中使用任何types,所以没有更好的答案, int就像猜测一样。 (许多早期的C编译器总是假定为每个非特定types的int ,并假定你的意思是...为每个函数声明的f() – 不是void – 这就是为什么许多现代代码标准build议总是把void作为参数if真的不应该有。)

修正

有两个常见的修复函数声明错误。

第一个解决scheme,在这里被许多其他的答案推荐,就是把原型放在函数首次被调用的地方之上的源代码中。 原型看起来就像函数的声明一样,但是它的主体应该是一个分号:

 char *do_something(char *dest, const char *src); 

通过首先放置原型,编译器就知道函数最终会是什么样子,所以不必猜测。 按照惯例,程序员经常把原型放在文件的顶部,就在#include语句之下,以确保它们在任何潜在的用法之前总是被定义的。

另一个解决scheme,也出现在一些现实世界的代码,只是简单地重新sorting你的函数,以便函数声明总是任何调用它们之前 ! 您可以将第一次调用之前的整个char *do_something(char *dest, const char *src) { ... }函数移动到该函数中,然后编译器就可以确切地知道函数的样子,而且不必猜测。

在实践中,大多数人使用函数原型,因为你也可以把函数原型.h到头文件( .h )中,以便其他.c文件中的代码可以调用这些函数。 但是任何一个解决scheme都可以工作,而且许多代码库都使用这两者

C99和C11

注意C标准的新版本中的规则略有不同是有用的。 在较早的版本(C89和K&R)中,编译器真的在函数调用时猜测types(如果K&R编译器错误,它们往往不会提醒你)。 C99和C11都要求函数声明/原型必须在第一次调用之前,如果没有,就是错误的。 但是,许多现代的C编译器(主要是为了与以前的代码向后兼容)只会警告一个丢失的原型,不会将其视为错误。

确保函数声明中的types是首先声明的。

/* start of the header file */



struct intr_frame{...}; //must be first!



void kill (struct intr_frame *);



/* end of the header file */

当您修改交streamfunction定义并忘记更新相应的标题定义时,通常会发生这种情况。