我的一个答案的comment给我留下了一点疑惑。当试图计算将两个字符串连接到新的内存块需要多少内存时,据说使用snprintf
比strlen
优先,如下所示:
size_t length = snprintf(0, 0, "%s%s", str1, str2);
// preferred over:
size_t length = strlen(str1) + strlen(str2);
我可以在这背后得到一些推理吗?有什么好处,,如果有的话,有人会看到一个结果与另一个结果不同吗?
答案 0 :(得分:5)
我是那个说出来的人,我遗漏了my comment中+1
的{{1}},这篇文章写得很快,不小心,所以让我解释一下。我的观点仅仅是你应该使用相同方法的模式来计算最终用于填充字符串的长度,而不是使用两种可能在细微方面存在差异的不同方法。
例如,如果您有三个字符串而不是两个字符串,并且其中两个或多个重叠,则strlen(str1)+strlen(str2)+strlen(str3)+1
可能超过SIZE_MAX
并且包裹过零,导致分配不足和截断输出(如果使用snprintf
)或极其危险的内存损坏(如果使用strcpy
和strcat
)。
snprintf
时, -1
将errno=EOVERFLOW
返回INT_MAX
,因此您受到保护。您确实需要在使用之前检查返回值,并为null终止符添加一个。
答案 1 :(得分:3)
如果你只需要确定两个字符串串联的大小,我没有看到任何特别的理由选择snprintf
,因为确定两个字符串总长度的最小操作是这两个strlen
调用的作用是什么。 snprintf
几乎肯定会变慢,因为它必须检查参数并解析格式字符串,而不仅仅是遍历计算字符的两个字符串。
...但是...... 如果你想要连接两个字符串的场景,使用snprintf
可能是一个聪明的举动,并且有一个静态的,不是太大的缓冲区来处理正常情况,但是你可以在大字符串的情况下回退到动态分配的缓冲区,例如:
/* static buffer "big enough" for most cases */
char buffer[256];
/* pointer used in the part where work on the string is actually done */
char * outputStr=buffer;
/* try to concatenate, get the length of the resulting string */
int length = snprintf(buffer, sizeof(buffer), "%s%s", str1, str2);
if(length<0)
{
/* error, panic and death */
}
else if(length>sizeof(buffer)-1)
{
/* buffer wasn't enough, allocate dynamically */
outputStr=malloc(length+1);
if(outputStr==NULL)
{
/* allocation error, death and panic */
}
if(snprintf(outputStr, length, "%s%s", str1, str2)<0)
{
/* error, the world is doomed */
}
}
/* here do whatever you want with outputStr */
if(outputStr!=buffer)
free(outputStr);
答案 2 :(得分:0)
一个优点是输入字符串只扫描一次(在snprintf()
内),而不是strlen
/ strcpy
解决方案的两次。< / p>
实际上,在重新阅读此问题以及对之前答案的评论时,我看不出使用sprintf()
只是为了计算连接的字符串长度有什么意义。如果您实际上正在进行连接,我的上一段适用。
答案 3 :(得分:0)
编辑:随机,错误的废话被删除。 我 是否说过?
编辑: Matteo在下面的评论中是绝对正确的,我绝对错了。
来自C99:
2 snprintf函数等效于fprintf,但输出被写入 数组(由参数s指定)而不是流。如果n为零,则不写任何内容, 和s可以是空指针。否则,超出n-1的输出字符是 丢弃而不是写入数组,并在末尾写入空字符 实际写入数组的字符。如果在对象之间进行复制 重叠,行为未定义。
返回 3 snprintf函数返回已写入的字符数 已经足够大,不计算终止空字符,或者是否定义 如果发生编码错误,则为value。因此,空终止输出已经 当且仅当返回值为非负且小于n时才完全写入。
谢谢你,Matteo,我向OP道歉。
这是一个好消息,因为它仅在三周前给question I'd asked here一个肯定答案。我无法解释为什么我没有阅读所有答案,这给了我想要的东西。真棒!
答案 4 :(得分:0)
您需要在strlen()示例中添加1。请记住,您需要为nul终止字节分配空间。
答案 5 :(得分:0)
我在这里可以看到的“优势”是strlen(NULL)
可能导致分段错误,而(至少glibc)snprintf()
处理NULL
参数而不会失败。
因此,使用glibc - snprintf()
,您不需要检查其中一个字符串是否为NULL
,尽管length
可能略大于所需的字符串,因为(至少在我的系统)printf("%s", NULL);
打印“(null)”而不是什么。
我不建议使用snprintf()
代替strlen()
。这不是很明显。一个更好的解决方案是strlen()
的包装器,当参数为NULL
时返回0:
size_t my_strlen(const char *str)
{
return str ? strlen(str) : 0;
}
答案 6 :(得分:0)
所以snprintf()给了我一个字符串的 大小。 这意味着我可以为那个人提供malloc()空间。非常有用。
我想(但直到现在才发现)snprintf()的这个函数,因为我格式化了大量的字符串以便稍后输出;但我不想为输出分配静态buf,因为很难预测输出会有多长。所以我最终得到了很多4096长的char数组:-(
但是现在 - 使用这个新发现的(对我来说)snprintf()字符计数功能,我可以在晚上输出bufs AND 睡觉,两者。
再次感谢并向OP和Matteo道歉。