【问题标题】:Is it ok to pass an int array to scanf and printf when using the %s specifier?使用 %s 说明符时可以将 int 数组传递给 scanf 和 printf 吗?
【发布时间】:2021-11-26 09:33:20
【问题描述】:

我偶然发现了一个关于这段代码的问题

int arr[10];
scanf("%s", arr);
printf("%s", arr);

我不知道其目的,但我完全知道这段代码有多么难闻。但我的问题是这是否合法,如果我可以期望它重新打印我输入的字符串? (前提是输入的字符串不是太长导致缓冲区溢出)

我还想知道是否有任何实际有用的示例。

来自关于fprintf的C标准https://port70.net/%7Ensz/c/c11/n1570.html#7.21.6.1p8

如果不存在 l 长度修饰符,则参数应为指向字符类型数组的初始元素的指针。

和 fscanf https://port70.net/%7Ensz/c/c11/n1570.html#7.21.6.2p12

如果没有 l 长度修饰符,对应的参数应该是一个指向一个足够大的字符数组的初始元素的指针,它可以接受序列和一个终止的空字符,它将自动添加。

在这种情况下,它不是字符数组。但我似乎记得有一些关于与 char 指针的隐式转换的特殊规则,但我也记得这些不适用于可变参数函数。

【问题讨论】:

  • 它的形式是 UB,但如果 scanf 只是将指针转换为 char*(并且标准库被编译为标准 C,这绝不是必需的),那么它将起作用。跨度>
  • 可以通过在scanfprintf参数中使用(char *)arr使其合法化。但请注意,scanf 可能会将arrint 元素设置为陷阱表示,这可能会导致在读取int 元素时引发实现定义的信号。
  • @TomKarzes 你可能是整个历史上第一个犯这个错误的程序员;)

标签: c printf scanf language-lawyer


【解决方案1】:

程序将运行,但警告说明%s 期望变量arrchar * 类型,否则为char 数组。 因此,输出将是用户根据程序要求键入的字符串...

【讨论】:

  • 我看不出这是否合法
  • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
【解决方案2】:

对于语言律师,我会说这是非法的:

从 C11:7.21.6。

s 如果不存在l 长度修饰符,则参数应为指向字符类型数组的初始元素的指针。 280)

9 如果转换规范无效,则行为未定义。 282) 如果任何参数不是相应转换规范的正确类型,则行为未定义。

对于任何实际用途,我想说的是,只要您输入的字符数不超过数组的容量,我想不出它可能会失败的场景。

问题扩展后更新:

但我似乎记得有一些关于与 char 指针之间的隐式转换的特殊规则,但我也记得这些不适用于可变参数函数。

只有在已知目标类型的情况下才能进行正确的隐式转换。对于可变参数函数,不会对指针进行此类转换。

而且在那些将进行隐式转换的情况下,只有其中一个是void *,而不是在int *char * 之间才有可能。

【讨论】:

    【解决方案3】:

    ...关于 char 指针的隐式转换有一些特殊规则

    不,这不正确。

    对于 void 指针,您有隐式转换,但对于 char 指针则没有。在非常古老的 K&R 时代(即在引入“void”之前)char 指针被用作“通用指针”类型,但仍然没有隐式转换。

    也许您将转换与别名混淆了。对于别名 char 指针是特殊的,因为 char 指针允许为其他对象类型别名。

    结论是代码具有未定义的行为。在需要 char 指针时传递 int 指针不符合标准。

    【讨论】:

    • “也许你把转换和别名混淆了” - 是的。出于某种原因,我总是把它搞混
    【解决方案4】:

    您可以使用char 引用访问intint 数组; C 2018 6.5 7 表示可以使用字符类型来访问对象。

    但是,对于 %sscanfprintf,应该传递 char *,而不是 int *。即使char * 可以用于(取消引用)来访问intint 的数组,但这并不意味着int * 将代替char *。当scanfprintf 尝试获得他们期望的char * 时(如使用va_arg),int * 被传递的事实使得行为未由 C 标准定义。

    如果您将int * 转换为char *

    scanf("%s", (char *) arr);
    printf("%s", (char *) arr);
    

    那么行为可以说是由 C 标准定义的。 (“可以说”是因为 scanfprintf 的内部结构没有正式指定。推测它们与 6.5 7 中的别名规则一致。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-20
      • 1970-01-01
      • 2019-09-27
      • 2016-04-27
      • 1970-01-01
      • 1970-01-01
      • 2016-06-14
      • 1970-01-01
      相关资源
      最近更新 更多