我可以在我的应用程序中看到许多sprintf
用于复制字符串。
我有一个字符数组:
char myarray[10];
const char *str = "mystring";
现在,如果我想将字符串str
复制到myarray
,最好使用:
sprintf(myarray, "%s", str);
或
strncpy(myarray, str, 8);
答案 0 :(得分:38)
根本不应该使用它们。
sprintf
已被snprintf
标记为危险,已弃用并被其取代。使用旧的sprintf
安全地使用字符串输入的唯一方法是在调用sprintf
之前测量它们的长度,这是丑陋且容易出错的,或者通过添加字段精度说明符(例如{{1}带有额外整数参数的%.8s
或大小限制)。这也是丑陋且容易出错的,特别是如果涉及多个%.*s
说明符。
%s
也很危险。 不 strncpy
的缓冲区大小限制版本。它是一个将字符复制到固定长度的null- 填充(而不是null- 终止)数组的函数,其中源可以是C字符串或固定长度字符数组,至少是目标的大小。它的用途是用于传统的unix目录表,数据库条目等,它们使用固定大小的文本字段,并且不希望在磁盘或内存中浪费单个字节以进行空终止。它可以被误用为缓冲区大小限制strcpy
,但这样做有两个原因是有害的。首先,如果整个缓冲区用于字符串数据(即,如果源字符串长度至少与dest缓冲区一样长),则无法终止null。您可以自己添加终止,但这很难看并且容易出错。第二,当源字符串比输出缓冲区短时,strcpy
总是用空字节填充整个目标缓冲区。这只是浪费时间。
那么您应该使用什么呢?
有些人喜欢BSD strncpy
功能。在语义上,它与strlcpy
相同,只是返回值为snprintf(dest, destsize, "%s", source)
并且它不会对字符串长度施加人为size_t
限制。但是,大多数流行的非BSD系统缺少INT_MAX
,并且很容易编写自己的危险错误,所以如果你想使用它,你应该从一个值得信赖的来源获得一个安全的,已知的工作版本。 / p>
我的偏好是简单地使用strlcpy
进行任何重要的字符串构建,而snprintf
+ strlen
则用于一些被测量为性能关键的琐碎案例。如果你养成了正确使用这个习惯用法的习惯,几乎不可能意外地编写带有字符串相关漏洞的代码。
答案 1 :(得分:3)
不同版本的printf / scanf功能非常慢,原因如下:
他们使用变量参数列表,这使得参数传递更加复杂。这是通过各种模糊的宏和指针完成的。必须在运行时解析所有参数以确定其类型,这会增加额外的开销代码。 (VA列表也是该语言的一个冗余功能,并且也很危险,因为它比普通参数传递具有更好的输入。)
他们必须处理许多复杂的格式并支持所有不同的类型。这也为该功能增加了大量开销。由于所有类型评估都是在运行时完成的,因此编译器无法优化掉从未使用过的函数部分。因此,如果您只想使用printf()打印整数,您将获得与您的程序相关联的浮点数,复杂算术,字符串处理等的支持,因为完全浪费了空间。
另一方面,strcpy(),特别是memcpy()等函数由编译器进行了大量优化,通常在内联汇编中实现,以获得最佳性能。
我曾在裸机16位低端微控制器上进行的一些测量包含在下面。
根据经验,您不应该在任何形式的生产代码中使用stdio.h。它被认为是一个调试/测试库。 MISRA-C:2004禁止生产代码中的stdio.h。
修改强>
用事实取代主观数字:
目标飞思卡尔HCS12上的strcpy与sprintf的测量,编译器飞思卡尔 Codewarrior 5.1。使用C90实现的sprintf,C99还会更无效。启用所有优化。测试了以下代码:
const char str[] = "Hello, world";
char buf[100];
strcpy(buf, str);
sprintf(buf, "%s", str);
执行时间,包括参数混洗开/关调用堆栈:
strcpy 43 instructions
sprintf 467 instructions
分配的程序/ ROM空间:
strcpy 56 bytes
sprintf 1488 bytes
分配的RAM /堆栈空间:
strcpy 0 bytes
sprintf 15 bytes
内部函数调用次数:
strcpy 0
sprintf 9
函数调用堆栈深度:
strcpy 0 (inlined)
sprintf 3
答案 2 :(得分:1)
我不会仅仅使用sprintf复制字符串。它太过分了,读取该代码的人肯定会停下来,想知道为什么我这样做了,如果他们(或我)遗失了什么。
答案 3 :(得分:0)
有一种方法可以使用sprintf()(或者如果是偏执的话,请使用snprintf())进行“安全”的字符串复制,该复制将被截断而不是溢出该字段或使其不为NUL终止。
将“ *”格式字符用作“字符串精度”,如下所示:
所以:
char dest_buff[32];
....
sprintf(dest_buff, "%.*s", sizeof(dest_buff) - 1, unknown_string);
这会将unknown_string的内容放入dest_buff中,为终止NUL留出空间。