【问题标题】:Why strcpy() is working without including <string.h>为什么 strcpy() 在不包含 <string.h> 的情况下工作
【发布时间】:2019-04-02 14:19:22
【问题描述】:

我正在处理一些 C 示例,并使用函数 strcpy(),但忘记包含 &lt;string.h&gt;,尽管我已包含 &lt;stdio.h&gt;。令我惊讶的是,代码运行成功。以下是我正在执行的代码:

#include <stdio.h>

int main() {
   char message[10];
   int count, i;

   strcpy(message, "Hello, world!");

   printf("Repeat how many times? ");
   scanf("%d", &count);

   for(i=0; i < count; i++) {
      printf("%3d - %s\n", i, message);
   }
}

我正在使用gcc version 3.3.6 (Ubuntu 1:3.3.6 - 15ubuntu1)

我什至没有收到任何编译警告。

为什么我的代码不包含&lt;string.h&gt; 也能正常工作?

提前致谢。

【问题讨论】:

  • 未定义的行为就是这样。你不会总是那么幸运,编译器警告应该被认真对待。
  • @AbhayKumar 尝试使用-Wall -Wextra 启用警告。
  • 是否确定行为未定义,@WeatherVane?如果该实现的stdio.h 包含string.h(似是而非,但不能保证)怎么办?
  • 把旋钮调到 11。 C4013: 'strcpy' undefined;假设 extern 返回 int。
  • strcpy(message, "Hello, world!"); 肯定是缓冲区溢出

标签: c gcc


【解决方案1】:

编译器假定int strcpy() 并调用古老的向后兼容行为。指针参数是它适用的类型之一,所以没问题。

不要依赖它。安全栏杆已关闭。

【讨论】:

    【解决方案2】:

    我记得,GCC 3 默认符合 C89 和 GNU 扩展。 C89 允许在没有事先声明的情况下调用函数——假定它们返回int,并且它们的参数类型是从参数类型中推断出来的。如果生成的隐式类型恰好与被调用函数的实际类型匹配(在遗留 C 代码中比在现代代码中更有可能),那么一切都很好。如果不是,则行为未定义,但未定义行为可能会发生任何事情,甚至是编写代码的程序员预期会发生的情况。

    此外,尽管标准没有指定任何标准头文件包含其他头文件,但它们并没有被禁止这样做,并且在实践中,在某些 C 实现中,其中一些会这样做。如果碰巧您的实现的stdio.h 包含string.h 或以其他方式为strcpy() 提供兼容的定义,那么该实现 就可以了。但是,如果这就是您所依赖的东西,无论是否有意,那么当您尝试使用具有不同实现的程序时,您将面临意外失败的风险。

    最后,请注意 GCC 3.3 非常旧。如果可能的话,你应该升级到更新的东西。即使是陈旧的、长期稳定的 Linux 也倾向于使用至少 4.x 系列的后期版本,最新的是 GCC 8.3。

    【讨论】:

    • GCC 5.1(2015 年 4 月)是第一个默认为 C11 的版本;没有版本默认为 C99。 GCC 3 非常非常老了(2006 年 3 月的 3.4.6 是最后一个 3.x 版本;2005 年 5 月 3.3.6 日;2004 年 5 月 3.3 日)。来自GCC Releases的信息。
    【解决方案3】:

    string.h 包含文件告诉编译器如何 strcpy() 是通过给出函数声明来定义的,但它不提供函数本身,而是位于库中并将自动与您的程序链接。

    如果函数在到达它时没有被声明,那么编译器将假设一个声明,基于默认值和你如何使用函数。

    编译器获取您编写的内容,即messageHello, world! 的地址,并假定函数返回int。由于include,您不告诉编译器它必须如何编译代码是冒险的。结果是未定义的行为。

    顺便说一句,Hello, world! 需要 14 个字符,13 + 结尾的 \0,比你声明的 message 多 4 个字符。这是未定义的行为,因为额外的 4 个字符将被写入不应写入的位置。看似工作,崩溃...

    您不能依赖显示预期结果的程序。这是未定义的行为。

    始终使用警告选项进行编译,例如 -Wall,并考虑修复所有警告。

    【讨论】:

    • 关于声明和实现之间的区别,这是一个很好的观点,并且经常被新手误解(+1)。然而,您的措辞可能会导致混淆,因为如果标题确实包含 strcpy() 的定义,那么它提供该功能。它包含一个声明,它告诉编译器如何定义函数。
    • 在这种情况下,我希望您不要反对进一步的调整(已编辑)。
    【解决方案4】:

    编译器知道一些“内置”函数(printf() 是一些编译器的另一个例子),并通过提供它来弥补你的错误。依赖这个是一个坏习惯,因为即使你今天的编译器这样做,也不能保证另一个编译器也会这样做,所以编译的代码是不可预测的。

    尝试使用-Wall -Wextra 进行编译,以使编译器显示更多警告,包括您忘记的标头的警告:gcc -o input input.c -Wall -Wextra

    另见此处:warning: incompatible implicit declaration of built-in function ‘xyz’

    【讨论】:

      猜你喜欢
      • 2011-08-13
      • 2020-09-07
      • 2017-11-19
      • 1970-01-01
      • 1970-01-01
      • 2017-10-03
      • 2018-06-01
      • 2014-10-10
      • 2021-12-27
      相关资源
      最近更新 更多