program having printf() function invocation without including <stdio.h>

S

sam_cit

Hi Everyone,

int main()
{
printf("not included stdio.h");
}

Yes, i haven't included stdio.h and my compiler would generate a
warning and would assume that it would return a int, my question is how
does the linker manage to link the function invocation to the proper
printf function?
and does including a header file having function prototype help the
linker in any way?
 
K

Kenny McCormack

Hi Everyone,

int main()
{
printf("not included stdio.h");
}

Yes, i haven't included stdio.h and my compiler would generate a
warning and would assume that it would return a int, my question is how
does the linker manage to link the function invocation to the proper
printf function?
and does including a header file having function prototype help the
linker in any way?

The droids will tell you that this is undefined behavior, for all the
usual drecky reasons.
 
S

santosh

Hi Everyone,

int main()
{
printf("not included stdio.h");
}

Yes, i haven't included stdio.h and my compiler would generate a
warning and would assume that it would return a int, my question is how
does the linker manage to link the function invocation to the proper
printf function?
and does including a header file having function prototype help the
linker in any way?

The prototype is primarily for the compiler's benifit, as it specifies
the number and type of parameters the function expects, if any, and the
type of value it returns, if any. It helps the compiler to output the
correct object code when the function is called and to perform the
correct conversions on the return value.

The linker will simply search the library archive files specified for
the label 'printf'/'_printf' and link in the object code. It doesn't
need the function's prototype.

Strictly according to the standard calling a function whose prototype
is not in scope leads to undefined behaviour. It might work for simpler
functions on some implementations. Some compilers like gcc, often have
the ability to "magically" recognise certain common functions and
include the code inline, but all these are implementation specific
behaviour. You cannot portably rely on them.
 
J

jacob navia

(e-mail address removed) a écrit :
Hi Everyone,

int main()
{
printf("not included stdio.h");
}

Yes, i haven't included stdio.h and my compiler would generate a
warning and would assume that it would return a int, my question is how
does the linker manage to link the function invocation to the proper
printf function?
and does including a header file having function prototype help the
linker in any way?

1) Linkers deal with object files, this has nothing to do with header
files.
2) Header files describe interfaces of functions and modules. This has
nothing to do with object files.

Please keep those apart.

Now, the interfaces descriptions are for the compiler, that generates
code according to those descriptions.

When (in C ) a prototype is not in scope, an automatic
prototype is provided by the compiler. It assumes a function that
has an undertemined number of arguments and returns an int.

In both cases, whether a prototype is in scope or not,
an external reference to a function is issued by the compiler.
In this case the object code would contain an external
reference to the function "printf" (or "_printf", it depends
on the compiler).

The linker goes through the object files and sees:
"Mmmm this is an external reference to the printf function.
Let's look if I find it somewhere".

Then, depending on the linker, it will find it or not in the
libraries it uses BY DEFAULT.

Default libraries vary from compiler system to compiler system.
At least the startup code is ALWAYS automatically included,
then, most compilers assume the C library as a default
library, i;e. one that you do not need to include in the
linker command line. Other compilers may differ, for example some
braindead compiler systems force you to write the math
library as an extra command line option, and it is not
included by default, so if you use the sqrt function for
instance, compilation will fail unless you tell the linker to
add the math library.

Others will include a lot of default libraries so that your
code mostly will link without specifying any extra library
Microsoft Visual C for instance will include all these:

comctl32.lib shlwapi.lib kernel32.lib user32.lib gdi32.lib
winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib
oleaut32.lib uuid.lib odbc32.lib odbccp32.lib

Other compiler systems will be less bloated, for instance
lcc-win32 includes just 6-7 libraries.

Then, the answer to your question is simple:
The linker finds the printf function in the C library of
the compiler that is included by default.

jacob
 
C

Christopher Benson-Manica

santosh said:
Strictly according to the standard calling a function whose prototype
is not in scope leads to undefined behaviour.

That's not completely true. Calling a variadic function like printf()
without its prototype in scope yields UB. I'm not enough of a
language lawyer to parse 6.5.2.2 for you, but the fact that Annex J.2
is at pains to note when a function call without a function prototype
yields UB seems to indicate that there are cases where the behavior is
not undefined.
 
