6

I was going through the article - http://www.geeksforgeeks.org/extern-c-in-c/

There are two example given -

int printf(const char *format,...);

int main()
{
    printf("GeeksforGeeks");
    return 0;
}

It say this wont compile because the compiler wont be able to find the mangled version of 'printf' function. However, the below give output.

extern "C"
{
    int printf(const char *format,...);
}

int main()
{
    printf("GeeksforGeeks");
    return 0;
}

This is beacuse extern "C" block prevent the name from being mangled. However, the code run and gives output. From where does it get the definition of 'printf'. I read a post which says 'stdio.h' is included by default. If this is true, below code must run. However, it give error that printf is not defined.

int main()
{
    printf("GeeksforGeeks");
    return 0;
}

Can somebody explain this?

Naved Alam
  • 827
  • 9
  • 25
  • 1
    See here please: http://stackoverflow.com/questions/18877437/undefined-reference-to-errors-when-linking-static-c-library-with-c-code/18879053?s=3|0.6947#18879053 – πάντα ῥεῖ Aug 29 '15 at 10:45
  • 3
    `stdio.h is included by default` Wrong. – deviantfan Aug 29 '15 at 10:45
  • *"However, the code run and gives output."* Which compiler did you use? – dyp Aug 29 '15 at 10:48
  • About the compiling, eg. GCC on Ideone doesn´t like the first code: http://ideone.com/OqQg4g – deviantfan Aug 29 '15 at 10:49
  • 1
    @dyp i used ideone - http://ideone.com/qdJTt3 – Naved Alam Aug 29 '15 at 10:50
  • @NavedAlam That´s the second code, and as you´re saying yourself it should work (and yes, it should work). The first code is wrong (and the third too). So everything is ok? – deviantfan Aug 29 '15 at 10:50
  • @deviantfan, my question is from where did the compiler get the definition of printf in second code? – Naved Alam Aug 29 '15 at 10:52
  • 1
    From the C standard library (or perhaps a compiler intrinsic). – Mat Aug 29 '15 at 10:53
  • 1
    Please read about the difference of a compiler and linker. Both are necessary (and compiling with GCC will call the linker too unless you explicitely prevent it). The compiler doesn´t include any normal header per default, like stdio.h, but the linker has some default libs (binary format this time) – deviantfan Aug 29 '15 at 10:55
  • 1
    Ok, got it. So stdio or any other header file are not included by default but there are libraries which are always linked by default and libraries with printf definition is one of those. – Naved Alam Aug 29 '15 at 11:03
  • 1
    Yes, the full (compiled) printf code is in one of these binary libs the linker is using for your program. The compiler, running before the linker, just needs to know that there is some function printf with parameters X and return type Y which is defined elsewhere. And the reason for header files is that you can use such functions without writing such lines as `int printf(const char *format,...);` for every function. – deviantfan Aug 29 '15 at 11:06
  • Yeah, got it. Thanks – Naved Alam Aug 29 '15 at 11:08
  • 1
    @deviantfan No, calling the standard library printf() function (should) require an explicit extern "C" declaration in scope. The declaration in the first example is that of a C++ function with a particular signature and linking with the C function in the standard library should fail. – ewd Aug 29 '15 at 14:09
  • @ewd Exactly, that´s what I said. `The first code is wrong (and the third too)` – deviantfan Aug 29 '15 at 14:14
  • @deviantfan I see. I didn't realize that when you wrote "int printf(const char *format,...);" that you actually meant "extern "C" int printf(char const *format, ...);" – ewd Aug 29 '15 at 14:20

2 Answers2

4

Your compiler is being helpful by treating printf specially as a built-in.

Sample code "tst.cpp":

int printf(char const *format,...);
int foo(int a, char const *b);

int main() {
    printf("Hello, World!");
    foo(42, static_cast<char const *>("Hello, World!"));
    return 0;
}

