C++ %s的printf()的不同运行时检查?

C++ %s的printf()的不同运行时检查?,c++,runtime-error,printf,C++,Runtime Error,Printf,我知道,为了正确使用,我们需要根据const char*格式中定义的内容进一步传入相同数量的值,即%s/%f/%d.. int printf( const char* format, ... ); 我只是注意到,尽管不推荐,但如果我们不传入任何值,它将在没有任何运行时错误的情况下运行,如以下所示(当然,我们将得到非预期的结果): 如果这适用于所有格式,我会毫不奇怪地认为printf()不做任何运行时检查。奇怪的是,它会给出%s(似乎也是唯一的一个)的运行时错误 更有趣的是,它似乎只对第一个或连

我知道,为了正确使用,我们需要根据
const char*格式中定义的内容进一步传入相同数量的值,即
%s/%f/%d..

int printf( const char* format, ... );
我只是注意到,尽管不推荐,但如果我们不传入任何值,它将在没有任何运行时错误的情况下运行,如以下所示(当然,我们将得到非预期的结果):

如果这适用于所有格式,我会毫不奇怪地认为
printf()
不做任何运行时检查。奇怪的是,它会给出
%s
(似乎也是唯一的一个)的运行时错误

更有趣的是,它似乎只对第一个或连续的
%s
进行运行时检查。请查看以下示例:

printf("%s%s", "xxx"); // run-time error: Access violation reading location
printf("%s%d%s", "xxx"); // ok
所以,我的问题是:

  • printf()
    运行时检查
    %s
    是否与其他格式不同?为什么
  • 为什么
    printf()
    只对第一个或连续的
    %s
    执行运行时检查

PS:我在VS2010、OS:Wi7x64、

< P>(ED:注:这个问题最初是用C++标记的,这个答案假设OP是C++编写的) 尽管不推荐使用,但在以下情况下,它将在没有任何运行时错误的情况下运行: 我们不传递任何值

不,这会产生未定义的行为。任何事情都可能发生,包括你所期望的。这并不意味着它可以

printf
不会在运行时检查您发送的任何垃圾,如果您发送了任何垃圾。您有责任确保编写正确的代码

顺便说一句,您在后面的案例中遇到了“未处理的异常”错误——这不是
sprintf
执行运行时检查的结果。这是另一种未定义行为的表现

所有这些奇怪而危险的行为都是因为sprintf是一个类型不安全、危险的函数。编写编译器乐于接受的代码非常容易,在测试中甚至在生产中运行一段时间似乎都很好,但以某种微妙的方式表现出未定义的行为。这通常会在你周末外出前的一个小时,在周五咬你


这里的人生教训是:不要在C++中使用sprintf。改用现代且类型安全的工具,如streams。

没有运行时检查
printf
将盲目读取堆栈上的任何乱码(或正在使用的任何var arg机制)。在
%s
的情况下,它将乱码解释为指针,然后尝试从该地址读取内容,通常会导致访问冲突*

本质上,你看到的是未定义的行为



*好的,我想这是一种运行时检查,在OS/HW级别。

这是未定义的行为,所以任何事情都可能发生。实际上,您看到的差异可能是由于您使用的所有其他说明符都直接引用堆栈上的值,因此会显示垃圾,但不会崩溃。对于%s,它是不同的-随机值被视为指针,并且您的程序访问内存的随机区域,因此它很可能崩溃。
话虽如此,它仍然是未定义的,不应该使用。

没有运行时检查,技术上很难做到这一点。但也有编译检查可用

使用GCC,它是
-Wall

使用Visual Studio,您需要运行它的静态分析工具(在更昂贵的版本上提供)。

printf(“%d”)
:尝试从可能非法的内存地址读取4个字节

printf(“%s”)
:试图从可能非法的内存地址读取未知数量的字节,直到遇到值为0的字节

在这两种情况下,是否会发生内存访问冲突是“运气的问题”

但是,您可以理解,在代码< > %s >代码>的情况下,这些几率要高得多。在这种情况下,C++标准回落到了这一点:在代码< 7.19.61段中,这是未定义的行为。FPrTNF函数涵盖了代码> Prtff <代码>,关于格式说明符,说:

[…]如果格式的参数不足,则该行为为 未定义

因此,正如标准中对未定义行为的定义所述:

可能的未定义行为包括完全忽略不可预测的情况 结果是,在翻译或程序执行过程中以环境特有的文件化方式(有或没有发出诊断消息)表现,终止翻译或执行(发出诊断消息)

任何事情都有可能发生


由于
printf
是一个函数,它必须依赖调用者来正确表示传入的数据。所以运行时检查是不可能的。尽管有几个编译器提供编译时检查,例如,
gcc
甚至将此检查作为一个属性提供,可以与您自己的函数一起使用。

请继续阅读
printf
就是这样一个函数。使用可变函数时,不能对参数进行任何运行时检查,因此必须依赖另一个已知参数,例如
printf
中的格式字符串。如果您作为程序员没有给它正确的参数,它将读取堆栈上的任何垃圾<代码>%d
和其他人打印这些垃圾值<代码>%s将垃圾值作为指针并取消引用,这会导致分段错误(访问冲突)。我也很好奇,Prftfys不提供任何检查吗?@ SaffikyAgMurM:只是因为这里没有任何代码不能在C下编译,这并不能使C成为一个问题。在C++下使用<代码> SpReFTF <代码>是不明智的。唯一相关的是
printf("%s"); // run-time error: Access violation reading location
printf("%s%s", "xxx"); // run-time error: Access violation reading location
printf("%s%d%s", "xxx"); // ok