9
是否可以用宏替换数字以使这行代码具有更多的代码可维护性?
scanf("%9[^\n]s", str);
我尝试阅读文档,但找不到这些操作的确切名称:
"[^\n]s"
"%ns"
我尝试了这些替代方案,但 Clion 将两行中第一次出现的标记str
为错误:
scanf("%" str(MAX_LENGTH) "%[^\n]s", str);
scanf("%" str(MAX_LENGTH) "[^\n]%*c", str);
9
是否可以用宏替换数字以使这行代码具有更多的代码可维护性?
scanf("%9[^\n]s", str);
我尝试阅读文档,但找不到这些操作的确切名称:
"[^\n]s"
"%ns"
我尝试了这些替代方案,但 Clion 将两行中第一次出现的标记str
为错误:
scanf("%" str(MAX_LENGTH) "%[^\n]s", str);
scanf("%" str(MAX_LENGTH) "[^\n]%*c", str);
您收到错误的原因不是
str
内置函数也不是预定义的宏。您可以定义str
为宏并使用字符串化运算符#
来执行替换,但这很棘手且令人困惑:str
必须定义为调用另一个宏的宏xstr
,而该宏又将其参数字符串化为#x
:但请注意,您的两个示例都有问题:
scanf("%" str(MAX_LENGTH) "%[^\n]s", str);
s
格式末尾有一个额外的内容,这是无用的,并且表明%[...]
转换 和之间存在混淆%s
,两者都需要字符计数前缀以防止缓冲区溢出。第二个%
也是不正确的。str
此外,您不应该对宏和目标数组使用相同的标识符:虽然不是错误,但它会使代码不必要地混乱。代码应该写成:scanf("%" str(MAX_LENGTH) "[^\n]%*c", str);
MAX_LENGTH
具有正确的形式,但将无条件消耗匹配后的下一个字节,如果该行在换行符之前有多个字节,则该字节不是换行符。不会向调用者返回任何指示。%9[^\n]
在空输入行上将失败,因为没有字符与转换规范匹配。scanf()
将返回0
并使目标数组处于未确定状态。这是一个简短的例子:
如果
str
被定义为#define str(x) #x
,调用str(MAX_LENGTH)
将扩展为"MAX_LENGTH"
。第二个宏调用在第一次扩展初始宏参数后执行其替换,因此str(MAX_LENGTH)
扩展为xstr(9)
,它扩展为"9"
。还要注意,这
MAX_LENGTH
不是目标数组的长度:必须为空终止符添加一个额外的字符,并且宏调用中没有一致性检查:MAX_LENGTH
和定义之间的一致性buf
完全依赖于程序员。此外,如果 的定义
MAX_LENGTH
不是没有后缀的整数常量,则此宏扩展技巧将无法生成正确的scanf
转换说明符。更可靠的方法是
snprintf
构造scanf()
格式字符串:这个版本效果更好,但也有自己的缺点:它阻止编译器检查格式字符串和其余参数之间的一致性
scanf()
,这将导致在建议的警告级别( )下发出警告-Wall -Wextra
,并且这种一致性检查非常有用,而格式字符串构造格式字符串很容易出错。最后,这两种方法都很麻烦并且容易出错。
fgets()
用于您的目的并手动删除尾随换行符要可靠得多:行为略有不同:
fgets
除非行太长,否则将消耗换行符,这会使错误恢复更加困难。总体而言,更好的解决方案似乎是使用自定义函数:
请注意,该行为仍然与原始
scanf()
调用略有不同,但可能更接近您的目标:get_line
读取整行,包括换行符,多余的字符将被丢弃。get_line
如果不为 0,则始终将 C 字符串存储到目标数组中size
,即使在文件末尾处buf
也是空字符串。scanf()
将EOF
在文件末尾返回并保持buf
不变。get_line
将接受空行,而scanf()
会失败、返回0
并buf
处于不确定状态,这是您可能没有意识到的限制。结论:
scanf()
充满了怪癖。尝试使用显式字符计数来避免缓冲区溢出是一个好主意,但scanf()
会导致其他不易处理的问题。通常需要编写自定义代码才能获得精确且一致的语义。仅当要扩展的宏是一个简单的数字(例如,不是涉及加法或减法的更通用的表达式)时,才可以使用字符串化。
然后,您必须使用正常的双宏舞蹈才能正确扩展宏:
进而: