我正在通过K& R2并编写一个小程序来测试7.7节中给出的fputs和fgets函数。当我编译时,我收到错误:' fputs'的冲突类型。我没有收到同样的错误' fgets'。根据我的理解,fput和fgets都在stdio.h中,为什么我只得到fput的错误?
如果我将fput重命名为fputs_77,则没有编译错误。如果没有重命名fgets,我怎么知道对它的调用(在getline函数中)是来自程序中的fgets函数而不是stdio.h?
我编译:
gcc -Wall -Wextra -ansi -pedantic -fno-builtin 7.7.c -o 7.7
#include <stdio.h>
#include <string.h>
#define MAXLINE 1000
char *fgets(char *s, int n, FILE *iop);
int fputs(char *s, FILE *iop);
int getline(char *line, int max);
/* demo of functions in 7.7 */
int main(void)
{
char line[MAXLINE];
while (getline(line, MAXLINE) > 0) {
fputs(line, stdout);
}
return 0;
}
/* fgets: get at most n chars from iop */
char *fgets(char *s, int n, FILE *iop)
{
register int c;
register char *cs;
cs = s;
while (--n > 0 && (c = getc(iop)) != EOF) {
if ((*cs++ = c) == '\n') {
break;
}
}
*cs = '\0';
return (c == EOF && cs == s) ? NULL : s;
}
/* fputs: put string s on file iop */
int fputs(char *s, FILE *iop)
{
int c;
while ((c = *s++)) { /* assignment */
putc(c, iop);
}
return ferror(iop) ? EOF : 0;
}
/* getline: read a line, return length */
int getline(char *line, int max)
{
if (fgets(line, max, stdin) == NULL) {
return 0;
} else {
return strlen(line);
}
}
答案 0 :(得分:1)
一般来说,如果所有声明都是兼容的,则允许多次声明函数(和变量)。你声明fgets()
与stdio.h中相同函数的标准库声明兼容(实际上是相同的),并且你的编译器没有问题。您的fputs()
也是如此。
您将该功能声明为
int fputs(char *s, FILE *iop);
,但在C99中,C标准库将其声明为
int fputs(const char *s, FILE *iop);
,在C11中,C标准库将其声明为
int fputs(const char * restrict s, FILE *iop);
。无论哪种方式,函数的第一个参数的额外限定符使标准库的声明与您的声明不兼容,这就是编译器所抱怨的。
由于您的编译器正在接受标准库函数的兼容重新声明和重新定义,因此您有两种可能的修复方法:
另一方面,尽管有上述任何一点,但请注意,重新声明或重新定义标准库函数的标识符作为外部函数,正如您所做的那样,每paragraph 7.1.3/2 of the standard生成一个未定义的行为。因此,即使您的声明相同,编译器也没有义务接受它,并且就C而言,运行时行为可以是任何东西。最远的 最佳 替代方法是提供不会与标准库发生冲突的函数名称。
答案 1 :(得分:0)
因为fgets()
的declration / definition与<stdio.h>
中的标准函数声明不冲突,所以你被允许
只要它们兼容,就有多个外部声明。
但fputs()
的声明与<stdio.h>
中提供的标准声明不同。
注意:由于您正在重新定义标准功能,因此您的程序并不严格符合C标准。最好将功能重命名为其他功能 - 例如my_fgets()
,my_fputs()
等。
虽然getline()
不符合C标准,但它在POSIX标准中。所以,您可能也想重命名它。 -ansi
等同于-std=c90
- 如果您碰巧在POSIX下编译,这可能会妨碍。您也可以将其重命名为my_getline()
。
如果不重命名fgets,我怎么知道对它的调用(在getline函数中)是来自程序中的fgets函数而不是stdio.h?
GNU链接器通常的符号解析顺序是程序中的符号首先出现。这意味着如果您提供兼容的声明(通过编译器诊断),那么您可以确定它是您的 fgets()
。例如,您可以使用函数中的printf()
语句轻松地对此进行测试。