简体   繁体   中英

Why does function return only one value when called multiple times in a single printf?

I ran across an issue where a function that returns a character string returns incorrect values when called multiple times within a single printf . At first I thought it was due to the static designation of binstr preventing the variable from updating. After reviewing How to reset static variables within a function I don't think that explains the behavior. In creating the function, my goal was to avoid global variables and to avoid dynamically allocating within the function and turning over the responsibility to free() the memory to the calling routine. Below the original function binstr_8 is listed first. For reasons I'm trying to understand if it is called multiple times within printf it always returns the value of the first return - but behaves as desired in all other situations (when not called multiple times in the same printf )

/* binstr_8 - binary string for uint8_t (char) */
char *
binstr_8 (uint8_t x)
{
    static char binstr [sizeof(uint8_t) * CHAR_BIT + 1] = {0};
    size_t szstr = sizeof (uint8_t) * CHAR_BIT;
    size_t z = 0;

    for (z = 0; z < szstr; z++)
        binstr [szstr - 1 - z] = ((x >> z) & 0x1) ? '1' : '0';

    return binstr;
}

A short example helps explain:

printf ("\nTesting binstr_8 & binstr_8_dyn\n\n");

be = 0b01011000;  // 88
printf (" 88 & 87 = %d  (%s)\n\n", (88 & 87), binstr_8 (88 & 87));

printf (" %d  %s  rightmost bit off: %s\n\n", be, binstr_8 (be), binstr_8 (be & (be - 1)));
printf (" 88  %s\n 87  %s\n  &  ---------\n     %s\n\n",
        binstr_8 (88), binstr_8 (87), binstr_8 (88 & 87));

You can download the source of the example program binstr_8_test.c The output from the printf statements are:

Testing binstr_8 & binstr_8_dyn

88 & 87 = 80  (01010000)

88  01011000  rightmost bit off: 01011000

88  01011000
87  01011000
&  ---------
    01011000

When called in the printf line with "rightmost bit off", each instance of binstr_8 returns the same value: 01011000 (which is just 88, the same returned by the first call of binstr_8 in the statement) In fact, when binstr_8 appears more than once in a printf , every call returns 88? (I even hardcoded the values) What is going on there? Breaking the statements up such that each printf contains only a single call to binstr_8 works :

printf (" %d  %s  rightmost bit off: ", be, binstr_8 (be));
printf ("%s\n\n", binstr_8 (be & (be - 1)));
printf (" 88  %s\n", binstr_8 (88));
printf (" 87  %s\n", binstr_8 (87));
printf ("  &  ---------\n     %s\n\n", binstr_8 (88 & 87));    

Output:

88  01011000  rightmost bit off: 01010000

88  01011000
87  01010111
&  ---------
    01010000

I can re-write the function as binstr_8_dyn to dynamically allocate char *binstr and then use the same printf statements containing multiple calls to the function and it works . How is that possible? Example:

/* same function re-written to dynamically allocate binstr */
char *
binstr_8_dyn (uint8_t x)
{
    char *binstr = NULL;
    size_t z;
    size_t szstr = sizeof (uint8_t) * CHAR_BIT;

    binstr = (char *)malloc (szstr + 1);
    binstr [szstr] = '\0';

    for (z = 0; z < szstr; z++)
        binstr [szstr - 1 - z] = ((x >> z) & 0x1) ? '1' : '0';

    return binstr;
}
/* snip */

printf (" %d  %s  rightmost bit off: %s\n\n", be, binstr_8_dyn (be), binstr_8_dyn (be & (be - 1)));
printf (" 88  %s\n 87  %s\n  &  ---------\n     %s\n\n",
        binstr_8_dyn (88), binstr_8_dyn (87), binstr_8_dyn (88 & 87));

The output is correct:

88  01011000  rightmost bit off: 01010000

88  01011000
87  01010111
&  ---------
    01010000

Now I have managed to totally confuse myself as to why binstr_8 cannot be called multiple times in the same printf while binstr_8_dyn can. What fundamental principle am I overlooking? Also, how else would I provide the return without involving a global or passing a pointer other than using static or allocating dynamically?

当返回static变量的地址并再次使用时,该值始终是相同的,并且它是最后一个已计算的值。

The binstr_8 function returns the same pointer every time - a pointer to your static buffer.

So if you pass it twice as argument to printf, printf receives the same buffer twice, therefore the same thing prints twice. Both calls to binstr_8 run before printf is called (a function's arguments must be evaluated before the function is called, obviously).

The malloc example returns a different buffer each time.

Update : example of using multiple static buffers, inside the binstr_8 function:

static char binstr [5][CHAR_BIT + 1];
static char which = 0;
if ( ++which == 5 ) which = 0;

// use binstr[which] where you had binstr

Then you can use up to five calls to binstr_8 in each printf .

You are returning a pointer to the static buffer so all of the binstrs point to the same memory location. When you create a new one you are overwriting all of the existing binstrs. With binstr_dyn they are are different buffers. As all of your arguments are evaluated before the function call the printf prints the values of the last string created, which happens to be 88 because to order of evaluation of arguments is undefined.

binstr_8_dyn is the correct inplementation but you will neet to rember to free every string it creates.

Adding to Matt McNabb's answer:

For a function like that the output string length can be determined in advance, so you can make a caller to allocate the output buffer. Buffers can be automatic and you avoid problems with deallocation:

#define STR8LEN     (sizeof (uint8_t) * CHAR_BIT)
#define STR8BUFLEN  (sizeof (uint8_t) * CHAR_BIT + 1)

char *binstr_8 (uint8_t x, char *binstr)
{
    size_t z;
    size_t szstr = STR8LEN;

    binstr [szstr] = 0;
    for (z = 0; z < szstr; x >>= 1, z++)
        binstr [szstr - 1 - z] = '0' + (x & 1);

    return binstr;
}

void fun()
{
    char buf1 [STR8BUFLEN], buf2 [STR8BUFLEN], buf3 [STR8BUFLEN];

    printf (" 88  %s\n 87  %s\n  &  ---------\n     %s\n\n",
        binstr_8 (88, buf1), binstr_8 (87, buf2),
        binstr_8 (88 & 87, buf3));
}

It's a caller function fun() responsibility to allocate buffers prior to call and free them after use; binstr_8 just uses them. Each time binstr_8 is called with a separate buffer, so the results do not overlap, and each call returns its own buffer as a result, so they can be used by printf. You can also use them later, as they keep their contents as long as they exist (provided you do not overwrite them!):

void fun()
{
    char buf1 [STR8BUFLEN], buf2 [STR8BUFLEN], buf3 [STR8BUFLEN];

    binstr_8 (88, buf1);
    binstr_8 (87, buf2);
    binstr_8 (88 & 87, buf3);

    printf (" 88  %s\n 87  %s\n  &  ---------\n     %s\n\n",
        buf1, buf2, buf3);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM