我很想知道用动态语言实现printf()模拟的聪明方法。问题是参数列表可以包含深层嵌套的数据类型,所以我不能轻易知道应该为最终缓冲区分配多少内存。显而易见的方法是通过参数进行2次传递:一次用于估计缓冲区大小,另一种用于实际格式化字符串。有没有更好的方法呢?
澄清:我正在考虑为Erlang编写C函数。 Erlangs的数据类型是深层次的,所以要使用类似asprintf的函数,我需要将它们全部解包(并且可能重写formattring),这很昂贵。
答案 0 :(得分:2)
如果您试图模拟它是printf
,那么您没有问题,因为您不需要缓冲区,您可以在找到它时将每个标记写入控制台。
如果您要模仿sprintf
,则需要更新您的问题。
对于sprintf。 。 。使用可扩展的字符串缓冲区。
如果您要自己动手,请从合理的512字节缓冲区开始。当你达到这个限制时,分配另一个缓冲区两次前一个限制(所以1024第一次,2048秒等),将buffer1复制到buffer2,交换你的新缓冲区为旧并扔掉/ free / delete / deallocate第一个缓冲区。
然后,当你完成后,你分配一个正确长度的字符串,将你的缓冲区复制到字符串并返回它。
如果您不介意将缓冲区作为结果返回,可以忽略最后一步,即使它在技术上太大而且可能大部分未使用。
<强>更新强>
由于重新分配,感觉像次优解决方案。我错了吗?
总之,是的 这就是动态列表&amp;数组在主要框架中实现,如C ++ STL和.Net框架。如果你考虑格式可能会破坏512字节的可能性,破坏1024或2048的可能性有多大?这是三个额外的副本,如果字符串最终是那么长。您可以应用80/20规则,80%的时间您永远不会达到前512个限制(您可能会将第一个分配放到64个字节并仍然应用80/20规则)
现在考虑您的替代方案,对要格式化的项目进行两次传递 如果你有一个32位的int,你几乎要把它转换成一个字符串来找出字符串的长度。您将为列表中的每个项目执行一次额外的时间,这是用于执行转换的缓冲区的分配,执行转换的时间,然后取消分配字符串。与其他一些数据类型相比,获取int的长度相对简单。
还要考虑复杂的对象,如果你得到它们的长度,它们的表示是(可能)通过调用一些.ToString
类似的方法来建立的,这将连接它的所有子对象的结果{{1方法在一起,你将再次这样做两次。
考虑到可扩展字符串缓冲区之间的折腾,并建立所有字符串一次额外的时间来获取它们的长度?我每次都去找缓冲区。
答案 1 :(得分:0)
有sprintf()
的变体叫asprintf()
,这个变体mallocs用于存储结果字符串的空间,而不必事先知道它的长度。
它作为C stdlib的一部分在大多数平台上都可用,您可以使用man asprintf
,or at this online copy of the manpage.阅读更多(可能):
从手册页开始:
printf()函数系列根据如下所述的格式生成输出。 printf()和vprintf()函数写入 输出到标准输出流stdout; fprintf()和vfprintf()将输出写入给定的输出流; dprintf()和 vdprintf()将输出写入给定的文件描述符; sprintf(),snprintf(),vsprintf()和vsnprintf()写入字符串 S; 和asprintf()以及vasprintf()使用malloc(3)动态分配一个新字符串。
添加:
asprintf()和vasprintf()函数将* ret设置为指向缓冲区的指针,该缓冲区足以容纳格式化的字符串。这个 应该将指针传递给free(3)以在不再需要时释放已分配的存储。如果有足够的空间不能同 cated,asprintf()和vasprintf()将返回-1并将ret设置为NULL指针。
(加粗加注)
以下是一个小样本用法:
char *buffer;
asprintf(buffer, "Hello %s", myunknownlengthstring);
这应该分配足够的空间来存储生成的格式化字符串并将其存储在&buffer
。你将负责释放这个内存,否则它会泄漏,当一个不再需要字符串时,一个简单的free(buffer)
就足够了。