Confusion in ANSI C's function concepts

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • vaib

    Confusion in ANSI C's function concepts

    hi all ,
    It really seems that C never ceases to amaze . All this time
    i've been doing C and i thought i was quite adept at it but i was
    wrong . So without wasting any more time , here's the confusion .

    I read in K&R that ANSI introduced the concept of function
    prototyping in C and it was missing there initially ( it borrowed the
    concept from C++ ) _but_ it did it make it compulsory to actually
    include the function declaration in the program , the reason being -
    so that older C code ( the ones missing the declarations ) could
    run on newer compilers too . So the situation now is this - if there
    is no function declaration corresponding to the function call and the
    call does not say anything about the return type then the compiler
    should assume a declaration with an int return type . for example ,
    main()
    { fun(3) ;
    return 0 ;
    }
    in this case the compiler assumes this declaration - int fun( the
    standard says nothing about the parameters so thats implementation
    dependent ) ;
    and the code compiles without any hitch .

    my question is what sort of declaration would the
    compiler assume in the following case :
    main()
    {
    void * p ;
    p = fun(3) ;
    }
    in the above case the return type is mentioned as void * .


    also what happens in this case :
    main()
    {
    void * p ;
    p = malloc(sizeof(i nt)) ;
    return 0 ;
    }
    the above code also compiles and _executes_ successfully on
    gcc .

    few more regarding these concepts have cropped up lately in my mind
    but i'll ask them as the thread proceeds .


    Thanking in anticipation . Vaib .

  • Ben Pfaff

    #2
    Re: Confusion in ANSI C's function concepts

    vaib <vaibhavpanghal @gmail.comwrite s:
    my question is what sort of declaration would the
    compiler assume in the following case :
    main()
    {
    void * p ;
    p = fun(3) ;
    }
    in the above case the return type is mentioned as void * .
    The compiler assumes that the return type is "int". The compiler
    always makes this assumption in the absence of a function
    declaration.

    Also, there is no reason that fun() would have to have a return
    type of void * for the above to be valid. Its return type could
    be pointer to any object or incomplete type.
    --
    char a[]="\n .CJacehknorstu" ;int putchar(int);in t main(void){unsi gned long b[]
    ={0x67dffdff,0x 9aa9aa6a,0xa77f fda9,0x7da6aa6a ,0xa67f6aaa,0xa a9aa9f6,0x11f6} ,*p
    =b,i=24;for(;p+ =!*p;*p/=4)switch(0[p]&3)case 0:{return 0;for(p--;i--;i--)case+
    2:{i++;if(i)bre ak;else default:continu e;if(0)case 1:putchar(a[i&15]);break;}}}

    Comment

    • Keith Thompson

      #3
      Re: Confusion in ANSI C's function concepts

      vaib <vaibhavpanghal @gmail.comwrite s:
      It really seems that C never ceases to amaze . All this time
      i've been doing C and i thought i was quite adept at it but i was
      wrong . So without wasting any more time , here's the confusion .
      >
      I read in K&R that ANSI introduced the concept of function
      prototyping in C and it was missing there initially ( it borrowed the
      concept from C++ ) _but_ it did it make it compulsory to actually
      include the function declaration in the program , the reason being -
      so that older C code ( the ones missing the declarations ) could
      run on newer compilers too . So the situation now is this - if there
      is no function declaration corresponding to the function call and the
      call does not say anything about the return type then the compiler
      should assume a declaration with an int return type .
      For certain values of "now".

      In the C89/C90 standard, which K&R2 describes, the situation is
      basically what you've described. In the C99 standard, the "implicit
      int" rule has been dropped, so the examples you show are illegal (more
      precisely, they're constraint violations requiring diagnostics).
      for example ,
      main()
      { fun(3) ;
      return 0 ;
      }
      Please use a more traditional code layout, even for short examples
      like this.

      main()
      {
      fun(3);
      return 0;
      }

      In C90, the compiler will assume that fun is a function returning int
      with a single parameter of type int (the latter because that's what
      you passed it).
      in this case the compiler assumes this declaration - int fun( the
      standard says nothing about the parameters so thats implementation
      dependent ) ;
      No, it's not implementation dependent; the compiler's assumption about
      the parameter types is based on the actual types of the arguments you
      pass, after promotion. (For example, short is promoted to int and
      float to double, so if you pass a float argument it will assume the
      parameter is of type double.)
      and the code compiles without any hitch .
      Right -- but if you use C99 mode you'll at least get a warning.
      You'll also probably get a warning on the declaration of main; use
      "int main(void)" rather than "main()".
      my question is what sort of declaration would the
      compiler assume in the following case :
      main()
      {
      void * p ;
      p = fun(3) ;
      }
      in the above case the return type is mentioned as void * .
      main()
      {
      void *p;
      p = fun(3);
      }

      A C90 compiler will assume that fun takes one argument of type int and
      returns a result of type int. In the absence of a visible
      declaration, it *always* assumes a return type of int. There is no
      implicit conversion from int to void*, so you should get at least a
      warning even in C90 mode.
      also what happens in this case :
      main()
      {
      void * p ;
      p = malloc(sizeof(i nt)) ;
      return 0 ;
      }
      the above code also compiles and _executes_ successfully on
      gcc .
      main()
      {
      void *p;
      p = malloc(sizeof(i nt));
      return 0;
      }

      sizeof yields a result of type size_t, which is an alias (a typedef)
      for some predefined unsigned integer type. Since sizeof is an
      operator, not a function, the compiler knows this.

      Let's assume size_t is unsigned long in the current implementation.
      Then a C90 compiler will assume that malloc takes an argument of type
      unsigned long (possibly declared as size_t, but that doesn't matter
      for these purposes) and returns a result of type int. You then
      attempt to assign this int result to an object of type void*, which is
      a constraint violation requiring a diagnostic. gcc doesn't complain
      about this by default, because with no options specified gcc is not a
      conforming C90 compiler. Try invoking it with at least "-ansi
      -pedantic", and possibly "-Wall -Wextra" in addition, and you'll get
      at least one warning.

      The best lesson to learn from this is not how compilers behave when
      you write bad code like this, but how to write good code that the
      compiler won't complain about. If you use a standard function,
      *always* provide a #include directive for the appropriate header. If
      you call your own function, *always* provide a prototype for it, not
      just an old-style declaration, but a prototype that specifies the
      types of the parameters. And if the compiler complains about a type
      mismatch, adding a cast is almost never the right solution; more
      often, it's like taping over a warning light on your car's dashboard.
      few more regarding these concepts have cropped up lately in my mind
      but i'll ask them as the thread proceeds .
      --
      Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
      Nokia
      "We must do something. This is something. Therefore, we must do this."
      -- Antony Jay and Jonathan Lynn, "Yes Minister"

      Comment

      • Eric Sosman

        #4
        Re: Confusion in ANSI C's function concepts

        Keith Thompson wrote:
        [...]
        main()
        {
        fun(3);
        return 0;
        }
        >
        In C90, the compiler will assume that fun is a function returning int
        with a single parameter of type int (the latter because that's what
        you passed it).
        >
        > in this case the compiler assumes this declaration - int fun( the
        >standard says nothing about the parameters so thats implementation
        >dependent ) ;
        >
        No, it's not implementation dependent; the compiler's assumption about
        the parameter types is based on the actual types of the arguments you
        pass, after promotion. (For example, short is promoted to int and
        float to double, so if you pass a float argument it will assume the
        parameter is of type double.)
        [...]
        It may be just a wording problem, but this doesn't seem quite right
        to me. As I read it, you're saying that `fun(3)' implicitly declares
        the function as `int fun(int)'. If that were so, then the fragment

        fun(3); /* now we know `int fun(int)' */
        fun(); /* ... or do we? */

        .... would require a diagnostic, because the second call omits the
        argument that the first call's implicit declaration specifies. But
        in fact no diagnostic is required, hence the first call does not
        actually declare fun's parameters.

        Obviously, at least one of the calls must be erroneous -- but a
        C90 compiler is not obliged to diagnose either of them.

        --
        Eric.Sosman@sun .com

        Comment

        • Keith Thompson

          #5
          Re: Confusion in ANSI C's function concepts

          Eric Sosman <Eric.Sosman@su n.comwrites:
          Keith Thompson wrote:
          >[...]
          > main()
          > {
          > fun(3);
          > return 0;
          > }
          >In C90, the compiler will assume that fun is a function returning int
          >with a single parameter of type int (the latter because that's what
          >you passed it).
          >>
          >> in this case the compiler assumes this declaration - int fun( the
          >>standard says nothing about the parameters so thats implementation
          >>dependent ) ;
          >No, it's not implementation dependent; the compiler's assumption
          >about
          >the parameter types is based on the actual types of the arguments you
          >pass, after promotion. (For example, short is promoted to int and
          >float to double, so if you pass a float argument it will assume the
          >parameter is of type double.)
          >[...]
          >
          It may be just a wording problem, but this doesn't seem quite right
          to me. As I read it, you're saying that `fun(3)' implicitly declares
          the function as `int fun(int)'. If that were so, then the fragment
          >
          fun(3); /* now we know `int fun(int)' */
          fun(); /* ... or do we? */
          >
          ... would require a diagnostic, because the second call omits the
          argument that the first call's implicit declaration specifies. But
          in fact no diagnostic is required, hence the first call does not
          actually declare fun's parameters.
          >
          Obviously, at least one of the calls must be erroneous -- but a
          C90 compiler is not obliged to diagnose either of them.
          That's exactly right. In C90, if you're not using prototypes (or in
          pre-ANSI C where you couldn't use prototypes), getting the argument
          types right in a function call is entirely up to the programmer. If
          you get it wrong, the behavior is undefined; no diagnostic is
          required.

          A sufficiently clever compiler could warn about the fact that it's
          seen two incompatible implicit declarations for the same function, but
          it's not required to do so.

          A call to a unprototyped function doesn't create an implicit
          declaration that remains visible after the call. The compiler
          generates code to throw the argument values in the general direction
          of the function; if they don't land in the right place, that's the
          programmer's problem.

          Remember that in pre-ANSI C, there was nothing special about printf;
          it was just another function. <varargs.h>, the predecessor of
          <stdarg.h>, was available, but it didn't affect the *declaration* of
          printf. If the compiler couldn't complain about
          printf("hello\n ");
          printf("x = %d\n", x);
          then it would have no basis for complaining about
          fun(3);
          fun();

          (In one of my first C programs, written about 10 years before the ANSI
          standard, I made a mistake and passed the wrong number of arguments to
          a function. The compiler didn't complain. When I realized what was
          going on, I was *horrified*. Most of my previous programming had been
          in Pascal.)

          --
          Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
          Nokia
          "We must do something. This is something. Therefore, we must do this."
          -- Antony Jay and Jonathan Lynn, "Yes Minister"

          Comment

          • Eric Sosman

            #6
            Re: Confusion in ANSI C's function concepts

            Keith Thompson wrote:
            [... concerning `fun(3)' without a declaration ...]
            A call to a unprototyped function doesn't create an implicit
            declaration that remains visible after the call. [...]
            Are you sure of that? I don't have a C90 at hand for study,
            and of course in C99 the rules changed.

            I argued earlier that `fun(3)' did not magically declare
            `int fun(int)', which your earlier post seemed to imply (though
            as I said, it might just have been a misreading). However, all
            the compilers I've seen have behaved as if a different declaration
            did in fact appear out of thin air: `int fun()'. Specifically,

            int main(void) {
            fun(3);
            return 0;
            }

            double fun(int x) { return x * 0.5; }

            .... generates a diagnostic indicating that the definition of fun()
            conflicts with a prior declaration. So if this isn't just a matter
            of compilers trying to be helpful and informative, it means that
            the implicit declaration has "staying power" beyond the immediate
            context.

            --
            Eric.Sosman@sun .com

            Comment

            • Harald van =?UTF-8?b?RMSzaw==?=

              #7
              Re: Confusion in ANSI C's function concepts

              On Thu, 25 Sep 2008 11:15:07 -0400, Eric Sosman wrote:
              Keith Thompson wrote:
              >[... concerning `fun(3)' without a declaration ...] A call to a
              >unprototyped function doesn't create an implicit declaration that
              >remains visible after the call. [...]
              >
              Are you sure of that? I don't have a C90 at hand for study,
              and of course in C99 the rules changed.
              >
              I argued earlier that `fun(3)' did not magically declare
              `int fun(int)', which your earlier post seemed to imply (though as I
              said, it might just have been a misreading). However, all the compilers
              I've seen have behaved as if a different declaration did in fact appear
              out of thin air: `int fun()'. Specifically,
              >
              int main(void) {
              fun(3);
              return 0;
              }
              >
              double fun(int x) { return x * 0.5; }
              >
              ... generates a diagnostic indicating that the definition of fun()
              conflicts with a prior declaration. So if this isn't just a matter of
              compilers trying to be helpful and informative, it means that the
              implicit declaration has "staying power" beyond the immediate context.
              The implicit declaration does remain, but your example doesn't show it.
              The declaration of fun has block scope, so it's not visible when the
              definition of fun is encountered. Your example is one of compilers trying
              to be helpful and informative. But, consider this program:

              int main(void) {
              int (*fp)();
              fp = fun; /* #1 */
              { fun(); }
              fp = fun; /* #2 */
              fun();
              fp = fun; /* #3 */
              return 0;
              }

              the assignments marked #1 and #2 are invalid, while the assignment marked
              #3 is valid. #1 is invalid because fun is undeclared, but #2 is invalid
              for the same reason. #3, on the other hand, is valid, because the implicit
              declaration of function fun remains visible for the rest of the block.

              Comment

              • Keith Thompson

                #8
                Re: Confusion in ANSI C's function concepts

                Eric Sosman <Eric.Sosman@su n.comwrites:
                Keith Thompson wrote:
                >[... concerning `fun(3)' without a declaration ...]
                >A call to a unprototyped function doesn't create an implicit
                >declaration that remains visible after the call. [...]
                >
                Are you sure of that? I don't have a C90 at hand for study,
                and of course in C99 the rules changed.
                >
                I argued earlier that `fun(3)' did not magically declare
                `int fun(int)', which your earlier post seemed to imply (though
                as I said, it might just have been a misreading). However, all
                the compilers I've seen have behaved as if a different declaration
                did in fact appear out of thin air: `int fun()'. Specifically,
                >
                int main(void) {
                fun(3);
                return 0;
                }
                >
                double fun(int x) { return x * 0.5; }
                >
                ... generates a diagnostic indicating that the definition of fun()
                conflicts with a prior declaration. So if this isn't just a matter
                of compilers trying to be helpful and informative, it means that
                the implicit declaration has "staying power" beyond the immediate
                context.
                Assuming a C90-conforming compiler, it's just being helpful; the
                diagnostic is not required.

                But now that I've actually checked the C90 standard, I see that my
                description was a bit off (though I think the effect is about the
                same).

                C90 6.3.2.2:

                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.

                with a footnote:

                That is, an identifier with block scope declared to have external
                linkage with type function without parameter information and
                returning an int. If in fact it is not defined as having type
                ``function returning int'', the behavior is undefined.

                So there is an implicit declaration, but it's not visible outside the
                innermost block containing the call, and it provides no parameter
                information.

                Later in that same section in C90:

                If the expression that denotes the called function has a type that
                does not include a prototype, the integral promotions are
                performed on each argument and arguments that have type float are
                promoted to double. These are called the _default argument
                promotions_. If the number of arguments does not agree with the
                number of parameters, the behavior is undefined. If the function
                is defined with a type that does not include a prototype, and the
                types of the arguments after promotion are not compatible with
                those of the parameters after promotion, the behavior is
                undefined. If the function is defined with a type that includes a
                prototype, and the types of the arguments after promotion are not
                compatible with the types of the parameters, or if the prototype
                ends with an ellipsis (,... ), the behavior is undefined.

                Even without implicit declations, non-prototype function declarations
                can cause similar problems. For example, this requires no diagnostic,
                even in C99, though it's certain to invoke undefined behavior:

                int main(void)
                {
                extern void func();
                func();
                func(42);
                func("hello");
                return 0;
                }

                --
                Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
                Nokia
                "We must do something. This is something. Therefore, we must do this."
                -- Antony Jay and Jonathan Lynn, "Yes Minister"

                Comment

                • David Thompson

                  #9
                  Re: Confusion in ANSI C's function concepts

                  On Wed, 24 Sep 2008 15:23:03 -0400, Eric Sosman <Eric.Sosman@su n.com>
                  wrote:
                  vaib wrote:
                  <snip much good>
                  {
                  void * p ;
                  p = malloc(sizeof(i nt)) ;
                  return 0 ;
                  }
                  the above code also compiles and _executes_ successfully on
                  gcc .
                  >
                  It shouldn't. Since malloc() has not been declared, it is
                  assumed to return an int and the compiler should object to your
                  attempt to convert that int to a pointer. A diagnostic is required;
                  I speculate that you are running gcc in some kind of lenient mode.
                  >
                  Depends on quite what you mean by 'should[n't]'.

                  A diagnostic is required, but need be no more than a warning. If you
                  ignore or suppress (or leave suppressed) the warning it's Undefined
                  Behavior; the compiler can proceed to generate code to do whatever it
                  likes. On many machines (and implementations ) where 'int' and 'void*'
                  are both a word, and use the same return mechanism, an easy thing to
                  do is just use the assumed-int-actually-voidptr return value, and it
                  does work. This is not only permitted, but arguably reasonable.
                  But it is not required, and (thus) should not be relied on.

                  And as both you and Keith noted (but I snipped) you should request and
                  at least think about diagnostics. Even when you aren't forced to.

                  And nearby on Wed, 24 Sep 2008 12:58:28 -0700, Keith Thompson
                  <kst-u@mib.orgwrote in <lnzllx8gez.fsf @nuthaus.mib.or g>:
                  vaib <vaibhavpanghal @gmail.comwrite s:
                  {
                  fun(3);
                  return 0;
                  }
                  >
                  In C90, the compiler will assume that fun is a function returning int
                  with a single parameter of type int (the latter because that's what
                  you passed it).
                  >
                  in this case the compiler assumes this declaration - int fun( the
                  standard says nothing about the parameters so thats implementation
                  dependent ) ;
                  >
                  No, it's not implementation dependent; the compiler's assumption about
                  the parameter types is based on the actual types of the arguments you
                  pass, after promotion. (For example, short is promoted to int and
                  float to double, so if you pass a float argument it will assume the
                  parameter is of type double.)
                  >
                  It's not impl _defined_, but it can be impl _dependent_; the default
                  argument promotions for narrow unsigned integer types depends on the
                  ranges, (mostly) chosen by the impl, for several of the integer types.

                  <snip other points>

                  - formerly david.thompson1 || achar(64) || worldnet.att.ne t

                  Comment

                  Working...