0
votes

I am making my way through K&R2 and wrote a small program to test the fputs and fgets functions given in section 7.7. When I compile, I receive error: conflicting types for 'fputs'. I do not receive the same error for 'fgets'. From what I understand, fputs and fgets are both in stdio.h, so why am I only getting the error for fputs?

If I rename fputs to fputs_77, there are no compile errors. Without also renaming fgets, how do I know the call to it (in the getline function) is from the fgets function in my program and not stdio.h?

I compiled with:

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);
    }
}
2
Note, by the way, that despite my geek reverence for Brian Kernighan and Dennis Ritchie, I have to observe that even the second edition of K&R is now pretty dated. - John Bollinger
@JohnBollinger, thanks for the answer! And ya, I realize K&R is dated, but I'm glad I'm reading it, appreciating it's historical importance. I think without the internet (read: finding helpful people like you on sites like stackoverflow), it would be more challenging to get through. - rsarson
Note: K&R2 is outdated since 18 years and does not cover modern standard C. Get a more recent C book. - too honest for this site
Re-declaring standard functions invokes undefined behaviour. That's one of the problem using outdated books. - too honest for this site

2 Answers

1
votes

Generally speaking, you are permitted to declare functions (and variables) multiple times, provided that all the declarations are compatible. You declare fgets() compatibly with (in fact identically to) the standard library's declaration of the same function in stdio.h, and your compiler has no problem with that. The same is not true for your fputs().

You declare that function as

int fputs(char *s, FILE *iop);

, but in C99, the C standard library declares it as

int fputs(const char *s, FILE *iop);

, and in C11, the C standard library declares it as

int fputs(const char * restrict s, FILE *iop);

. Either way, the extra qualifiers on the function's first parameter make the standard library's declaration incompatible with yours, and that's what the compiler's complaining about.

Inasmuch as your compiler is accepting compatible redeclarations and redefinitions of standard library functions, you have two possible fixes:

  1. change your declarations to match the standard library's, or
  2. avoid including stdio.h, either directly or indirectly.

On the other hand, notwithstanding any of the above, be aware that redeclaring or redefining the identifier of a standard library function as an external function, as you are doing, produces undefined behavior per paragraph 7.1.3/2 of the standard. As such, the compiler is not obligated to accept it even if your declarations are identical, and the runtime behavior can be anything, as far as C is concerned. Far and away your best alternative is to give your functions names that do not collide with the standard library.

0
votes

Because fgets()'s declration/definition doesn't conflict with the standard function's declaration in <stdio.h> and you are allowed to have multiple external declarations as long as they're compatible.

But fputs()'s declaration differs with the standard one provided in <stdio.h>.

NB: Your program is not strictly conforming to the C standard since you are re-defining standard functions. You are better off renaming your functions to something else - such as my_fgets(), my_fputs() etc.

While getline() is not in C standard, it' in POSIX standard. So, you may want to rename it too. -ansi is equivalent to -std=c90 - if you ever happen to compile under POSIX, this might get in the way. You could rename this to my_getline() as well.

Without also renaming fgets, how do I know the call to it (in the getline function) is from the fgets function in my program and not stdio.h?

The usual symbol resolving order done by GNU linker is that the symbols within your programs come first. That means if you provide a compatible declaration (that gets past compiler diagnostics) then you can be sure it's your fgets() that's called. You can easily test this with printf() statements in your functions for example.