如何使用复合字面量在`fprintf()`中输出多个带任意进制的格式化数字?

7

我想将多个数字转换为某种表达形式,然后使用*printf()格式指示符的标志、宽度和精度。最好避免使用全局或static缓冲区。关键问题似乎是如何为每个转换后的数字提供一个char[]

fprintf(ostream, "some_format", foo(int_a, base_x), foo(int_b, base_y), ...);

如何使用C99(或更高版本)的复合字面量来解决这个问题?请注意,不再使用C11。

为什么只使用C11?它同样适用于C99。 - alk
你的意思是想使用%s而不是%d或其他吗? - M.M
@M.M 使用"%s""%d"或其他格式。我不想扼杀新颖的想法。 - chux - Reinstate Monica
1
@ alk 根据您的正确观察进行了问答修改。 - chux - Reinstate Monica
1个回答

15

C99 引入了复合字面量(compound literals),它不仅允许复杂的初始化结构,还允许“内联”变量。

代码可以调用转换函数并传递一个新缓冲区 (char [UTOA_BASE_N]){0} 给每个函数调用,从而使函数能够返回同一缓冲区,该缓冲区现在已写入所需的内容且仍在其生命周期中。返回的字符串然后使用"%s"说明符可用的各种标志、宽度和精度打印出来。

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

// Maximum buffer size needed
#define UTOA_BASE_N (sizeof(unsigned)*CHAR_BIT + 1)

char *utoa_base(char *s, unsigned x, unsigned base) {
  s += UTOA_BASE_N - 1;
  *s = '\0';
  if (base >= 2 && base <= 36) {
    do {
      *(--s) = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[x % base];
      x /= base;
    } while (x);
  }
  return s;
}

#define TO_BASE(x,b) utoa_base((char [UTOA_BASE_N]){0} , (x), (b))

void test(unsigned x) {
  printf("base10:%10u base2:%5s  base36:%s ", x, TO_BASE(x, 2), TO_BASE(x, 36));
  printf("%lu\n", strtoul(TO_BASE(x, 36), NULL, 36));
}

int main(void) {
  test(0);
  test(25);
  test(UINT_MAX);
}

输出

base10:         0 base2:    0  base36:0 0
base10:        25 base2:11001  base36:P 25
base10:4294967295 base2:11111111111111111111111111111111  base36:1Z141Z3 4294967295

参考:有没有printf转换器可以以二进制格式打印?有很多答案,但它们都不允许像上面那样简单地进行内存管理(没有static),并且可以使用fprintf()标志宽度、精度和使用数字的全部范围。

这是一个自问自答的答案。


1
注意:我断言返回值仍然在作用域内,因为“...它的生命周期从与其关联的块的进入开始,直到该块的执行以任何方式结束。(进入封闭块或调用函数会暂停但不会结束当前块的执行。)”C11dr §6.2.4 6 - chux - Reinstate Monica
@rici 对于作用域/生命周期和神秘的42(https://en.wikipedia.org/wiki/42_(number)#The_Hitchhiker.27s_Guide_to_the_Galaxy)表示同意。帖子已经修改。[评论](https://dev59.com/LpLea4cB1Zd3GeqPxxUP#34292061?noredirect=1#comment56327557_34292061)应该写成“我断言返回值在其生命周期内由于” - chux - Reinstate Monica
2
是的,是的,是的,这个答案(的例子)展示了为什么我们应该喜欢复合字面量!;-) - alk
1
@rici 这是故意的。复合字面量没有“临时寿命”。6.2.4/8是指从函数按值返回的结构体(例如,foo().x = 5;)。 - M.M
1
@m.m.:是的,有了chux关于复合字面量是lvalue的有用观察,我就明白了。非lvalue数组字面量因此是不可能的,因为函数不能返回数组。(另外,复合字面量不能是VLA,所以它们的生命周期可以从块的开头开始。) - rici
显示剩余12条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接