Thread: why does printf behave differently in the following example

  1. #1
    Registered User
    Join Date
    Jul 2010
    Posts
    22

    why does printf behave differently in the following example

    The following program prints this, which is what I want it to do:

    1 1
    1 1
    1 1
    1 1
    1 1
    Code:
    #include <stdio.h>
    #define MAX_TERMS 5
    
    int ret_one(int);
    
    int call_counter;
    
    int main() 
    {
      int i;
    
      for(i = 1; i <= MAX_TERMS; i++, call_counter = 0) {
    	 printf("%d", ret_one(i));
    	 printf("%4d\n", call_counter);
      }
    
      return 0;
    }
    
    
    int ret_one(int num) 
    {
      call_counter++;
      
      return 1;
    }
    but when I use a single printf statement in the loop like this :

    Code:
      for(i = 1; i <= MAX_TERMS; i++, call_counter = 0) {
    	 printf("%d%4d\n", ret_one(i), call_counter);
      }
    it prints this :

    1 0
    1 0
    1 0
    1 0
    1 0
    I don't know why call_counter is printed as 0, I watched it in the debugger and its value is 1 when it's passed as an argument to printf, but it still prints out as 0. I tell you, these little things...

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > printf("%d%4d\n", ret_one(i), call_counter);
    There is nothing in the C standard which states that ret_one() must be called before (or after) loading call_counter as a parameter value.

    This is a nice example of undefined behaviour - you should avoid it.

    LLVM Project Blog: What Every C Programmer Should Know About Undefined Behavior #1/3
    Undefined behavior - Wikipedia, the free encyclopedia
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Dec 2012
    Posts
    307
    Code:
    for(i = 1; i <= MAX_TERMS; i++) 
       {
         call_counter = 0;
         result=ret_one(i);
         printf("%d%4d\n",result, call_counter);
       }
    btw, this all is just goofy!!!

    sending i, yet not using it at all, returning 1

    i would say avoid everything you have done in this example!

  4. #4
    Registered User
    Join Date
    Apr 2013
    Posts
    1,658
    Quote Originally Posted by dnj23 View Post
    Code:
         printf("%d%4d\n", ret_one(i), call_counter);
    As Salem mentioned, there's no rule about the order that parameters are evaluated. In the case of the standard calling model where parameters are pushed onto the stack, the parameters are evaluated right to left, since that is the order they are pushed onto the stack, and that's what happened with this printf. If the calling model passes (some) parameters in registers, I don't think that C specifies the order of evaluation.

  5. #5
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by rcgldr View Post
    If the calling model passes (some) parameters in registers, I don't think that C specifies the order of evaluation.
    I'm pretty sure that under no circumstances, register parameters or otherwise, does C specify the order of evaluation of function parameters.

    Quote Originally Posted by rcgldr View Post
    In the case of the standard calling model where parameters are pushed onto the stack, the parameters are evaluated right to left, since that is the order they are pushed onto the stack, and that's what happened with this printf.
    Are you referring to the x86 calling convention? That's only "standard" for x86-based machines, common for home systems, but far from standard. Other architectures are free to use whatever calling convetions they like. For example, SPARC (Sun) systems used to used a sliding register window for functions with few enough parameters (I assume they still do, but can't be sure). Furthermore, it's the order the resulting values are pushed on the call stack that is important, and that has nothing to do with the order of evaluation (which is implementation dependent and may change between compiler verison, optimization settings, etc). For example
    Code:
    printf("%d %d %d", a(x), b(y), c(z));
    Where a(x) returns 42, b(y) returns 17 and c(z) returns 99

    The compiler may generate instructions that evaluate a then b then c, or it may evaluate c then b then a (or even b then a then c, though that is probably less likely). The only thing it must do for the calling convention you describe is push the resulting values in reverse order, i.e. 99, 17, 42.

  6. #6
    Registered User
    Join Date
    Jul 2010
    Posts
    22
    Quote Originally Posted by rcgldr View Post
    As Salem mentioned, there's no rule about the order that parameters are evaluated. In the case of the standard calling model where parameters are pushed onto the stack, the parameters are evaluated right to left, since that is the order they are pushed onto the stack, and that's what happened with this printf. If the calling model passes (some) parameters in registers, I don't think that C specifies the order of evaluation.
    I see. I thought the comma operator guaranteed left to right evaluation, but I guess it behaves differently as an argument list.

    btw, this all is just goofy!!!

    sending i, yet not using it at all, returning 1

    i would say avoid everything you have done in this example!
    I simplify my examples to facilitate the point. I forgot to take that out. Would you rather read a 1000 lines of unnecessary code?
    Last edited by dnj23; 08-14-2013 at 01:55 PM.

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Salem
    This is a nice example of undefined behaviour - you should avoid it.
    While we should indeed avoid it, I don't think it is undefined behaviour: there is a sequence point before the call to ret_one and at the return statement at the end of ret_one, hence call_counter is only read once between consecutive sequence points when we consider the arguments to printf. Rather, the order of evaluation here is unspecified, i.e., if the call to ret_one is evaluated before call_counter, then the output should be like dnj23's earlier code snippet, otherwise it would be like the latter code snippet, but if the program prints "hello world!" instead, then we could rightfully call it the result of a compiler bug.

    Quote Originally Posted by rcgldr
    If the calling model passes (some) parameters in registers, I don't think that C specifies the order of evaluation.
    C does not specify the order of evaluation here either way.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    Quote Originally Posted by dnj23 View Post
    I see. I thought the comma operator guaranteed left to right evaluation,
    You do not have here comma operator, and you do not want to have it here - it returns the last value. I'm sure you do not want to try passing n values to function to encounter that only last one is actually arrived to its destination.
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  9. #9
    Registered User
    Join Date
    Apr 2013
    Posts
    1,658
    Quote Originally Posted by anduril462 View Post
    I'm pretty sure that under no circumstances, register parameters or otherwise, does C specify the order of evaluation of function parameters.
    I was only explaining what had happened in the second case, the order isn't specified in C, but using X86 32 bit mode stack model with a microsoft compiler will usually result in the parameters being evaluated right to left (in the order they are pushed onto the stack). For X86 64 bit mode, microsoft compliers only support one model, where up to 4 parameters are passed in registers, and any parameters beyond 4 are pushed onto the stack.

    So as mentioned above, the issue is that the order of evaluation for function parameters is undefined. It just happened to be right to left in that second case.

  10. #10
    Registered User
    Join Date
    Jul 2010
    Posts
    22
    Quote Originally Posted by vart View Post
    You do not have here comma operator, and you do not want to have it here - it returns the last value. I'm sure you do not want to try passing n values to function to encounter that only last one is actually arrived to its destination.
    The books I've read don't define it well, but I understand what you mean.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. C++ objects behave upon assignment
    By derder in forum Windows Programming
    Replies: 1
    Last Post: 11-07-2011, 05:10 AM
  2. SetTimer() doesn't behave.
    By OnionKnight in forum Windows Programming
    Replies: 9
    Last Post: 04-09-2007, 03:52 PM
  3. OpenGL - Lights behave strangely
    By Frobozz in forum Game Programming
    Replies: 5
    Last Post: 06-06-2004, 02:00 PM
  4. Why does it behave so
    By mudigonda_ms in forum C Programming
    Replies: 6
    Last Post: 12-21-2003, 01:54 AM
  5. Why the function behave like that?
    By zahid in forum C Programming
    Replies: 1
    Last Post: 12-21-2001, 06:56 AM