C ++:格式不是字符串文字,也不是格式参数

时间:2014-06-05 20:02:15

标签: c++ char stack bison

我一直在尝试打印一个字符串文字但似乎我做错了,因为我收到了编译警告。这可能是由于错误的格式化或我对c_str()函数的误解,我认为这应该返回string

parser.y: In function ‘void setVal(int)’:
parser.y:617:41: warning: format not a string literal and no format arguments [-Wformat-security]

第617行:

sprintf(temp, constStack.top().c_str());

有这些声明

#include <stack>
const int LENGTH = 15;
char *temp = new char[LENGTH];
stack<string> constStack;

如何为字符串提供正确的格式化?

3 个答案:

答案 0 :(得分:9)

简单 - 提供格式字符串:

sprintf(temp, "%s", constStack.top().c_str());

但更好,更好:

string temp = constStack.top();

答案 1 :(得分:4)

您在评论中告诉我,问题不在于您的代码没有达到预期效果的警告。

解决这个和其他类似问题的方法是摆脱C ++代码中强大的C影响。具体来说,不要使用原始动态分配的char数组或sprintf。请改用std::string

在这种情况下,您使用sprintf非常错误。你见过它的签名吗?它是这样的:

sprintf(char *str, char const *format, ...)

str 操作的输出。 format 描述输出应该是什么。其余的是格式参数,它必须通过纯约定匹配format中描述的内容。

现在这个“休息”写成...,意味着你可以传递任意数量的参数,甚至是零。这就是为什么你的代码甚至可以编译(顺便提供一个很好的例子,说明为什么...是一个危险的特性)。

在您的代码中,输出字符串可能不正确,是您的temp字符串。描述输出的格式几乎肯定是错误的,它会发生在你的堆栈顶部。

这只是一个字符串分配给另一个字符串,使用sprintf只是因为它或多或少可以作为其功能集提供的特殊情况吗?没有必要进行这样的黑客攻击,因为C ++的std::string开箱即用了字符串:

std::string temp = constStack.top();

请注意,这也消除了提前知道字符串长度的需要。

如果由于某种原因,您确实需要格式化(但您的问题并不真正显示它需要),那么请详细了解字符串流作为格式化字符串的替代解决方案。< / p>

答案 2 :(得分:3)

警告表明它是-Wformat-security选项的结果;你可以通过删除选项来禁用警告;但这可能是不明智的。

除非您的代码要广泛分发,否则security issue可能是理论上的。或许更直接的问题是代码崩溃或异常行为的可能性。

问题是字符串是可变的,并且可能在运行时包含格式化字符,导致它尝试读取不存在的参数。例如,如果从用户输入接收到字符串并且用户输入“%s”,则它将尝试从堆栈上的某个位置读取字符串。这最多会在temp中放置垃圾,但更糟糕的是如果内存读取在前15个字节中没有包含空字符,则会超出临时值并损坏堆(在本例中)。堆损坏可能比堆栈损坏更糟糕 - 潜伏的bug在你的代码中很长时间没有引起注意,只是在经过一些无关的更改后才开始崩溃;如果它确实崩溃了,它就不可能与原因有任何关系。