计算字符串长度的不同方法

发布于 2024-10-31 14:39:09 字数 639 浏览 1 评论 0原文

对我的一个答案的评论让我有点困惑。当尝试计算将两个字符串连接到新的内存块需要多少内存时,据说使用 snprintf 优于 strlen,如下所示:

size_t length = snprintf(0, 0, "%s%s", str1, str2);
// preferred over:
size_t length = strlen(str1) + strlen(str2);

我可以得到一些背后的推理吗?其优势是什么(如果有的话)?人们会看到一个结果与另一个结果不同吗?

A comment on one of my answers has left me a little puzzled. When trying to compute how much memory is needed to concat two strings to a new block of memory, it was said that using snprintf was preferred over strlen, as shown below:

size_t length = snprintf(0, 0, "%s%s", str1, str2);
// preferred over:
size_t length = strlen(str1) + strlen(str2);

Can I get some reasoning behind this? What is the advantage, if any, and would one ever see one result differ from the other?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

ゞ记忆︶ㄣ 2024-11-07 14:39:09

我是说这句话的人,我在 我的评论写得又快又粗心,所以让我解释一下。我的观点只是,您应该使用相同方法来计算最终用于填充字符串的长度的模式,而不是使用可能存在细微差别的两种不同方法。

例如,如果您有三个字符串而不是两个,并且其中两个或多个重叠,则 strlen(str1)+strlen(str2)+strlen(str3)+1 可能会超过 < code>SIZE_MAX 并回绕零,导致分配不足和输出截断(如果使用 snprintf)或极其危险的内存损坏(如果 strcpystrcat 被使用)。

当生成的字符串长于 INT_MAX 时,snprintf 将返回 -1 并带有 errno=EOVERFLOW,因此您'重新保护。不过,在使用返回值之前,您确实需要检查它,并为空终止符添加 1。

I was the one who said it, and I left out the +1 in my comment which was written quickly and carelessly, so let me explain. My point was merely that you should use the pattern of using the same method to compute the length that will eventually be used to fill the string, rather than using two different methods that could potentially differ in subtle ways.

For example, if you had three strings rather than two, and two or more of them overlapped, it would be possible that strlen(str1)+strlen(str2)+strlen(str3)+1 exceeds SIZE_MAX and wraps past zero, resulting in under-allocation and truncation of the output (if snprintf is used) or extremely dangerous memory corruption (if strcpy and strcat are used).

snprintf will return -1 with errno=EOVERFLOW when the resulting string would be longer than INT_MAX, so you're protected. You do need to check the return value before using it though, and add one for the null terminator.

身边 2024-11-07 14:39:09

如果您只需要确定两个字符串的连接有多大,我认为没有任何特别的理由更喜欢 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);

If you only need to determine how big would be the concatenation of the two strings, I don't see any particular reason to prefer snprintf, since the minimum operations to determine the total length of the two strings is what the two strlen calls do. snprintf will almost surely be slower, because it has to check the parameters and parse the format string besides just walking the two strings counting the characters.

... but... it may be an intelligent move to use snprintf if you are in a scenario where you want to concatenate two strings, and have a static, not too big buffer to handle normal cases, but you can fallback to a dynamically allocated buffer in case of big strings, e.g.:

