如何移除Print函数中的tmp缓冲区与二次std::snprintf调用?
问题
能否移除Print函数中的tmp缓冲区以及对std::snprintf的第二次调用?能否仅通过一次std::snprintf调用实现相同功能?
编译环境:基于arm-none-eabi-g编译,启用C14、O3优化,禁用RTTI与异常,无法使用std::cout。
原代码示例:
// arm-none-eabi-g++ -std=c++14 -O3 -Wall -fno-rtti -fno-exceptions #include <array> #include <cstdint> #include <cstdio> #include <utility> void TransmitViaUartAndDma(const char * buffer, int len) { // "Embedded World" (void)buffer; (void)len; } constexpr std::size_t buf_size = 64; // = sizeof(buffer)/sizeof(buffer[0]) std::array<char, buf_size> buffer{}; template<typename Arg, typename ...Args> void Print(Arg&& arg, Args&&... args) { std::array<char, buf_size> tmp{}; int len = std::snprintf(tmp.begin(), buffer.size(), "%s" "\n", std::forward<Arg>(arg)); // std::printf("len = %d\n", len); len = std::snprintf(buffer.begin(), buffer.size(), tmp.cbegin(), std::forward<Args>(args)...); // std::printf("len = %d\n", len); TransmitViaUartAndDma(buffer.cbegin(), len); } int main() { std::uint32_t val1{222}; std::uint32_t val2{111}; Print("%u. %u. Hello, ", val1, val2); // std::printf(buffer.cbegin()); Print("%u. %u. World!", val2, val1); // std::printf(buffer.cbegin()); return 0; } // 预期输出: // 222. 111. Hello, // 111. 222. World!
解决方案
完全可以移除tmp缓冲区和第二次std::snprintf调用,通过一次调用就能实现相同功能。
原代码的逻辑是先把传入的格式字符串和换行符拼接进tmp缓冲区,再以tmp为新的格式字符串填充参数,属于冗余操作。我们可以直接将原格式字符串与换行符合并作为std::snprintf的格式参数,一次性完成格式化:
修改后的Print函数:
template<typename Arg, typename ...Args> void Print(Arg&& arg, Args&&... args) { int len = std::snprintf(buffer.begin(), buffer.size(), "%s\n", std::forward<Arg>(arg), std::forward<Args>(args)...); TransmitViaUartAndDma(buffer.cbegin(), len); }
说明
- 原代码中第一次
snprintf仅用于拼接格式字符串与换行符,第二次才是真正填充参数。我们直接用"%s\n"作为格式模板,将传入的格式字符串arg作为第一个占位符的参数,后续紧跟原有的args参数,一次调用就完成了格式拼接和参数填充。 - 该修改移除了tmp缓冲区,减少了内存占用和一次函数调用开销,输出结果与原代码完全一致。
- 需注意
buf_size要足够容纳格式化后的内容,避免输出被截断。
内容的提问来源于stack exchange,提问作者ge45mue
相关产品推荐
相关产品推荐

