arrays – Getting conflicting types for function in C, why?

arrays – Getting conflicting types for function in C, why?

You are trying to call do_something before you declare it. You need to add a function prototype before your printf line:

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

Or you need to move the function definition above the printf line. You cant use a function before it is declared.

In classic C language (C89/90) when you call an undeclared function, C assumes that it returns an int and also attempts to derive the types of its parameters from the types of the actual arguments (no, it doesnt assume that it has no parameters, as someone suggested before).

In your specific example the compiler would look at do_something(dest, src) call and implicitly derive a declaration for do_something. The latter would look as follows

int do_something(char *, char *)

However, later in the code you explicitly declare do_something as

char *do_something(char *, const char *)

As you can see, these declarations are different from each other. This is what the compiler doesnt like.

arrays – Getting conflicting types for function in C, why?

A C Function-Declaration Backgrounder

In C, function declarations dont work like they do in other languages: The C compiler itself doesnt search backward and forward in the file to find the functions declaration from the place you call it, and it doesnt scan the file multiple times to figure out the relationships either: The compiler only scans forward in the file exactly once, from top to bottom. Connecting function calls to function declarations is part of the linkers job, and is only done after the file is compiled down to raw assembly instructions.

This means that as the compiler scans forward through the file, the very first time the compiler encounters the name of a function, one of two things have to be the case: It either is seeing the function declaration itself, in which case the compiler knows exactly what the function is and what types it takes as arguments and what types it returns — or its a call to the function, and the compiler has to guess how the function will eventually be declared.

(Theres a third option, where the name is used in a function prototype, but well ignore that for now, since if youre seeing this problem in the first place, youre probably not using prototypes.)

History Lesson

In the earliest days of C, the fact that the compiler had to guess types wasnt really an issue: All of the types were more-or-less the same — pretty much everything was either an int or a pointer, and they were the same size. (In fact, in B, the language that preceded C, there were no types at all; everything was just an int or pointer and its type was determined solely by how you used it!) So the compiler could safely guess the behavior of any function just based on the number of parameters that were passed: If you passed two parameters, the compiler would push two things onto the call stack, and presumably the callee would have two arguments declared, and that would all line up. If you passed only one parameter but the function expected two, it would still sort-of work, and the second argument would just be ignored/garbage. If you passed three parameters and the function expected two, it would also still sort-of work, and the third parameter would be ignored and stomped on by the functions local variables. (Some old C code still expects these mismatched-argument rules will work, too.)

But having the compiler let you pass anything to anything isnt really a good way to design a programming language. It worked well in the early days because the early C programmers were mostly wizards, and they knew not to pass the wrong type to functions, and even if they did get the types wrong, there were always tools like lint that could do deeper double-checking of your C code and warn you about such things.

Fast-forward to today, and were not quite in the same boat. C has grown up, and a lot of people are programming in it who arent wizards, and to accommodate them (and to accommodate everyone else who regularly used lint anyway), the compilers have taken on many of the abilities that were previously part of lint — especially the part where they check your code to ensure its type-safe. Early C compilers would let you write int foo = hello; and it would just blithely assign the pointer to the integer, and it was up to you to make sure you werent doing anything stupid. Modern C compilers complain loudly when you get your types wrong, and thats a good thing.

Type Conflicts

So whats all this got to do with the mysterious conflicting-type error on the line of the function declaration? As I said above, C compilers still have to either know or guess what a name means the first time they see that name as they scan forward through the file: They can know what it means it if its an actual function declaration itself (or a function prototype, more on that shortly), but if its just a call to the function, they have to guess. And, sadly, the guess is often wrong.

When the compiler saw your call to do_something(), it looked at how it was invoked, and it concluded that do_something() would eventually be declared like this:

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

Why did it conclude that? Because thats how you called it! (Some C compilers may conclude that it was int do_something(int arg1, int arg2), or simply int do_something(...), both of which are even farther from what you want, but the important point is that regardless of how the compiler guesses the types, it guesses them differently from what your actual function uses.)

Later on, as the compiler scans forward in the file, it sees your actual declaration of char *do_something(char *, char *). That function declaration isnt even close to the declaration that the compiler guessed, which means that the line where the compiler compiled the call was compiled wrong, and the program is just not going to work. So it rightly prints an error telling you that your code isnt going to work as written.

You might be wondering, Why does it assume Im returning an int? Well, it assumes that type because theres no information to the contrary: printf() can take in any type in its variable arguments, so without a better answer, int is as good a guess as any. (Many early C compilers always assumed int for every unspecified type, and assumed you meant ... for the arguments for every function declared f() — not void — which is why many modern code standards recommend always putting void in for the arguments if there really arent supposed to be any.)

The Fix

There are two common fixes for the function-declaration error.

The first solution, which is recommended by many other answers here, is to put a prototype in the source code above the place where the function is first called. A prototype looks just like the functions declaration, but it has a semicolon where the body should be:

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

By putting the prototype first, the compiler then knows what the function will eventually look like, so it doesnt have to guess. By convention, programmers often put prototypes at the top of the file, just under the #include statements, to ensure that theyll always be defined before any potential usages of them.

The other solution, which also shows up in some real-world code, is to simply reorder your functions so that the function declarations are always before anything that calls them! You could move the entire char *do_something(char *dest, const char *src) { ... } function above the first call to it, and the compiler then would know exactly what the function looks like and wouldnt have to guess.

In practice, most people use function prototypes, because you can also take function prototypes and move them into header (.h) files so that code in other .c files can call those functions. But either solution works, and many codebases use both.

C99 and C11

It is useful to note that the rules are slightly different in the newer versions of the C standard. In the earlier versions (C89 and K&R), the compiler really would guess the types at function-call time (and K&R-era compilers often wouldnt even warn you if they were wrong). C99 and C11 both require that the function declaration/prototype must precede the first call, and its an error if it doesnt. But many modern C compilers — mainly for backward compatibility with earlier code — will only warn about a missing prototype and not consider it an error.

Leave a Reply

Your email address will not be published.