R

Richard Heathfield

(e-mail address removed) said:
Hi Everyone,

int main()
{
printf("not included stdio.h");
}

Yes, i haven't included stdio.h

and so you have called a variadic function without a valid prototype in
scope, as a result of which the behaviour of the program is undefined.

May I recommend "The C Programming Language", 2nd edition, by Brian W
Kernighan and Dennis M Ritchie? Reading it (slowly - its information
density is extremely high) will answer many of your questions.
 
K

Keith Thompson

Christopher Benson-Manica said:
That's not completely true. Calling a variadic function like printf()
without its prototype in scope yields UB. I'm not enough of a
language lawyer to parse 6.5.2.2 for you, but the fact that Annex J.2
is at pains to note when a function call without a function prototype
yields UB seems to indicate that there are cases where the behavior is
not undefined.

In C99, you can't call a function unless a declaration for it is
visible, but that declaration needn't be a prototype (i.e., it needn't
specify the parameter types). It *should*, and there's no good reason
not to, but non-prototype declarations are allowed for backward
compatibility.

For example:

void foo();

...

foo("hello", 42);

This is ok if the *definition* of foo() specifies that it returns
void, has two parameters of types char* and int, and that it's not
variadic.
 
R

Richard Tobin

Keith Thompson said:
In C99, you can't call a function unless a declaration for it is
visible, but that declaration needn't be a prototype

In C90 and earlier, if you call a function without a declaration it
will be assumed to return int, and its arguments will be subject to
the default promotions, and it will be assumed to be non-variadic
(variadic functions might have a different calling convention).

In practice, most compilers by default just warn about undeclared
functions, and things often work unless the return type is a
floating-point number or (on systems with ints and pointers of
different size) a pointer. It's a really bad idea to rely on it
though, because it may work on one system but not another, so
take note of compiler warnings!

-- Richard
 
C

Chris Torek

... i haven't included stdio.h and my compiler would generate a
warning and would assume that it would return a int, my question is how
does the linker manage to link the function invocation to the proper
printf function?

Some have described the process by which particular specific linkers
happen to -- by "designed-in luck", as it were, although whether that
is "good luck" or "bad luck" is something of a matter of opinion --
make your call to printf() to a printf() routine in a library.

At least one person has noted that the behavior is officially
undefined.

There are some (rare) implementations on which the linker does
*not* manage to link your call to a library printf. So asking how
it did manage presupposes that you do not have such an implementation
-- it is a little like asking why sheep are white. (White sheep
are white for various reasons, but black sheep do exist.)
and does including a header file having function prototype help the
linker in any way?

It might. It might not. These things depend on the implementation.
Consider C++ for a moment, and note that C++ compilers commonly
use a technique called "name mangling", in which source-code function
names are replaced with link-time "mangled" names that encode the
*type* of the function as well as the original name. There is no
reason a C compiler could not do the same thing, so that:

int foo(void);
...
result = foo();

causes the linker to search for a name like i$foo$v. Including
stdio.h, which has to declare "printf" as having type int(const
char *,...) might tell the compiler to cause the linker to search
for i$printf$V.pc; but failure to include that header would leave
the compiler searching for i$printf$pc, so that the link would
fail.
 
G

Guest

Chris said:
Consider C++ for a moment, and note that C++ compilers commonly
use a technique called "name mangling", in which source-code function
names are replaced with link-time "mangled" names that encode the
*type* of the function as well as the original name. There is no
reason a C compiler could not do the same thing, so that:

int foo(void);
...
result = foo();

causes the linker to search for a name like i$foo$v. Including
stdio.h, which has to declare "printf" as having type int(const
char *,...) might tell the compiler to cause the linker to search
for i$printf$V.pc; but failure to include that header would leave
the compiler searching for i$printf$pc, so that the link would
fail.

While name mangling is not prohibited, it is considerably more
difficult to implement in C, since function prototypes are not
required. There is nothing preventing a program from doing this:

a.c:
int (*get_puts(void))()
{
extern int puts();
return &puts;
}

