【问题标题】:Why does function return only one value when called multiple times in a single printf?为什么函数在单个 printf 中多次调用时只返回一个值?
【发布时间】:2014-07-24 10:55:18
【问题描述】:

我遇到了一个问题,当在单个 printf 中多次调用时,返回字符串的函数返回不正确的值。起初我认为这是由于 binstr 的静态指定阻止了变量更新。在查看How to reset static variables within a function 之后,我认为这并不能解释这种行为。在创建函数时,我的目标是避免使用全局变量并避免在函数内动态分配并将 free() 内存的责任移交给调用例程。下面首先列出原始函数binstr_8。出于某种原因,我试图了解它是否在 printf 内被多次调用,它总是返回第一次返回的值 - 但在所有其他情况下表现如所期望(当未在同一个 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;
}

一个简短的例子有助于解释:

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));

您可以下载示例程序的源代码binstr_8_test.cprintf语句的输出是:

Testing binstr_8 & binstr_8_dyn

88 & 87 = 80  (01010000)

88  01011000  rightmost bit off: 01011000

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

当在printf 行中以“最右边位关闭”调用时,binstr_8 的每个实例都返回相同的值:01011000(仅为 88,与第一次调用 @ 时返回的相同987654333@ 语句中) 实际上,当binstr_8printf 中出现多次时,每次调用都返回88? (我什至对这些值进行了硬编码)那里发生了什么? 分解语句,使每个printf 只包含一个对binstr_8 的调用有效

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));    

输出:

88  01011000  rightmost bit off: 01010000

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

我可以将函数重写为binstr_8_dyn 以动态分配char *binstr,然后使用包含对该函数的多次调用的相同printf 语句,它有效这怎么可能?示例:

/* 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));

输出正确:

88  01011000  rightmost bit off: 01010000

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

现在我完全弄糊涂了,为什么不能在同一个 printf 中多次调用 binstr_8binstr_8_dyn 可以。我忽略了什么基本原则?另外,除了使用静态或动态分配之外,我如何在不涉及全局或传递指针的情况下提供返回?

【问题讨论】:

  • printf (... binstr_8 (be), binstr_8 (be &amp; (be - 1))); 失败。 binstr_8() 被多次调用并更改其静态缓冲区中的数据之前 printf() 被调用。将printf() 分解成更小的部分。
  • 只是为了澄清:请不要在生产代码中使用binstr_8_dyn() - 我会泄漏内存。每次通话都会有一点,但总而言之。
  • 是的,我知道。我的目标是避免将 free() 的负担放在调用者身上,这也是最初决定在 binstr_8 中使用静态类型的原因。基本上,我为我的二进制库保留了两个原型。 binstr_8 用于一般用途,指向公共缓冲区的返回不是问题,而 binstr_8_dyn 用于有限用途。是的,我知道这两个问题都可以通过传递一个存储返回的指针来解决,但这是我试图避免的其他事情,以防止在 printf ("%s\n", binstr_8 (var)) 情况下令人沮丧的使用。 (这里所有的解决方案都很丑:)

标签: c function printf


【解决方案1】:

添加到马特麦克纳布的答案:

对于这样的函数,可以预先确定输出字符串的长度,因此您可以让调用者分配输出缓冲区。缓冲区可以是自动的,您可以避免释放问题:

#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));
}

调用函数fun()负责在调用之前分配缓冲区并在使用后释放它们; binstr_8 只是使用它们。每次调用binstr_8 时使用单独的缓冲区,因此结果不会重叠,并且每次调用都返回自己的缓冲区作为结果,因此它们可以被 printf 使用。您也可以稍后使用它们,因为只要它们存在,它们就会保留其内容(前提是您不覆盖它们!):

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);
}

【讨论】:

    【解决方案2】:

    binstr_8 函数每次都返回相同的指针 - 指向静态缓冲区的指针。

    所以如果你将它作为参数传递给 printf 两次,printf 会收到两次相同的缓冲区,因此同样的东西会打印两次。对binstr_8 的两次调用都在调用printf 之前运行(显然,必须在调用函数之前评估函数的参数)。

    malloc 示例每次返回不同的缓冲区。

    更新:在binstr_8函数内使用多个静态缓冲区的示例:

    static char binstr [5][CHAR_BIT + 1];
    static char which = 0;
    if ( ++which == 5 ) which = 0;
    
    // use binstr[which] where you had binstr
    

    然后,您最多可以在每个printf 中调用五个binstr_8

    【讨论】:

    • 好的,这很有道理,也解释了为什么用char binst[CHAR_BIT + 1]binstr 设为全局也会失败——printf 中的多个调用仍然指向同一个缓冲区。那么在这个例子中,函数调用起作用的唯一方法是在函数内动态分配。这太糟糕了,所以调用者必须设法释放内存。
    • 是的,或者您可以使用多个静态缓冲区(这有点难看,但这里所有的解决方案都很难看)。
    • 添加了多个静态缓冲区的示例。请注意,sizeof(uint8_t) 的定义是 1,但如果您认为它增加了清晰度,您可以保留它以保持清晰 :)
    【解决方案3】:

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

    【讨论】:

    • 而且调用顺序也未指定。
    • 那是另一个难题。所以基本上这是一个掷骰子,我会根据评估多个binstr_8的顺序得到答案——这从一开始就无法确定。
    【解决方案4】:

    您正在返回一个指向静态缓冲区的指针,因此所有 binstr 都指向 same 内存位置。当您创建一个新的时,您将覆盖所有现有的 binstr。对于 binstr_dyn,它们是不同的缓冲区。由于您的所有参数都在函数调用之前进行评估,因此 printf 会打印最后创建的字符串的值,该字符串恰好是 88,因为参数的评估顺序未定义。

    binstr_8_dyn 是正确的实现,但您需要记住释放它创建的每个字符串。

    【讨论】:

      猜你喜欢
      • 2012-11-12
      • 2020-02-07
      • 2022-11-30
      • 1970-01-01
      • 1970-01-01
      • 2022-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多