如果我们打印一个包含“%s”的字符串,输出将是什么?

时间:2019-07-16 14:29:03

标签: c printf undefined c-strings

我想知道如果我们打印内容中包含“%s”的字符串会得到什么结果?

我尝试将其打印为“ hi%s”。

library(googleAuthR)
library(bigrquery)

scopes = c("https://www.googleapis.com/auth/bigquery")


options(googleAuthR.scopes.selected = scopes)
service_token <- gar_auth_service(json_file="somejason.json")
gar_auth_service("somejason.json")


projectid<-'project_id'
datasetid<-'id'
bq_conn <-  dbConnect(bigquery(), 
                      project = projectid,
                      dataset = datasetid, 
                      use_legacy_sql = FALSE
)

tables_list <- dbListTables(bq_conn)

The bigrquery package is requesting access to your Google account. Select 
a pre-authorised account or enter '0' to obtain a new token. Press 
Esc/Ctrl + C to abort.

1: ("Account I've used to authenticate")

我希望输出为“ hi”。 但我以“ hi hi%s”的名字来获得它。

  

“嗨,嗨,%s”

5 个答案:

答案 0 :(得分:8)

如在其他答案中指定的,将发生未定义的行为。

在这种情况下未定义的行为是什么意思:  当printf收到带有 n 个格式说明符(例如%s)的字符串时,它将期望将 n 个参数传递给该函数除了字符串。因此,当您有一个类似printf("hi%s")的语句时,该函数的行为就好像您传递了一个参数(在这种情况下,第二个参数应为char *),即使没有。该函数只是要获取堆栈中的下一个值,在这种情况下,这是一些垃圾值。然后,该函数将引用该垃圾值,并将其视为字符缓冲区。未定义此行为的原因是无法确定堆栈上的垃圾值是多少。

可能的结果

  1. 堆栈上的垃圾值为0->取消引用垃圾值时出现分段错误。

  2. 堆栈上的垃圾值恰好是一个有效的内存地址->内存位置的字节将一直插入到字符串中(在这种情况下为“ hi”),直到该值的一个字节为止遇到0或发生分段错误。

  3. 堆栈上的垃圾值不是0,但不是有效的内存位置->垃圾值被取消引用时出现分段错误。

产生同样的情况 下面的代码部分与printf("hi%s")

非常相似
void foo() {
   char * myJunkValue;  // Since not a global/static variable, this is not guaranteed to be 0
   printf(myJunkValue); // Undefined behavior
}

答案 1 :(得分:6)

您的程序调用{​​{3}}。

您的代码等同于

 printf("hi%s");

其中%s是一个转换说明符,它期望提供一个参数。

引用C11,第7.21.6.1章

  

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

建议:如果您只需要打印字符串而无需进行任何转换(格式化),则可以使用undefined behaviour

答案 2 :(得分:5)

您不是在“打印内容中包含%s的字符串”。您正在将诸如 format string 之类的字符串传递给printf,并且这样做没有格式字段的匹配参数,您的程序就具有未定义的行为printf的第一个参数不是您要打印的字符串。这是一个格式字符串,它控制如何解释/转换其余参数,并且还可以包含将其合并到其中的文字文本。

“打印内容中包含%s的字符串”(如您的问题,其中ptr指向该字符串)可以通过printf("%s", ptr)puts(ptr)完成

答案 3 :(得分:4)

会有未定义的行为。:)

函数将字符串文字%s的这一部分视为格式说明符。

根据C标准(7.21.6.1 fprintf函数)

  
      
  1. ...如果格式的参数不足,则行为为   未定义。
  2.   

如果您想按原样输出字符串"hi%s",则应定义文字"hi%%s"

这是一个演示程序

#include <stdio.h>

int main(void) 
{
    char *ptr = "hi%%s";

    printf( ptr );

    return 0;
}

其输出为

hi%s

答案 4 :(得分:0)

正如其他回答所提到的那样,您正在执行的操作将调用undefined behavior,因为您在调用printf时没有给定格式说明符的足够参数。就您而言,您有垃圾输出,但您的程序可能同样容易崩溃。

要对此进行扩展,请使用如下代码:

printf(ptr);

几乎总是错误的。如果ptr指向的字符串可以由用户以任何方式控制,则您可以打开format string vulnerability。用户可以使用%s%x等提供特制的字符串,以转储内存的内容并公开敏感数据,或者使用%n提供可以写入内存和允许攻击者接管您的程序。

由于这个原因,如果printf的第一个参数不是字符串常量,许多静态分析器都会发出警告。