c#语言输出字符串长度,关于C#:计算字符串长度的不同方法
对我的一个答案的评论让我有些困惑。 尝试计算将两个字符串连接到一个新的内存块需要多少内存时,据说使用snprintf优于strlen,如下所示:size_t length = snprintf(0, 0,"%s%s", str1, str2);// preferred over:size_t length = strlen(str1) + strlen(str2);我可以在此背后得到一些推理吗?
对我的一个答案的评论让我有些困惑。 尝试计算将两个字符串连接到一个新的内存块需要多少内存时,据说使用snprintf优于strlen,如下所示:
size_t length = snprintf(0, 0,"%s%s", str1, str2);
// preferred over:
size_t length = strlen(str1) + strlen(str2);
我可以在此背后得到一些推理吗? 优势是什么(如果有的话),一个人看到的结果会不同于另一个吗?
你为什么不问评论者呢?
我就是这么说的,我在评论中省去了+1,它写得很粗心,所以请让我解释一下。 我的观点只是,您应该使用使用相同方法来计算最终将用于填充字符串的长度的模式,而不是使用可能以细微方式不同的两种不同方法。
例如,如果您有三个而不是两个字符串,并且两个或两个以上字符串重叠,则strlen(str1)+strlen(str2)+strlen(str3)+1可能会超过SIZE_MAX并回零,导致输出分配不足和截断(如果< (使用x3>)或极其危险的内存损坏(如果使用strcpy和strcat)。
当结果字符串长于INT_MAX时,snprintf将返回带有errno=EOVERFLOW的-1,因此受到保护。 不过,您确实需要在使用返回值之前对其进行检查,并为空终止符添加一个。
如果只需要确定两个字符串的串联大小,我看不出有什么特别的理由偏爱snprintf,因为确定两个字符串总长度的最小操作就是两个通话即可。 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);
所以snprintf()给了我一个字符串应该的大小。这意味着我可以为那个家伙malloc()空间。非常有用。
我想要(但直到现在才发现)snprintf()的此功能,因为我格式化了大量字符串以供稍后输出;但是我不想不必为输出分配静态buf,因为很难预测输出会持续多长时间。所以我最终得到了很多4096个长的char数组:-(
但是现在-使用这个新发现的(对我来说)snprintf()字符计数函数,我可以malloc()输出bufs并在晚上睡觉。
再次感谢OP和Matteo并致歉。
我在这里看到的"优势"是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;
}
snprintf不接受%s指定符参数的NULL。
R ..:那又是一个愚蠢的GNU扩展吗?因为我的glibc- snprintf()乐于接受%s说明符的NULL自变量。
它的UB,因此实现有权执行任何所需的操作。在这种情况下,glibc的行为就像参数是"(null)"一样。
R ..:感谢您指出这一点。我更新了答案,尽管现在也可以删除它...
请不要删除,因为此讨论对这些问题很有帮助。请注意,my_strlen包装器将为NULL返回0,而glibcs?? snprintf将为NULL输出6个字节。这可能会导致字符串后面的部分被截断,或者如果某人愚蠢到只使用sprintf并假设已经计算出正确的长度,则将导致缓冲区溢出。
您需要在strlen()示例中添加1。请记住,您需要为nul终止字节分配空间。
还需要将snprintf表达式加1,因为它不包含在返回值中。为了简单起见,我只是从两者中省略了它。
编辑:随机的,错误的废话被删除。我说了吗
编辑:Matteo在下面的评论中是绝对正确的,而我绝对是错误的。
从C99:
2 snprintf函数与fprintf等效,除了将输出写入
一个数组(由s指定),而不是一个流。如果n为零,则不会写入任何内容,
和s可以为空指针。否则,输出第n-1st个字符
丢弃而不是写入数组,并且在末尾写入一个空字符
实际写入数组的字符数。如果在对象之间进行复制
重叠,行为是不确定的。
退货
3 snprintf函数返回将要写入的字符数
n足够大,不计算结尾的空字符或负数
发生编码错误时的值。因此,以零结尾的输出已
当且仅当返回值是非负且小于n时,才完全写入。
谢谢你,Matteo,我向OP致歉。
这是个好消息,因为它为我三周前在这里提出的问题提供了肯定的答案。我无法解释为什么我没有阅读所有答案,这给了我我想要的东西。太棒了!
在snprintf手册页上:"关于snprintf()的返回值,SUSv2和C99相互矛盾:当用size=0调用snprintf()时,SUSv2规定了小于1的未指定返回值,而C99允许< x5>在这种情况下为NULL,并提供返回值(一如既往)作为在输出字符串足够大的情况下应写入的字符数。"因此,在两种情况下都没有核心转储,并且就您的编译器符合C99标准而言,该调用是定义明确的。
并且,从C99标准(7.19.6.5 2)开始,"如果n为零,则不会写入任何内容,并且s可能是空指针。" (和3)" snprintf函数返回在n足够大的情况下要写入的字符数,不计算终止的空字符"
@M,谢谢,现在我对另一个我没有读完所有答案的问题表示不满:-)(想想:我该死的:-)
一个优点是,对于strlen / strcpy解决方案,输入字符串仅扫描一次(在snprintf()内部),而不是扫描两次。
实际上,在重读此问题和对先前答案的评论时,我看不出使用sprintf()仅用于计算级联字符串长度的意义。如果您实际上是在进行串联,则适用上面的段落。
@Pete Wilson:您不正确。从我系统上的man snprintf中:"当以大小= 0调用snprintf()时……C99在这种情况下允许str为NULL,并像往常一样给出返回值作为字符数如果输出字符串足够大则写入。"
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)