使用printf()输出std::string_view的正确方法是什么?
结论
你给出的写法不正确,测试用例能得到预期结果完全是场景巧合,本质属于未定义行为。
错误原因
- 格式符使用错误
printf的%*s格式中,*对应的参数设置的是最小输出字段宽度,不会限制%s对字符串的读取长度:%s会持续从传入的指针位置向后读取字符,直到碰到空终止符\0才会停止。如果std::string_view指向的缓冲区在视图长度范围内没有空终止符,就会触发越界读取,属于未定义行为。
你给出的测试代码中,sv是直接从字符串字面量"Hallo!"构造的,字面量本身自带末尾的\0,读取时刚好在视图长度位置碰到终止符,且字段宽度和输出长度恰好相等,才会看起来输出正确,换个场景立刻出错:std::string s = "abcdefgh"; std::string_view sv{s.data() + 2, 3}; // 视图内容为"cde",后续字符为'f'无空终止 // 用你给出的写法会越界读取,输出=cdefgh= 而非预期的=cde= printf("=%*s=\n", static_cast<int>(sv.length()), sv.data()); - 正确的
printf输出std::string_view的写法应当使用精度控制符., 格式为%.*s:
按照C标准规定,// 正确写法:. 后面的*对应传入的精度值,最多读取sv.length()个字符,无需空终止 printf("=%.*s=\n", static_cast<int>(sv.length()), sv.data());%s配合精度值N时,最多从传入指针位置读取N个字符,不需要缓冲区末尾存在空终止符。而std::string_view保证其data()返回的指针指向连续的、至少length()个可读取的字符,因此这个写法是标准合规、没有未定义行为的。
对其他printf风格函数的兼容性
只要是遵循C标准printf格式规范的函数(包括fprintf、sprintf、snprintf以及各平台合规的扩展printf系列函数),上述正确的%.*s写法都可以正常使用,使用时需要注意两个前提:
- 传入长度参数时必须显式转为
int:变参函数不会做自动类型转换,而std::string_view::size_type是无符号整数类型,64位环境下长度和int不一致,不转换会触发参数读取错位的未定义行为。 - 必须保证
std::string_view本身没有悬空,即其指向的内存块还在生命周期内,这是使用std::string_view的通用要求,和输出函数无关。
内容的提问来源于stack exchange,提问作者kuga
相关产品推荐
相关产品推荐

