我对sscanf
不好的建议有模糊的回忆。我知道如果我使用字段宽度说明符,它不会溢出缓冲区,所以我的记忆只是在玩弄技巧吗?
答案 0 :(得分:8)
我认为这取决于你如何使用它:如果你正在扫描像int
这样的东西,那很好。如果你正在扫描一个字符串,它不是(除非有一个我忘记的宽度字段?)。
修改强>:
扫描字符串并不总是安全的。
如果您的缓冲区大小是常量,那么您当然可以将其指定为类似%20s
的内容。但是如果它不是常数,你需要在格式字符串中指定它,你需要这样做:
char format[80]; //Make sure this is big enough... kinda painful
sprintf(format, "%%%ds", cchBuffer - 1); //Don't miss the percent signs and - 1!
sscanf(format, input); //Good luck
这是可能的,但非常容易出错,就像我在之前的编辑中所做的那样(忘了处理空终止符)。您甚至可能会溢出格式化字符串缓冲区。
答案 1 :(得分:4)
sscanf
可能被认为是错误的原因是因为它不要求您为字符串参数指定最大字符串宽度,如果从源字符串读取的输入更长,则可能导致溢出。所以准确的答案是:如果你在格式字符串中正确指定宽度是安全的,否则不是。
答案 2 :(得分:3)
是的,如果你指定了字符串宽度,那么就没有与缓冲区溢出相关的问题了。
无论如何,就像@Mehrdad向我们展示的那样,如果在编译时没有建立缓冲区大小,可能会出现问题。我想限制可以提供给sscanf的字符串的长度,可以消除问题。
答案 3 :(得分:3)
请注意,只要您的缓冲区至少与strlen(input_string)+1
一样长,%s
或%[
说明符就无法溢出。如果要强制执行更严格的限制,也可以在说明符中使用字段宽度,或者可以使用%*s
和%*[
来抑制赋值,而是使用%n
之前和之后获取偏移量在原始字符串中,然后使用它们从输入字符串就地读取生成的子字符串。
答案 4 :(得分:2)
有2点需要注意。
如其他人所说,如果你指定一个小于或等于格式字符串中输出缓冲区大小的大小,你就是安全的。
在这里,您需要确保它是一个空终止字符串,或者您不会读取超过输入缓冲区大小。
如果输入字符串不是null,则终止sscanf
可能会读取超出缓冲区的边界并在未分配存储器时崩溃。
答案 5 :(得分:2)
所有scanf
函数都有基本的设计缺陷,其中只有一部分可以修复。它们不应该用在生产代码中。
如果某个值超出了您将值存储在变量的可表示范围内,则数字转换具有全面的恶魔 - 飞出你的未定义行为。I am not making this up 。允许C库使程序崩溃只是因为有人输入了太多的输入数字。即使它没有崩溃,它也没有义务做任何合理的事情。没有解决方法。
正如其他几个答案所指出的,%s
和臭名昭着的gets
一样危险。 可能可以通过使用' m'来避免这种情况。修饰符或字段宽度,但您必须记住为要转换的每个文本字段执行此操作,并且必须将字段宽度连接到格式字符串 - 您无法通过{{1}作为一个论点。
如果输入与格式字符串不完全匹配,sizeof(buff)
不会告诉您在放弃之前输入缓冲区中有多少个字符。这意味着唯一可行的错误恢复策略是丢弃整个输入缓冲区。如果您正在处理一个简单的某种类型的线性记录数组(例如,使用CSV文件,"跳过格式错误的行并继续执行该文件),则此可以。下一个"是一个明智的错误恢复政策),但如果输入的结构不止于此,那么你就会被冲洗。
在C中,解析不够复杂的作业以证明使用sscanf
和lex
通常最好使用POSIX正则表达式(yacc
)或手动滚动字符串解析。 regex.h
数字转换函数做在溢出时具有明确且有用的行为,并且执行告诉您如何使用它们输入的字符,{{1}为手动解析器提供了许多方便的函数(strto*
,string.h
,strchr
等。)