我正在使用某些C示例,并使用函数strcpy()
,但是尽管我包含了<string.h>
,但是却忘记了包含<stdio.h>
。令我惊讶的是,代码成功运行了。以下是我正在执行的代码:
#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)
我什至没有收到任何编译警告。
为什么我的代码在不包含<string.h>
的情况下工作?
谢谢。
答案 0 :(得分:3)
编译器假定int strcpy()
并调用古老的向后兼容行为。指针参数是可以使用的一种类型,所以很好。
不要依赖它。安全导轨已关闭。
答案 1 :(得分:3)
我记得,GCC 3默认为C89一致性以及GNU扩展。 C89允许在不事先声明的情况下调用函数-假定它们返回int
,并且从参数类型推断出它们的参数类型。如果结果隐式类型恰好与被调用函数的实际类型相匹配(在旧版C代码中比在现代代码中更有可能),那么一切都很好。如果不是这样,则该行为是不确定的,但任何不确定的行为都可能发生,甚至是编写代码的程序员所期望发生的一切。
此外,尽管标准未指定任何标准标头包含其他标头,但也不禁止这样做,实际上,在某些C实现中,其中一些标头也可以。如果您的实现的stdio.h
包含string.h
或以其他方式提供了strcpy()
的兼容定义,那么对于该实现就可以了。但是,如果这是您所依赖的,则无论有意还是无意,当您尝试将程序与其他实现一起使用时,都可能会出现意外失败的风险。
最后,请注意,GCC 3.3的版本非常。如果有可能,您应该升级到较新的版本。甚至那些长期稳定的Linux也倾向于至少在4.x系列中使用最新版本,而最新版本是GCC 8.3。
答案 2 :(得分:2)
string.h
包含文件告诉编译器如何 strcpy()
是通过给出函数的声明来定义的,但它并不提供函数本身,而是位于在库中,并会自动与您的程序链接。
如果尚未在到达函数时声明该函数,则编译器将根据默认值和您如何使用该函数假定一个声明。
编译器接受您编写的内容,因此message
和Hello, world!
的地址并假定函数返回int
。由于include
,您不必告诉编译器如何编译代码,从而冒了风险。结果是不确定的行为。
顺便说一句,Hello, world!
包含14个字符,13个+尾随的\0
,比您声明的message
多4个字符。这是未定义的行为,因为多余的4个字符将被写在不应包含的位置。似乎正在工作,崩溃...
您不能依赖显示预期结果的程序。这是未定义的行为。
始终使用-Wall
之类的警告选项进行编译,并考虑修复所有警告。
答案 3 :(得分:0)
编译器知道一些“内置”函数(printf()
是某些编译器的另一个示例),并通过提供它来弥补您的错误。依靠它是一个坏习惯,因为即使您今天的编译器这样做,也不能保证您另一个编译器也会这样做,因此编译后的代码将不可预测。
尝试使用-Wall -Wextra
进行编译,以使编译器显示更多警告,包括有关您已忘记的标头的警告:gcc -o input input.c -Wall -Wextra
。
也请参见此处:warning: incompatible implicit declaration of built-in function ‘xyz’