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

C++ C++;:格式不是字符串文字,也没有格式参数,c++,char,stack,bison,C++,Char,Stack,Bison,我一直在尝试打印字符串文字,但似乎我做得不对,因为我得到了编译警告。这可能是由于格式错误或我对c_str()函数的误解,我认为该函数应该返回字符串 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()
函数的误解,我认为该函数应该返回
字符串

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;
#包括
常数int长度=15;
char*temp=新字符[长度];
堆栈constStack;

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

简单-提供格式字符串:

sprintf(temp, "%s", constStack.top().c_str());
但好得多:

string temp = constStack.top();

由于警告表明它是由于
-Wformat security
选项而发出的;您只需删除该选项即可禁用警告;但这可能是不明智的

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


问题在于字符串是可变的,并且在运行时可能包含格式字符,导致它尝试读取不存在的参数。例如,如果该字符串是从用户输入接收的,并且用户输入了“%s”,则它将尝试从堆栈上的某个位置读取字符串。这最多只能将垃圾放在
temp
中,但更糟糕的是,如果读取的内存在前15个字节中没有包含nul字符,它将溢出temp,并损坏堆(在本例中)。堆损坏可能比堆栈损坏更严重-潜在的错误可能会在代码中被忽略很长一段时间,直到在一些不相关的更改后才开始崩溃;如果它真的崩溃了,它不太可能接近原因。

您在评论中告诉我,问题与其说是警告,不如说是代码没有达到预期的效果

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

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

sprintf(char *str, char const *format, ...)
str
是操作的输出<代码>格式
描述了输出应该是什么。其余的是格式参数,按照惯例,这些参数必须与
format
中描述的内容相匹配

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

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

这仅仅是使用
sprintf
一个字符串分配给另一个字符串,因为它或多或少可以这样做,这是它的功能集提供的一个非常特殊的情况吗?因为C++具有“<代码> STD::String >:

”的字符串分配,所以不需要这样的黑客。
std::string temp = constStack.top();
请注意,这也消除了提前知道字符串长度的需要


如果出于某种原因,您确实需要格式化(但您的问题并没有显示出任何格式化的必要性),那么请进一步了解字符串流作为格式化字符串的替代解决方案。

您只是询问如何禁用警告,还是您的代码真的没有达到您预期的效果?@ChristianHackl,第二种选择:我对我的问题进行了编辑,使之有所改进,希望落选票消失。如果你们中有人投我反对票,我能知道原因吗?我可以改进哪一部分?这是一个相当古老的问题。我知道这是重复的。我自己没有投反对票,但我认为你问题的主要问题是你没有提供MCVE。您提供的小代码如果没有大量繁琐的修改(添加
main
函数、add
std::
等)就无法编译,而且它甚至没有显示
c_str()
的任何用法。这让其他人猜测你的真实代码可能是什么,这很烦人。@ChristianHackl我很感谢你的意见,并将根据你的建议尝试改进问题。@nobodynoone请解释第二个代码是如何不起作用的?你有错误吗?+1。。。或者使用std::istringstream对象-sprintf()有更多的安全问题,仅此而已。然而,在这种情况下,非常明显的strcpy()(或者更好的是
strncpy()
)也会起作用。@nobodynoone:那么以后使用
temp.c_str()
有什么困难呢?第二个版本比第一个版本至少少了两个bug。@nobodynoone:这没什么意义。对于
constack.top()
,您很乐意使用
std::string
,只需在接口处转换为
char*
,即可编写不在您控制范围内的代码。我不确定作为一个“编译器项目”是如何合理的。很好的解释!谢谢!我可以从中学到一些;)谢谢+1对于stringstream,尽管正如您所指出的,它是sprintf的一个很好的替代品,但这两种方法都不适用于这段代码所做的事情—strcpy@克利福德:是的,OP似乎滥用了sprintf来复制字符串。在任何情况下,格式字符串通常都是更好的方法,例如,当复杂的输出必须针对不同的语言进行本地化时,某些语言可能会改变单个元素的顺序(例如德语中的“Show%USERNAME%”与“%USERNAME%anzegen”的对比)。使用面向流的解决方案,定位是硬编码的。在这种情况下,可以考虑Booop.Frand来结合灵活性和类型安全性。