b.c:
extern int (*get_puts(void))();
int main(void)
{
(*get_puts()) ((const char *) "Hello, world!");
}

When a.c is compiled, the compiler cannot know which types of arguments
puts expects. It may have special knowledge for the standard library
functions, but I could just as easily have used a user-defined function.
 
C

Chris Torek

Chris said:
... There is no reason a C compiler could not do [C++-like name mangling]

While name mangling is not prohibited, it is considerably more
difficult to implement in C, since function prototypes are not
required.

Yes, I actually considered that but decided to omit details from
the sketch I posted.

The trick is that you can declare a function with prototype entirely
omitted:

/* optional: include "extern" keyword */ T func();

or with a full prototype:

/* extern */ T func(T1 arg1, T2 arg2, T3 arg3);

but not with any sort of "partial prototype". Moreover, a prototype
is always required if the function is variadic. Given the syntax I
suggested -- type-code, $, function-name, $, "V." for variadic, then
a sequence of type-codes separated by "$" signs, the linker would be
obligated to match a compiler reference of:

i$puts

to an actual:

i$puts$pc

symbol. However, i$puts$pi -- int puts(int *) -- would not match
to i$puts$pc -- int puts(char *). (Note that I also dropped "const"
in this particular notation. I am not sure off-hand whether it
would be OK to require const and volatile qualifiers to match.)

Because prototypes are required for variadic functions, "i$printf"
would not match "i$printf$V.pc", even though "i$puts" does match
"i$puts$pc".

The linker algorithm, in this notation, is more or less:

/*
* Test for function call symbol match.
* Return true if ref (a reference) matches def (a definition).
* The arguments may be modified temporarily.
*/
int function_matchp(char *ref, char *def) {
int ndollar = count_char(ref, '$');

if (ndollar == 1) {
/*
* The reference did not use a prototype, so we match
* any non-variadic function with the same name and
* return type.
*/
char *args;

args = find_arguments(def); /* roughly, two strchr()s for '$' */
if (args == NULL)
panic("function_matchp");
if (strncmp(args, "V.") == 0) /* def says it's variadic */
return 0; /* can't match without a prototype */

/* match against just the type and name */
if (strncmp(ref, def, args - def) != 0)
return 0;
if (warningcontrol.sloppy_match)
warn("matched non-prototyped call to %F to %F\n", ref, def);
return 1;
}

/*
* Reference included a prototype, so go for exact match.
*/
return strcmp(ref, def) == 0;
}
 
G

Guest

Chris said:
Chris said:
... There is no reason a C compiler could not do [C++-like name mangling]

While name mangling is not prohibited, it is considerably more
difficult to implement in C, since function prototypes are not
required.

Yes, I actually considered that but decided to omit details from
the sketch I posted.

The trick is that you can declare a function with prototype entirely
omitted:

/* optional: include "extern" keyword */ T func();

or with a full prototype:

/* extern */ T func(T1 arg1, T2 arg2, T3 arg3);

but not with any sort of "partial prototype". Moreover, a prototype
is always required if the function is variadic. Given the syntax I
suggested -- type-code, $, function-name, $, "V." for variadic, then
a sequence of type-codes separated by "$" signs, the linker would be
obligated to match a compiler reference of:

i$puts

to an actual:

i$puts$pc

symbol. However, i$puts$pi -- int puts(int *) -- would not match
to i$puts$pc -- int puts(char *).

That would be possible.
(Note that I also dropped "const"
in this particular notation. I am not sure off-hand whether it
would be OK to require const and volatile qualifiers to match.)

The intent is that it is not allowed, and this intent is expressed in
the footnote attached to 6.2.5p25, but the normative text does not
state this (hence the cast in my example).
Because prototypes are required for variadic functions, "i$printf"
would not match "i$printf$V.pc", even though "i$puts" does match
"i$puts$pc".

The linker algorithm, in this notation, is more or less:

That's unnecessarily complicated. :) A compiler that sees a definition
of a function compatible with an unprototyped declaration may choose to
emit this function under two different names. Using memcpy because of
the trivial implementation, the compiler would compile

