首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >‘'strncpy’vs. 'sprintf‘

‘'strncpy’vs. 'sprintf‘
EN

Stack Overflow用户
提问于 2012-09-05 14:06:48
回答 4查看 34.5K关注 0票数 18

我可以在我的应用程序中看到许多用于复制字符串的sprintf

我有一个字符数组:

代码语言:javascript
运行
复制
char myarray[10];
const char *str = "mystring";

现在,如果我想要将字符串str复制到myarray中,最好使用:

代码语言:javascript
运行
复制
sprintf(myarray, "%s", str);

代码语言:javascript
运行
复制
strncpy(myarray, str, 8);
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-09-05 14:55:54

两者都不应该使用。

  1. sprintf是危险的,已被弃用,并已被snprintf取代。将旧的sprintf安全地用于字符串输入的唯一方法是在调用sprintf之前测量它们的长度,这很难看,而且容易出错,或者通过添加字段精度说明符(例如,%.8s%.*s,带有额外的整数参数来限制大小)。这也很难看,而且容易出错,特别是当不止一个%s说明符是involved.
  2. strncpy时,这也是危险的。它不是一个缓冲区大小受限的strcpy版本。它是一个函数,用于将字符复制到固定长度的空填充(而不是以空结尾)数组中,其中源可以是C字符串或至少与目标大小相同的固定长度字符数组。它的预期用途是用于遗留的unix目录表、数据库条目等,这些表使用固定大小的文本字段,并且不希望在磁盘或内存中浪费一个字节用于null终止。它可能会被误用为缓冲区大小受限的strcpy,但这样做是有害的,原因有两个。首先,如果整个缓冲区用于字符串数据(即,如果源字符串长度至少与目标缓冲区一样长),则它无法空终止。您可以自己添加终止,但这很难看,而且容易出错。其次,当源字符串比输出缓冲区短时,strncpy总是用空字节填充整个目标缓冲区。这简直是浪费时间。

那么你应该用什么来代替呢?

有些人喜欢BSD的strlcpy函数。在语义上,它与snprintf(dest, destsize, "%s", source)相同,只是返回值是size_t,并且它不会对字符串长度施加人为的INT_MAX限制。但是,大多数流行的非BSD系统都缺少strlcpy,而且编写自己的系统很容易出现危险的错误,所以如果您想使用它,您应该从可靠的来源获得一个安全的、已知有效的版本。

我更喜欢对任何非平凡的字符串构造简单地使用snprintf,而对于一些已经被测量为性能关键型的平凡情况,则使用strlen+memcpy。如果您养成了正确使用此习惯用法的习惯,那么意外地编写具有字符串相关漏洞的代码几乎是不可能的。

票数 47
EN

Stack Overflow用户

发布于 2012-09-05 14:28:03

不同版本的printf/scanf都是非常慢的函数,原因如下:

  • 他们使用可变参数列表,这使得参数传递变得更加复杂。这是通过各种晦涩的宏和指针来完成的。所有参数都必须在运行时进行解析以确定它们的类型,这会增加额外的开销代码。(VA列表也是该语言的一个非常冗余的功能,也很危险,因为它的类型比普通参数传递弱得多。)
  • 它们必须处理许多复杂的格式和支持的所有不同类型。这也增加了函数的大量开销。由于所有类型计算都是在运行时完成的,因此编译器无法优化掉函数中从未使用过的部分。因此,如果你只想用printf()打印整数,你将获得对浮点数、复杂算术、字符串处理等链接到你的程序的支持,因为完全浪费了space.
  • Functions,比如strcpy(),特别是memcpy(),编译器对其进行了大量优化,通常是在内联汇编中实现的,以获得最大的性能。

下面是我曾经在barebone 16位低端微控制器上进行的一些测量。

根据经验,您永远不应该在任何形式的生产代码中使用stdio.h。它被认为是一个调试/测试库。MISRA-C:2004在生产代码中禁止stdio.h。

编辑

用事实替换主观数字:

在目标飞思卡尔HCS12,编译器飞思卡尔Codewarrior5.1上对strcpy和sprintf的测量。使用sprintf的C90实现,C99的效率会更低。已启用所有优化。测试了以下代码:

代码语言:javascript
运行
复制
  const char str[] = "Hello, world";
  char buf[100];

  strcpy(buf, str);
  sprintf(buf, "%s", str);

执行时间,包括参数混洗开/关调用堆栈:

代码语言:javascript
运行
复制
strcpy   43 instructions
sprintf  467 instructions

分配的程序/ROM空间:

代码语言:javascript
运行
复制
strcpy   56 bytes
sprintf  1488 bytes

分配的RAM/堆栈空间:

代码语言:javascript
运行
复制
strcpy   0 bytes
sprintf  15 bytes

内部函数调用次数:

代码语言:javascript
运行
复制
strcpy   0
sprintf  9

函数调用堆栈深度:

代码语言:javascript
运行
复制
strcpy   0 (inlined)
sprintf  3 
票数 3
EN

Stack Overflow用户

发布于 2012-09-05 14:27:50

我不会仅仅为了复制字符串而使用sprintf。这太夸张了,读过这些代码的人肯定会停下来,想知道我为什么要这么做,他们(或我)是否遗漏了什么。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12275381

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档