When compiling with Microsoft's cl compiler command "cl /c tst.cpp" we can inspect the resulting .obj and find:

00000000 r $SG2552
00000010 r $SG2554
00000000 N .debug$S
00000000 i .drectve
00000000 r .rdata
00000000 t .text$mn
         U ?foo@@YAHHPBD@Z
         U ?printf@@YAHPBDZZ
00e1520d a @comp.id
80000191 a @feat.00
00000000 T _main  

Note that both foo() and printf() are mangled.

But when we compile with /usr/lib/gcc/i686-pc-cygwin/3.4.4/cc1plus.exe via cygwin "g++ -c tst.cpp", we get:

00000000 b .bss
00000000 d .data
00000000 r .rdata
00000000 t .text
         U __Z3fooiPKc
         U ___main
         U __alloca
00000000 T _main
         U _printf

Here foo() is mangled and printf() is not, because the cygwin compiler is being helpful. Most would consider this a compiler defect. If the cygwin compiler is invoked with "g++ -fno-builtin -c tst.cpp" then the problem goes away and both symbols are mangled as they should be.

A more up-to-date g++ gets it right, compiling with with /usr/libexec/gcc/i686-redhat-linux/4.8.3/cc1plus via "g++ -c tst.cpp" we get:

00000000 T main
         U _Z3fooiPKc
         U _Z6printfPKcz

Both foo() and printf() are mangled.

But if we declare printf such that cygwin g++ does not recognize it:

char const * printf(char const *format,...);
int foo(int a, char const *b);

int main() {
    printf("Hello, World!");
    foo(42, static_cast<char const *>("Hello, World!"));
    return 0;
}

Then both foo() and printf() are mangled:

00000000 b .bss
00000000 d .data
00000000 r .rdata
00000000 t .text
         U __Z3fooiPKc
         U __Z6printfPKcz
         U ___main
         U __alloca
00000000 T _main
ewd
  • 167
  • 1
  • 9
  • So, you found that an old version of Cygwin GCC has a bug / unexpected behaviour. ...so what? The whole comparison of different compilers doesn´t matter. OPs compiler did the right thing, but he/she wanted to know why it works that way. – deviantfan Aug 29 '15 at 14:17
  • Impression got that both example give output for OP. – ewd Aug 29 '15 at 14:26
  • Though, this is not directly related to the question. It helped a lot to get things cleared. Thanks. – Naved Alam Aug 29 '15 at 15:10
  • 1
    @deviantfan: It's not a bug in the compiler, but in the code. – Deduplicator Aug 31 '15 at 10:27
1

Let's take a look at the relevant standard quotes:

17.6.2.3 Linkage [using.linkage]

2 Whether a name from the C standard library declared with external linkage has extern "C" or extern "C++" linkage is implementation-defined. It is recommended that an implementation use extern "C++" linkage for this purpose.

17.6.4.3 Reserved names [reserved.names]

2 If a program declares or defines a name in a context where it is reserved, other than as explicitly allowed by this Clause, its behavior is undefined.

17.6.4.3.3 External linkage [extern.names]

1 Each name declared as an object with external linkage in a header is reserved to the implementation to designate that library object with external linkage, both in namespace std and in the global namespace.
2 Each global function signature declared with external linkage in a header is reserved to the implementation to designate that function signature with external linkage.
3 Each name from the Standard C library declared with external linkage is reserved to the implementation for use as a name with extern "C" linkage, both in namespace std and in the global namespace. 4 Each function signature from the Standard C library declared with external linkage is reserved to the implementation for use as a function signature with both extern "C" and extern "C++" linkage, or as a name of namespace scope in the global namespace.

What we get from this is that the compiler may assume that printf in any of the given instances always refers to the standard-library-function printf, and thus can have any amount of info about them baked in. And if you get the declaration wrong, or indeed simply provide your own, it is free to do whatever it wants, including but not limited to magically correcting it.
Anyway, you cannot know which language-linkage it expects.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118