invoke function without declaration

Discussion in 'C Programming' started by Stanley Rice, Nov 15, 2011.

  1. Stanley Rice

    Stanley Rice Guest

    Dear all

    Once again I come across some question about linking. In f1.c, I have
    the following function definition.
    ----------------f1.c------------
    #include <stdio.h>
    void my_print(float a, int b)
    {
    printf("%f\t%d\n", a, b);
    }

    int add(int a, int b)
    {
    return a + b;
    }

    In main.c, I call the two function without declaration.
    ----------------main.c-------------
    int main(void)
    {
    int a = add(1, 2);
    printf("%d\n", a);
    my_print(2.0, a);

    return 0;
    }

    Notice here, in file main.c, I don't include any headers, say, 'f1.h',
    'stdio.h', and don't declare anything deliberately, trying to get what
    the result would like to be.

    In my machine, with compiler gcc on ubuntu, I got some warnings while
    compiling the code, and the binary file runs happily, printing the
    following lines:
    3
    0.000000 1073741824

    From the result, I got that
    1. the function 'add' and the standard library function 'printf'
    performs well, because the first line printed is 3.
    2. the function 'my_print' doesn't work, the printed line obviously
    shows.

    All three function are defined external without declaration in file
    main.c. But why the first two works well, but the last one fails? Is
    it undefined in ISO C?

    Thanks in advance.
    Stanley Rice, Nov 15, 2011
    #1
    1. Advertising

  2. On 11/15/2011 10:20 AM, Stanley Rice wrote:
    > Dear all
    >
    > Once again I come across some question about linking. In f1.c, I have
    > the following function definition.
    > ----------------f1.c------------
    > #include <stdio.h>
    > void my_print(float a, int b)
    > {
    > printf("%f\t%d\n", a, b);
    > }
    >
    > int add(int a, int b)
    > {
    > return a + b;
    > }
    >
    > In main.c, I call the two function without declaration.
    > ----------------main.c-------------
    > int main(void)
    > {
    > int a = add(1, 2);
    > printf("%d\n", a);
    > my_print(2.0, a);
    >
    > return 0;
    > }
    >
    > Notice here, in file main.c, I don't include any headers, say, 'f1.h',
    > 'stdio.h', and don't declare anything deliberately, trying to get what
    > the result would like to be.
    >
    > In my machine, with compiler gcc on ubuntu, I got some warnings while
    > compiling the code, and the binary file runs happily, printing the
    > following lines:
    > 3
    > 0.000000 1073741824
    >
    > From the result, I got that
    > 1. the function 'add' and the standard library function 'printf'
    > performs well, because the first line printed is 3.
    > 2. the function 'my_print' doesn't work, the printed line obviously
    > shows.
    >
    > All three function are defined external without declaration in file
    > main.c. But why the first two works well, but the last one fails? Is
    > it undefined in ISO C?
    >
    > Thanks in advance.


    when C does not know the exact type of arguments/return values it
    assumes 'int'. as long as arguments are in fact ints, it works. by when
    they differ, problems arise, since compiler put different types on
    stack, and tries to take different inside the function.

    it gets even funnier when architecture changes. your code gives
    different results on ia32 and amd64.

    real life situation - about a week ago a friend of mine found similar
    bug in code he develops. for some reason it crashed on amd64, while
    working stable on ia32. some debugging shown that stdlib.h was not
    included, thus malloc was not defined, thus compiler assumed it returns
    'int' instead of 'void*'. it worked well on ia32, since there, on GCC
    sizeof(int)==sizeof(void*). on the amd64 however
    sizeof(int)<sizeof(void*) and program used some random memory space,
    instead of newly allocated block.

    notice that similar problems may arise if you do not include some header
    (say: aaa.h), that you use calls from, but other header you do include
    (say bbb.h) does that (i.e. bbb.h includes aaa.h). it will work on your
    machine, but when moved to different implementation of library with
    bbb.h file, this implicit include may not take place (i.e. bbb.h may
    DON'T include aaa.h), users of that system will have, possibly
    nontrivial, problems with your code...

    summary is short. do NOT never, ever use functions that were not
    declared. also always include headers of the functions that you use,
    even if you "know" they are already included by some other header.

    --
    pozdrawiam serdecznie / best regards,
    Bartek 'BaSz' Szurgot

    http://www.baszerr.org
    bartek szurgot, Nov 15, 2011
    #2
    1. Advertising

  3. Stanley Rice

    Stanley Rice Guest

    On Nov 15, 6:10 pm, bartek szurgot <> wrote:
    > On 11/15/2011 10:20 AM, Stanley Rice wrote:
    >
    >
    >
    >
    >
    >
    >
    >
    >
    > > Dear all

    >
    > > Once again I come across some question about linking. In f1.c, I have
    > > the following function definition.
    > > ----------------f1.c------------
    > > #include <stdio.h>
    > > void my_print(float a, int b)
    > > {
    > >     printf("%f\t%d\n", a, b);
    > > }

    >
    > > int add(int a, int b)
    > > {
    > >     return a + b;
    > > }

    >
    > > In main.c, I call the two function without declaration.
    > > ----------------main.c-------------
    > > int main(void)
    > > {
    > >     int a = add(1, 2);
    > >     printf("%d\n", a);
    > >     my_print(2.0, a);

    >
    > >     return 0;
    > > }

    >
    > > Notice here, in file main.c, I don't include any headers, say, 'f1.h',
    > > 'stdio.h', and don't declare anything deliberately, trying to get what
    > > the result would like to be.

    >
    > > In my machine, with compiler gcc on ubuntu, I got some warnings while
    > > compiling the code, and the binary file runs happily, printing the
    > > following lines:
    > > 3
    > > 0.000000    1073741824

    >
    > > From the result, I got that
    > > 1. the function 'add' and the standard library function 'printf'
    > > performs well, because the first line printed is 3.
    > > 2. the function 'my_print' doesn't work, the printed line obviously
    > > shows.

    >
    > > All three function are defined external without declaration in file
    > > main.c. But why the first two works well, but the last one fails? Is
    > > it undefined in ISO C?

    >
    > > Thanks in advance.

    >
    > when C does not know the exact type of arguments/return values it
    > assumes 'int'. as long as arguments are in fact ints, it works. by when
    > they differ, problems arise, since compiler put different types on
    > stack, and tries to take different inside the function.
    >
    > it gets even funnier when architecture changes. your code gives
    > different results on ia32 and amd64.
    >
    > real life situation - about a week ago a friend of mine found similar
    > bug in code he develops. for some reason it crashed on amd64, while
    > working stable on ia32. some debugging shown that stdlib.h was not
    > included, thus malloc was not defined, thus compiler assumed it returns
    > 'int' instead of 'void*'. it worked well on ia32, since there, on GCC
    > sizeof(int)==sizeof(void*). on the amd64 however
    > sizeof(int)<sizeof(void*) and program used some random memory space,
    > instead of newly allocated block.
    >
    > notice that similar problems may arise if you do not include some header
    > (say: aaa.h), that you use calls from, but other header you do include
    > (say bbb.h) does that (i.e. bbb.h includes aaa.h). it will work on your
    > machine, but when moved to different implementation of library with
    > bbb.h file, this implicit include may not take place (i.e. bbb.h may
    > DON'T include aaa.h), users of that system will have, possibly
    > nontrivial, problems with your code...
    >
    > summary is short. do NOT never, ever use functions that were not
    > declared. also always include headers of the functions that you use,
    > even if you "know" they are already included by some other header.
    >
    > --
    > pozdrawiam serdecznie / best regards,
    > Bartek 'BaSz' Szurgot
    >
    > http://www.baszerr.org


    I found a similar question in comp.lang.c FAQ list.Qustion 1.25

    Functions which are called without a declaration in scope, perhaps
    because the first call precedes the function's definition, are assumed
    to be declared as if by:

    extern int f();
    That is, an undeclared function is assumed to return int, and to
    accept an unspecified number of arguments (though there must be a
    fixed number of them and none may be ``narrow'').

    The statement above is not consistant with what you said,
    'when C does not know the exact type of arguments/return values it
    assumes 'int'. as long as arguments are in fact ints, '

    The points is that what on earth the type of argument in the function
    which is not declared is, int or not specified at all?
    As you can see that even I pass an int variable to the function
    that requires an int type, the result is confusing.
    Does the standard say something about this, I got a draft, but
    don't know where to get the point.
    Stanley Rice, Nov 15, 2011
    #3
  4. Stanley Rice <> writes:
    <snip>
    >> On 11/15/2011 10:20 AM, Stanley Rice wrote:

    <snip>
    >> > ----------------f1.c------------
    >> > #include <stdio.h>
    >> > void my_print(float a, int b)
    >> > {
    >> >     printf("%f\t%d\n", a, b);
    >> > }

    >>
    >> > int add(int a, int b)
    >> > {
    >> >     return a + b;
    >> > }

    >>
    >> > In main.c, I call the two function without declaration.
    >> > ----------------main.c-------------
    >> > int main(void)
    >> > {
    >> >     int a = add(1, 2);
    >> >     printf("%d\n", a);
    >> >     my_print(2.0, a);

    >>
    >> >     return 0;
    >> > }

    <snip>
    > I found a similar question in comp.lang.c FAQ list.Qustion 1.25
    >
    > Functions which are called without a declaration in scope, perhaps
    > because the first call precedes the function's definition, are assumed
    > to be declared as if by:
    >
    > extern int f();
    > That is, an undeclared function is assumed to return int, and to
    > accept an unspecified number of arguments (though there must be a
    > fixed number of them and none may be ``narrow'').

    <snip>
    > Does the standard say something about this, I got a draft, but
    > don't know where to get the point.


    Most of what you want is in 6.5.2.2 paragraph 6.

    I don't think it's productive to try to find out about these sorts of
    rules by trying things out. It's very easy to think that, because
    something works, it's valid C. For example, if you'd left out my_print
    you might have concluded that the program is correct when, in fact,
    calling a variadic function (like printf) without a prototype in scope
    is undefined.

    --
    Ben.
    Ben Bacarisse, Nov 15, 2011
    #4
  5. Stanley Rice <> writes:
    [...]
    > I found a similar question in comp.lang.c FAQ list.Qustion 1.25
    >
    > Functions which are called without a declaration in scope, perhaps
    > because the first call precedes the function's definition, are assumed
    > to be declared as if by:
    >
    > extern int f();
    > That is, an undeclared function is assumed to return int, and to
    > accept an unspecified number of arguments (though there must be a
    > fixed number of them and none may be ``narrow'').

    [...]

    That's no longer the case in C99. In C99, a call to an undeclared
    function is a constraint violation. (It's a bad idea in either version
    of the language.)

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Nov 15, 2011
    #5
  6. Ben Bacarisse <> writes:
    <snip>
    > Most of what you want is in 6.5.2.2 paragraph 6.


    And, I have been reminded, paragraph 1 as well.

    <snip>
    --
    Ben.
    Ben Bacarisse, Nov 15, 2011
    #6
  7. Stanley Rice

    James Kuyper Guest

    On 11/15/2011 04:20 AM, Stanley Rice wrote:
    > Dear all
    >
    > Once again I come across some question about linking. In f1.c, I have
    > the following function definition.
    > ----------------f1.c------------
    > #include <stdio.h>
    > void my_print(float a, int b)
    > {
    > printf("%f\t%d\n", a, b);
    > }
    >
    > int add(int a, int b)
    > {
    > return a + b;
    > }
    >
    > In main.c, I call the two function without declaration.
    > ----------------main.c-------------
    > int main(void)
    > {
    > int a = add(1, 2);
    > printf("%d\n", a);
    > my_print(2.0, a);
    >
    > return 0;
    > }
    >
    > Notice here, in file main.c, I don't include any headers, say, 'f1.h',
    > 'stdio.h', and don't declare anything deliberately, trying to get what
    > the result would like to be.


    main.c relies upon the implicit int rules from C90. They cause add() to,
    in effect, be implicitly declared as taking two arguments of type 'int'
    and returning an int. They cause printf() in main.c to be treated as if
    took a char* and an int as arguments, and returns an int. They cause
    my_print() to be treated as if it took a double and an int as arguments,
    and returns an int.

    These implicit declarations are correct for add(), but not for printf()
    or my_print(). As a result, the behavior of your program is undefined.
    The implementation is not required to issue a diagnostic for such code.

    Since the problem occurs due to a mismatch between the implicit
    declaration in one translation unit, and the actual definition in
    another translation unit, the compiler cannot detect this mismatch. The
    linker could have enough information to detect it, but it doesn't need
    that information in order to do it's job, and therefore doesn't
    necessarily have access to such information.

    > In my machine, with compiler gcc on ubuntu, I got some warnings while
    > compiling the code,


    Good. However, it would have been better to compile in C99 mode, where
    the implicit int rule has been dropped. Your program provides a prime
    example of why it was dropped. At least one diagnostic message for this
    code due to missing declarations is mandatory in C99, and gcc would
    almost certainly refuse to continue after issuing that message.

    and the binary file runs happily, printing the
    > following lines:
    > 3
    > 0.000000 1073741824
    >
    > From the result, I got that
    > 1. the function 'add' and the standard library function 'printf'
    > performs well, because the first line printed is 3.


    The printf() call in main() has undefined behavior; that it appeared to
    work is a coincidence; it didn't have to work. And even though it
    appeared to work, it might have malfunctioned in some way that caused
    something else to go wrong.

    > 2. the function 'my_print' doesn't work, the printed line obviously
    > shows.


    It may seem obvious, but it's wrong. The function my_print() should work
    perfectly, if called correctly, and if stdout is writeable; it's the
    call to my_print() inside main() which is defective, not my_print() itself.

    > All three function are defined external without declaration in file
    > main.c. But why the first two works well, but the last one fails? Is
    > it undefined in ISO C?


    Yes.
    --
    James Kuyper
    James Kuyper, Nov 15, 2011
    #7
  8. Stanley Rice <> wrote:
    > Dear all
    >
    > Once again I come across some question about linking. In f1.c, I have
    > the following function definition.
    > ----------------f1.c------------
    > #include <stdio.h>
    > void my_print(float a, int b)
    > {
    > printf("%f\t%d\n", a, b);
    > }
    >
    > int add(int a, int b)
    > {
    > return a + b;
    > }
    >
    > In main.c, I call the two function without declaration.
    > ----------------main.c-------------
    > int main(void)
    > {
    > int a = add(1, 2);
    > printf("%d\n", a);
    > my_print(2.0, a);
    >
    > return 0;
    > }
    >


    Implicit declarations are a constraint violation in C99 but valid
    in C90.
    In C90, my_print(2.0, a) implicitly returns int, and has to take two
    arguments: The first is of type double (float is promoted to
    double) while the second is of type int. Since the prototype of
    my_print doesn't match, the behavior is undefined, even in C90.

    --
    André Gillibert
    André Gillibert, Nov 15, 2011
    #8
  9. James Kuyper <> writes:
    [...]
    > Good. However, it would have been better to compile in C99 mode, where
    > the implicit int rule has been dropped. Your program provides a prime
    > example of why it was dropped. At least one diagnostic message for this
    > code due to missing declarations is mandatory in C99, and gcc would
    > almost certainly refuse to continue after issuing that message.


    Almost, but not quite. gcc's diagnostic for a call to an undeclared
    function, even with "-std=c99 -pedantic", is merely a warning; after
    printing the warning, it proceeds under C90 rules. You can make it a
    fatal error by specifying "-pedantic-errors".

    This is conforming, of course; the standard doesn't distinguish between
    fatal errors and non-fatal warnings; it only requires diagnostics.

    [...]

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Nov 15, 2011
    #9
  10. Acid Washed China Blue Jeans <> writes:
    > In article <>,
    > Stanley Rice <> wrote:
    >
    >> #include <stdio.h>
    >> void my_print(float a, int b)
    >> {
    >> printf("%f\t%d\n", a, b);
    >> }

    >
    >> All three function are defined external without declaration in file
    >> main.c. But why the first two works well, but the last one fails? Is
    >> it undefined in ISO C?

    >
    > If you don't declare a function f, it is assumed to be
    > int f()
    > which passes all arguments with var-args rules.


    Not exactly, if by "var-args rules" you mean as if calling a variadic
    function (one with ", ..." in its declaration).

    Under C90 rules, the implicit declaration is for a function that returns
    int with a fixed number of parameters corresponding to the promoted
    types of the actual arguments in the call. So for

    my_print(2.0, a);

    the implicit declaration is

    int my_print(double, int);

    *not*

    int my_print(...);

    (which isn't even a valid declaration; variadic functions must have at
    least one non-variadic parameter).

    > So every float argument is promoted to double. However your function expects a
    > float which on most machines is passed in different manner and binary format.
    > Much confusion will occur.


    Right.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Nov 15, 2011
    #10
  11. Keith Thompson <> wrote:
    > Acid Washed China Blue Jeans <> writes:
    > > Stanley Rice <> wrote:
    > > >
    > > > #include <stdio.h>
    > > > void my_print(float a, int b)
    > > > {
    > > >     printf("%f\t%d\n", a, b);
    > > > }
    > > >
    > > > All three function are defined external without declaration in
    > > > file main.c. But why the first two works well, but the last one
    > > > fails? Is it undefined in ISO C?

    > >
    > > If you don't declare a function f, it is assumed to be
    > >         int f()
    > > which passes all arguments with var-args rules.

    >
    > Not exactly, if by "var-args rules" you mean as if calling a variadic
    > function (one with ", ..." in its declaration).


    If they meant default argument promotions are applied to all
    arguments,
    then that _is_ how they're passed.

    > Under C90 rules, the implicit declaration is for a function that
    > returns int with a fixed number of parameters corresponding to the
    > promoted types of the actual arguments in the call.


    No, the implicit declaration is simply int f(), i.e. a function
    returning int with an unspecified number of parameters.

    > So for
    >
    >     my_print(2.0, a);
    >
    > the implicit declaration is
    >
    >     int my_print(double, int);


    No, it's int my_print(). [Which, BTW, isn't compatible with the
    definition
    in the OP's post above.]

    > *not*
    >
    >     int my_print(...);
    >
    > (which isn't even a valid declaration; variadic functions must have
    > at least one non-variadic parameter).


    True. But to demonstrate that there are no implicit parameters in the
    implicit declaration consider...

    #include <stdio.h>

    int main(void)
    {
    int foo();
    int i = 42;
    int *ip = &i;
    const int *cip = &i;

    foo(ip);
    foo(cip);

    return 0;
    }

    int foo(int *cip)
    {
    printf("%d\n", *cip);
    return 0;
    }

    If the first call to foo imposed an implicit declaration with fixed
    parameters based on the promoted argument type, then the second call
    to foo() would violate a constraint. It doesn't under C90, and
    wouldn't under C99 if foo had been declared int foo() prior to
    calling.

    --
    Peter
    Peter Nilsson, Nov 17, 2011
    #11
  12. Peter Nilsson <> writes:
    > Keith Thompson <> wrote:
    >> Acid Washed China Blue Jeans <> writes:
    >> > Stanley Rice <> wrote:
    >> > >
    >> > > #include <stdio.h>
    >> > > void my_print(float a, int b)
    >> > > {
    >> > >     printf("%f\t%d\n", a, b);
    >> > > }
    >> > >
    >> > > All three function are defined external without declaration in
    >> > > file main.c. But why the first two works well, but the last one
    >> > > fails? Is it undefined in ISO C?
    >> >
    >> > If you don't declare a function f, it is assumed to be
    >> >         int f()
    >> > which passes all arguments with var-args rules.

    >>
    >> Not exactly, if by "var-args rules" you mean as if calling a variadic
    >> function (one with ", ..." in its declaration).

    >
    > If they meant default argument promotions are applied to all
    > arguments,
    > then that _is_ how they're passed.


    Yes and no. The default argument promotions are applied in the
    same way; the calling convention may be quite different. That's why
    calling a variadic function with no visible prototype has undefined
    behavior, even in C90. (I still don't know what the previous poster
    meant by "var-args rules".

    >> Under C90 rules, the implicit declaration is for a function that
    >> returns int with a fixed number of parameters corresponding to the
    >> promoted types of the actual arguments in the call.

    >
    > No, the implicit declaration is simply int f(), i.e. a function
    > returning int with an unspecified number of parameters.


    You're right, thanks for the correction.

    C90 6.3.2.2 says:

    If the expression that precedes the parenthesized argument list
    in a function call consists solely of an identifier, and if
    no declaration is visible for this identifier, the identifier
    is implicitly declared exactly as if, in the innermost block
    containing the function call, the declaration

    extern int identifier () ;

    appeared.

    (The effect of the nonexistent rule that I described would be similar to
    the actual rule in the case of a single call.)

    [snip]

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Nov 17, 2011
    #12
  13. Stanley Rice

    James Kuyper Guest

    On 11/16/2011 07:51 PM, Peter Nilsson wrote:
    > Keith Thompson <> wrote:
    >> Acid Washed China Blue Jeans <> writes:
    >>> Stanley Rice <> wrote:
    >>>>
    >>>> #include <stdio.h>
    >>>> void my_print(float a, int b)
    >>>> {
    >>>> � � printf("%f\t%d\n", a, b);
    >>>> }
    >>>>
    >>>> All three function are defined external without declaration in
    >>>> file main.c. But why the first two works well, but the last one
    >>>> fails? Is it undefined in ISO C?
    >>>
    >>> If you don't declare a function f, it is assumed to be
    >>> � � � � int f()
    >>> which passes all arguments with var-args rules.

    >>
    >> Not exactly, if by "var-args rules" you mean as if calling a variadic
    >> function (one with ", ..." in its declaration).

    >
    > If they meant default argument promotions are applied to all
    > arguments,
    > then that _is_ how they're passed.


    It's more than just the argument promotions. Variadic functions are
    allowed to use an ABI that is incompatible with that used by
    non-variadic functions, even when they take arguments of the same
    promoted type:

    "For two function types to be compatible, ... the parameter type lists,
    if both are present, shall agree ... in use of the ellipsis terminator.
    .... If one type has a parameter type list and the other type is
    specified by a function declarator that is not part of a function
    definition and that contains an empty identifier list, the parameter
    list shall not have an ellipsis terminator ..." (6.7.5.3p15).

    Consider the following code:

    define.c:
    int foo(char *format, ...)
    {
    // Definition of f()
    }

    int bar(
    char *format,
    int i,
    double d
    )
    {
    }

    main.c:
    int foo();
    int bar(char *, ...);

    int main(int argc, char *argv[])
    {
    short s;
    float f;
    // Set things up

    int a = foo(argv[1], s, f);
    int b = bar(argv[2], s, f);
    return 0;
    }

    The call to f() has undefined behavior because of 6.5.2.2p6: "... If the
    expression that denotes the called function has a type that does not
    include a prototype, ... [and] the function is defined with a type that
    includes a prototype, and ... the prototype ends with an ellipsis (,
    ....) ... the behavior is undefined. ..."

    In addition, both calls have undefined behavior because of 6.5.2.2p9:
    "If the function is defined with a type that is not compatible with the
    type (of the expression) pointed to by the expression that denotes the
    called function, the behavior is undefined."
    --
    James Kuyper
    James Kuyper, Nov 17, 2011
    #13
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Noah
    Replies:
    5
    Views:
    950
  2. Ovidesvideo
    Replies:
    4
    Views:
    499
    Andrey Tarasevich
    Dec 10, 2004
  3. Replies:
    4
    Views:
    1,071
    Richard Tobin
    Dec 12, 2006
  4. Bolin
    Replies:
    4
    Views:
    407
  5. Luca Forlizzi
    Replies:
    4
    Views:
    460
    Luca Forlizzi
    Nov 14, 2010
Loading...

Share This Page