/* 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);
ヅ她的身影、若隐若现 2024-11-07 14:39:09

一个优点是输入字符串仅扫描一次(在 snprintf() 内),而不是在 strlen/中扫描两次strcpy 解决方案。

实际上,在重读这个问题和对之前答案的评论时,我不明白使用 sprintf() 来计算连接字符串长度有什么意义。如果您确实进行连接,则我上面的段落适用。

One advantage would be that the input strings are only scanned once (inside the snprintf()) instead of twice for the strlen/strcpy solution.

Actually, on rereading this question and the comment on your previous answer, I don't see what the point is in using sprintf() just to calculate the concatenated string length. If you're actually doing the concatenation, my above paragraph applies.

梦醒灬来后我 2024-11-07 14:39:09

您需要在 strlen() 示例中添加 1。请记住,您需要为 nul 终止字节分配空间。

You need to add 1 to the strlen() example. Remember you need to allocate space for nul terminating byte.

于我来说 2024-11-07 14:39:09

所以 snprintf( ) 给我字符串的大小。这意味着我可以为那个家伙 malloc() 空间。非常有用。

我想要(但直到现在才找到)snprintf()的这个函数,因为我格式化了大量的字符串以便稍后输出;但我不想为输出分配静态缓冲区,因为很难预测输出的长度。所以我最终得到了很多 4096 长的字符数组 :-(

但现在 - 使用这个新发现的(对我来说) snprintf( ) 字符计数函数,我可以 malloc( ) 输出 bufs 再次

感谢并向 OP 和 Matteo 道歉。

So snprintf( ) gives me the size a string would have been. That means I can malloc( ) space for that guy. Hugely useful.

I wanted (but did not find until now) this function of snprintf( ) because I format tons of strings for output later; but I wanted not to have to assign static bufs for the outputs because it's hard to predict how long the outputs will be. So I ended up with a lot of 4096-long char arrays :-(

But now -- using this newly-discovered (to me) snprintf( ) char-counting function, I can malloc( ) output bufs AND sleep at night, both.

Thanks again and apologies to the OP and to Matteo.

策马西风 2024-11-07 14:39:09

编辑:删除了随机的、错误的废话。 说过吗?

编辑: Matteo 在下面的评论中是绝对正确的,而我绝对是错误的。

来自 C99:

2 snprintf 函数与 fprintf 等效,只不过输出被写入
一个数组(由参数 s 指定)而不是一个流。如果 n 为零,则不写入任何内容,
并且 s 可能是空指针。否则,第 n-1 个之后的输出字符为
丢弃而不是写入数组,并在末尾写入空字符
实际写入数组的字符数。如果复制发生在对象之间
重叠,行为是未定义的。

退货
3 snprintf 函数返回将要写入的字符数
n 足够大,不包括终止空字符或负数
发生编码错误时的值。因此,空终止输出为
当且仅当返回值非负且小于 n 时才完全写入。

谢谢你,马特奥,我向OP道歉。

这是个好消息,因为它对我在这里提出的问题给出了肯定的答案 就在三周前。我无法解释为什么我没有阅读所有答案,这些答案给了我我想要的东西。惊人的!

EDIT: random, mistaken nonsense removed. Did I say that?

EDIT: Matteo in his comment below is absolutely right and I was absolutely wrong.

From C99:

2 The snprintf function is equivalent to fprintf, except that the output is written into
an array (specified by argument s) rather than to a stream. If n is zero, nothing is written,
and s may be a null pointer. Otherwise, output characters beyond the n-1st are
discarded rather than being written to the array, and a null character is written at the end
of the characters actually written into the array. If copying takes place between objects
that overlap, the behavior is undefined.

Returns
3 The snprintf function returns the number of characters that would have been written
had n been sufficiently large, not counting the terminating null character, or a neg ative
value if an encoding error occurred. Thus, the null-terminated output has been
completely written if and only if the returned value is nonnegative and less than n.

Thank you, Matteo, and I apologize to the OP.

This is great news because it gives a positive answer to a question I'd asked here only a three weeks ago. I can't explain why I didn't read all of the answers, which gave me what I wanted. Awesome!

脸赞 2024-11-07 14:39:09

我在这里看到的“优点”是 strlen(NULL) 可能会导致分段错误,而(至少 glibc 的)snprintf() 处理 NULL< /code> 参数没有失败。

因此,使用 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;
}

The "advantage" that I can see here is that strlen(NULL) might cause a segmentation fault, while (at least glibc's) snprintf() handles NULL parameters without failing.

Hence, with glibc-snprintf() you don't need to check whether one of the strings is NULL, although length might be slightly larger than needed, because (at least on my system) printf("%s", NULL); prints "(null)" instead of nothing.

I wouldn't recommend using snprintf() instead of strlen() though. It's just not obvious. A much better solution is a wrapper for strlen() which returns 0 when the argument is NULL:

size_t my_strlen(const char *str)
{
    return str ? strlen(str) : 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文