void *memcpy(void *restrict s1, const void *restrict s2, size_t n)
{
return memmove(s1, s2, n);
}

into

..extern pv$memcpy
pv$memcpy:
..extern pv$memcpy$pv$pv$i
pv$memcpy$pv$pv$i:
jmp memmove

This way, the linker would not need any knowledge of the meaning of the
dollar signs, and the logic to detect whether a function is compatible
with an unprototyped declaration must already be present in the
compiler anyway.
 
U

user923005

Kenny said:
The droids will tell you that this is undefined behavior, for all the
usual drecky reasons.

I have literally seen this sort of thing cause program crash. On some
compilers, the calling convention will tell the compiler how to unpile
the stack. If you compile with an option to (for instance) change who
does the pushing and/or popping or to pass the values in registers,
then the program can crash (or do anything else it wants to).

If you write crappy code, expect a crappy result. It may not
necessarily cause demons or Scott Nudds to come flying out of your left
nostril, but it might fetch a bunch of lawyers from the elevator.
 
B

Ben Pfaff

user923005 said:
Kenny said:
The droids will tell you that this is undefined behavior, for all the
usual drecky reasons.

I have literally seen this sort of thing cause program crash. On some
compilers, the calling convention will tell the compiler how to unpile
the stack. [...]

As far as I know, though, the lack of terminating new-line on the
program's output really is drecky as a reason for undefined
behavior, beyond the mundane possibility of the line not
appearing at all.
 
C

CBFalconer

Ben said:
.... snip ...

As far as I know, though, the lack of terminating new-line on the
program's output really is drecky as a reason for undefined
behavior, beyond the mundane possibility of the line not
appearing at all.

Well, isn't that undefined behaviour. What if that final line was:

"Internal error - ignore previous results".

in a medical treatment recommendation system.

--
"A man who is right every time is not likely to do very much."
-- Francis Crick, co-discover of DNA
"There is nothing more amazing than stupidity in action."
-- Thomas Matthews
<http://www.cs.auckland.ac.nz/~pgut001/pubs/vista_cost.txt>
 
R

Richard Heathfield

Ben Pfaff said:
user923005 said:
Kenny said:
int main()
{
printf("not included stdio.h");
}

The droids will tell you that this is undefined behavior, for all the
usual drecky reasons.

I have literally seen this sort of thing cause program crash. On some
compilers, the calling convention will tell the compiler how to unpile
the stack. [...]

As far as I know, though, the lack of terminating new-line on the
program's output really is drecky as a reason for undefined
behavior, beyond the mundane possibility of the line not
appearing at all.

Um, the reason for the undefined behaviour was the omission of a prototype
for a variadic function.
 
B

Ben Pfaff

Richard Heathfield said:
Ben Pfaff said:
user923005 said:
Kenny McCormack wrote:
int main()
{
printf("not included stdio.h");
}

The droids will tell you that this is undefined behavior, for all the
usual drecky reasons.

I have literally seen this sort of thing cause program crash. On some
compilers, the calling convention will tell the compiler how to unpile
the stack. [...]

As far as I know, though, the lack of terminating new-line on the
program's output really is drecky as a reason for undefined
behavior, beyond the mundane possibility of the line not
appearing at all.

Um, the reason for the undefined behaviour was the omission of a prototype
for a variadic function.

That's another reason, and the most obvious one, but C89 and C99
both clearly state that, for text streams:

Whether the last line requires a terminating new-line
character is implementation-defined.
 
B

Ben Pfaff

CBFalconer said:
Well, isn't that undefined behaviour. What if that final line was:

"Internal error - ignore previous results".

in a medical treatment recommendation system.

Yes, it's undefined behavior. The "drecky" part is that the C
standard doesn't constrain behavior more tightly. It would be
perfectly reasonable in my opinion if it said something like "If
a text stream does not end in a new-line, the implementation may
drop the line or add a new-line itself" instead of making it so
wide-ranging.
 
K

Kenny McCormack

Richard Heathfield said:
Um, the reason for the undefined behaviour was the omission of a prototype
for a variadic function.

Correct. The other is just in the category of "You may not see the
output", not in the "UB" category.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top