2

I can't seem to understand what exactly is going on here

#include <stdio.h>

const char* mes(int a)
{
    static char mess[100];
    sprintf(mess, "%d", a);
    return mess;
}
const int* hes(int a)
{
    static int arr[100];
    arr[0] = a;
    return arr;
}

int main()
{
    printf("%s %s\n", mes(55), mes(25)); //55 55
    printf("%s\n", mes(55)); //55
    printf("%s\n", mes(25)); //25
    printf("%d %d\n", hes(55)[0], hes(25)[0]); //55 25
}

In the first printf the second function seems to be ignored and the output of the earlier input gets printed again.
At first I assumed it was a static variable issue, so I tried printing them separately and then they seem to work fine.
Then I assumed it was a printf issue so I tried to simulate the same behavior with an integer array, and it worked fine there too.
I've run this program a couple of times with various inputs, ruling out the possibility of UB.
So, what exactly am I missing here?

EDIT: I encountered this issue somewhere else and couldn't understand what was happening. So I reproduced the issue in a short sample code. But my question stands, (as many have mentioned) are all the parameters evaluated before printing? If so there should be an overwrite in both cases (int and char array) regardless of evaluation order.

3
  • 1
    Possible duplicate of Parameter evaluation order before a function calling in C Commented Dec 19, 2018 at 16:16
  • 2
    You can never rule out UB by observing the actual behavior of a program. UB does not necessarily mean "it crashes" or "it behaves inconsistently" or "it does something unexpected", though of course your program does do something you don't expect. Commented Dec 19, 2018 at 16:16
  • 1
    There is no UB here, and the suggested duplicate is not a duplicate. Commented Dec 19, 2018 at 16:27

5 Answers 5

5

The order of evaluation of function parameters is unspecified, meaning that you can theoretically see 25 25 instead. That's first thing. Second, when printf is called, both functions have been already evaluated, and the same pointer is passed as first and the second string (because it is a static location), which is the result of the last evaluation (55 in your case). So identical text is printed.

This is pretty much equivalent to the following:

char* a = mes(25);
char* b = mes(55);

// Note, the above can swap depending on the order of evaluation

printf("%s %s\n", a, b);

But here a equals b as both are pointing to a static array.

As for the second example this is not happening as it will be equivalent to the following (up to the order of evaluation):

int *ap = hes(55);
int a = ap[0];

int *bp = hes(25);
int b = bp[0];

printf("%d %d\n", a, b);

Note, that here the pointed values are passed and not the pointer itself. So even though ap equals bp, a is not the same as b.

Sign up to request clarification or add additional context in comments.

4 Comments

But shouldn't it be the same for the integer array?
So if I understand correctly, when pointers are involved parameters are all evaluated before printing. While when passing integers (by value) the printing is done in-place?
No, no. Parameters are always passed by value. Whenever they are pointer or not. It is just that in the first case two identical values have been passed to the function, but in second case two different ones were passed. Do you see why the code above is equivalent to yours?
Thanks, I think I got it.
1

The why code is wrong is well explained by others.

An alternative to static char mess[100] is to use a compound literal as an argument. Then the return value from mes(), hes() is valid until the end of the block of code - well after the printf().

#include <stdio.h>

const char* mes_helper(char mess[100], int a) {
  sprintf(mess, "%d", a);
  return mess;
}
const int* hes_helper(int arr[100], int a) {
  arr[0] = a;
  return arr;
}

// compound literal        v-------------v
#define mes(a) mes_helper( (char [100]){0}, (a))
#define hes(a) hes_helper( (int  [100]){0}, (a))

// No changes to `main() code
int main(void) {
  printf("%s %s\n", mes(55), mes(25)); //55 55
  printf("%s\n", mes(55)); //55
  printf("%s\n", mes(25)); //25
  printf("%d %d\n", hes(55)[0], hes(25)[0]); //55 25
}

Output

55 25
55
25
55 25

Comments

0

There is only one variable mess. When you call:

printf("%s %s\n", mes(55), mes(25));

You are filling in that one variable two different times, once with "25" and once with "55" (overwriting the "25"). As a result, when printf goes to format with %s %s, it finds the same string twice, "55" and again "55" because the "25" has already been overwritten.

You need to be aware that all parameters to a function are evaluated before the function is called. The order of parameter evaluation is not defined, but is often right-to-left. Breaking down that printf into little steps:

  1. Evaluate the mes(25); now static char mess is now "25".
  2. Evaluate the mes(55), now static char mess is overwritten to "55".
  3. Evaluate the parameter "%s %s\n" (there isn't much to evaluate here. Its just a string)
  4. Call printf with parameters: "%s %s\n", and "55" and "55"

1 Comment

If all parameters are evaluated before function call regardless of evaluation order, shouldn't there be an overwrite in the case of the integer array as well?
0

When you call:

printf("%s %s\n", mes(55), mes(25));

mes(55) and mes(25) get evaluated before their result is populated into the string. And since you're pointing to the same static memory, when it's time to populate the string, you get the same value.

Comments

0

Returning the address of a static variable is almost always a bad pattern as you'e found. There's only one such static allocation for the whole program, so for example calling mes twice causes the second call to overwrite the results of the first.

A more reasonable pattern is having the caller furnish a buffer. This is also needed for thread safety:

#include <stdio.h>

const char* mes(char *buf, int a)
{
    sprintf(buf, "%d", a);
    return buf;
}

const int* hes(int *arr, char *buf, int a)
{
    arr[0] = a;
    return arr;
}

int main()
{
    char buf1[100], buf2[100];
    int arr[100];
    printf("%s %s\n", mes(buf1, 55), mes(buf2, 25)); //55 55
    printf("%s\n", mes(buf1, 55)); //55
    printf("%s\n", mes(buf1, 25)); //25
    printf("%d %d\n", hes(arr, 55)[0], hes(arr, 25)[0]); //55 25
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.