21

In C, you cannot have the function definition/implementation inside the header file. However, in C++ you can have full method implementation inside the header file. Why is the behaviour different?

27

In C, if you define a function in a header file, then that function will appear in each module that is compiled that includes that header file, and a public symbol will be exported for the function. So if function additup is defined in header.h, and foo.c and bar.c both include header.h, then foo.o and bar.o will both include copies of additup.

When you go to link those two object files together, the linker will see that the symbol additup is defined more than once, and won't allow it.

If you declare the function to be static, then no symbol will be exported. The object files foo.o and bar.o will still both contain separate copies of the code for the function, and they will be able to use them, but the linker won't be able to see any copy of the function, so it won't complain. Of course, no other module will be able to see the function, either. And your program will be bloated up with two identical copies of the same function.

If you only declare the function in the header file, but do not define it, and then define it in just one module, then the linker will see one copy of the function, and every module in your program will be able to see it and use it. And your compiled program will contain just one copy of the function.

So, you can have the function definition in the header file in C, it's just bad style, bad form, and an all-around bad idea.

(By "declare", I mean provide a function prototype without a body; by "define" I mean provide the actual code of the function body; this is standard C terminology.)

  • 2
    It is not such a bad idea - this sort of things can be found even in the GNU Libc headers. – SK-logic Mar 9 '11 at 9:49
  • but what about the idiomatic wrapping of the header file in a conditional compilation directive? Then, even with the function declared AND defined in the header, it will only get loaded once. I am new to C, so I may be misunderstanding. – user305964 Oct 18 '17 at 13:14
  • 2
    @papiro The problem is, that wrapping only protects during a single run of the compiler. So if foo.c is compiled to foo.o in one run, bar.c to bar.o in another, and foo.o and bar.o are linked into a.out in a third (as is typical), that wrapping doesn't prevent multiple instances of it, one in each object file. – David Conrad Oct 18 '17 at 18:38
  • Isn't the problem described here what #ifndef HEADER_H is supposed to prevent? – Robert Harvey Aug 9 at 2:39
26

C and C++ behave very much the same in this regard -- you can have inline functions in headers. In C++, any method whose body is inside the class definition is implicitly inline. If you want to do the same in C, declare the functions static inline.

  • "declare the functions static inline"... and you'll still have multiple copies of the function in each translation unit that uses it. In C++ with non-static inline function you'll have only one copy. To actually have the implementation in the header in C, you must 1) mark the implementation as inline (e.g. inline void func(){do_something();}), and 2) actually say that this function will be in some particular translation unit (e.g. void func();). – Ruslan Jun 24 at 15:31
5

The concept of a header file needs a little explanation:

Either you give a file on the command line of compiler, or do a '#include'. Most of the compilers accept a command file with extension c, C, cpp, c++, etc. as the source file. However, they usually include a command-line option to enable usage of any arbitrary extension to a source file also.

Generally the file given on the command line is called 'Source', and the one included is called 'Header'.

The preprocessor step actually take them all and makes everything appear like a single big file to the compiler. What was in the header or in the source is actually not relevant at this point. There is usually an option of a compiler which can show the output of this stage.

So for each file that was given on the compiler command line, a huge file is given to the compiler. This may have code/data which will occupy memory and/or create a symbol to be referenced from other files. Now each of these will generate an 'object' image. The linker can give a 'duplicate symbol' if the same symbol is found in more than two object files which are being linked together. Perhaps this is the reason; it's not advised to put code in a header file, which can create symbols in the object file.

The 'inline' are usually inlined .. but when debugging they may not be inlined. So why does the linker not give multiply defined errors? Simple... These are 'weak' symbols, and as long as all the data/code for a weak symbol from all the objects are same size and content, the linked will keep one copy and drop copy from other objects. It works.

3

You can do this in C99: inline functions are guaranteed to be provided somewhere else, so if a function was not inlined, its definition is translated into a declaration (i.e., implementation is discarded). And, of course, you can use static.

0

C++ standard quotes

The C++17 N4659 standard draft 10.1.6 "The inline specifier" says that methods are implicitly inline:

4 A function defined within a class definition is an inline function.

and then further down we see that inline methods not only can, but must be defined on all translation units:

6 An inline function or variable shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case (6.2).

This is also explicitly mentioned in a note at 12.2.1 "Member functions":

1 A member function may be defined (11.4) in its class definition, in which case it is an inline member function (10.1.6) [...]

3 [ Note: There can be at most one definition of a non-inline member function in a program. There may be more than one inline member function definition in a program. See 6.2 and 10.1.6. — end note ]

GCC 8.3 implementation

main.cpp

struct MyClass {
    void myMethod() {}
};

int main() {
    MyClass().myMethod();
}

Compile and view symbols:

g++ -c main.cpp
nm -C main.o

output:

                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 W MyClass::myMethod()
                 U __stack_chk_fail
0000000000000000 T main

then we see from man nm that the MyClass::myMethod symbol is marked as weak on the ELF object files, which implies that it can appear on multiple object files:

"W" "w" The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is used with no error. When a weak undefined symbol is linked and the symbol is not defined, the value of the symbol is determined in a system-specific manner without error. On some systems, uppercase indicates that a default value has been specified.

-4

Probably for the same reason that you must put the full method implementation inside the class definition in Java.

They might look similar, with squiggly brackets and many of the same keywords, but they are different languages.

  • 1
    No, there is actually a real answer, and one of the primary goals of C++ was to be backward compatible with C. – Ed S. Mar 9 '11 at 8:51
  • 4
    No, it was designed to have "A High degree of C compatibility", and "No gratuitous incompatibilities with C". (both from Stroustrup). I agree that a more in-depth answer could be given, to highlight why this particular incompatibility is not gratuitous. Feel free to supply one. – Paul Butcher Mar 9 '11 at 9:19
  • I would have, but Simon Richter already had when I posted that. We can quibble about the difference between "backward compatible" and "A High degree of C compatibility", but the fact remains that this answer is incorrect. The last statement would be correct if we were comparing C# and C++, but not so much with C and C++. – Ed S. Mar 9 '11 at 18:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.