#include<stdio.h>
int *arr;
int main()
{
arr = calloc(1, sizeof(int));
free(arr);
return 0;
}
据我所知,出现这个警告是因为我没有在header中声明函数(在本例中,我应该包含stdlib.h
)。我的问题是:
1.为什么愚者没有给予错呢?因为据我所知,calloc
位于stdlib.h
,但我没有把它包含在我的程序中,为什么我的程序仍然知道什么是calloc
?
1.我们应该闭上眼睛吗?因为我的程序即使不包含stdlib.h
也能很好地工作。
4条答案
按热度按时间m528fe3b1#
据我所知,出现这个警告是因为我没有在header中声明函数(在本例中,我应该包含
<stdlib.h>
)。正确。编译器抱怨您正在调用一个它没有看到声明的函数。包含
<stdlib.h>
确实为函数calloc
提供了一个正确的声明。请注意,您也可以通过添加以下行自己提供一个声明:void *calloc(size_t, size_t);
。但是,建议使用标准的包含文件来获取库函数的确切声明。为什么愚者没有给予错误?
因为编译器对编译那些在C标准发布之前编写的老程序是比较宽容的。在作用域中没有声明或定义的情况下调用函数过去并不是什么错误。编译器只会从提供的参数类型推断原型。在您的例子中,原型被推断为
int calloc(int, size_t)
。这显然是不正确的,并且会导致未定义的行为。为了避免这样的问题,编译器发出了关于calloc
未声明的警告。为什么我的程序仍然知道什么是
calloc
?实际上,编译器还应该抱怨
int
返回值在存储到arr
时被隐式转换为int *
。如果int *
和int
在目标系统上有不同的表示形式,(在64位系统上也是如此),arr
的值将是无效的,并且free(arr)
肯定也会有未定义的行为。编译器会生成一个可执行文件,因为函数calloc
在C库中找到,它隐式链接到所有C程序。对于C程序,在链接时不检查参数和返回类型。我们应该对警告视而不见吗?
当然不是。你应该使用
gcc -Wall -Wextra -Werror
来编译你的程序,并启用所有的警告,gcc会把所有的警告当作致命的错误,并拒绝生成可执行文件,直到它们被纠正。这将为你节省许多小时的调试时间。遗憾的是,这种行为不是默认的。即使不包含
<stdlib.h>
,我程序也能很好地工作。它在你的系统上看起来可以工作,但在我的系统上却失败了(假设我删除了阻止
gcc
生成可执行文件的默认编译器选项)。程序有未定义的行为。不要依赖于机会。下面是一个正确的版本:
2jcobegt2#
calloc
不在stdlib.h
中。stdlib.h
中没有任何内容。任何.h
中没有任何内容(或应该没有任何内容)。stdlib.h
中的内容只是(我的意思是大约calloc
)calloc
在libc
中。当程序调用它时,它将在那里找到它。严格地说,您不需要任何其他东西(我的意思是,从执行的Angular 来看)就可以调用函数。
所有的
.c
代码都被编译来完成它们的任务。libc
很早以前就被编译过了。在链接时(对于静态库,以及指向它们的动态库),然后在运行时,所有编译过的代码中的所有函数都被加载。所以你的代码中的main
和calloc
会在那时找到对方。问题不在那里。
问题在于,为了编译main,编译器需要知道应该如何调用
calloc
。它需要知道它来检查语法错误。否则你可能会传递3个参数,或者只有一个,到
calloc
,编译器将无法知道这是不正确的。你可能会传递错误类型的参数,等等。它还需要知道它应该作为参数推送多少字节,甚至应该如何传递参数。
例如,请参阅这两个代码
one.c
two.c
使用编译它们(取决于您的编译器)
在我的电脑上打印
请注意,它只对使用int调用的
printInt
和使用double调用的printDouble
有效。它无法将1.0
转换为int来调用printInt
,或者相反,无法将1
转换为double来调用printDouble
。对于printFloat
,它在所有情况下都失败。因为编译器错误地假定了要压入的参数的大小。但除此之外,这3个函数都被调用了。缺少的不是函数的代码,而是编译器在调用它们时正确调用它们的能力。
只需在
two.c
中添加声明(Or创建一个包含这些元素的
one.h
,并在two.c
中创建#include "one.h"
,它会导致相同的结果)现在,输出符合预期
我甚至还没有开始使用类型声明。
#include
并不意味着提供库和其中的函数。这是在链接时做的,将.o
和-lsomelib
添加到喜欢的命令行(或使用其他方式,取决于您的编译器)。#include
提供了编译器需要知道如何调用这些函数的无代码声明。nqwrtyyt3#
1.函数并不位于
stdlib.h
,它位于同一个共享库对象libc.so
中,所以链接器找到符号没有问题。如果你试图调用一个构造函数,你也不会得到编译器错误,相反,你的链接器会抱怨无法解析符号。1.技术上可以,但绝对不应该。如果你不这样做,就不会收到参数错误的警告。如果你提供了正确的参数数量和类型,链接器不会(也不能)检查。
示例:
如果现在编译:
gcc main.c a.c
您只会收到一个警告,而如果您包含了头文件,则会收到一个错误。这是BAD,因为它可能导致未定义的行为,从而导致意外崩溃、内存损坏、安全问题等。你应该给予你的编译器一个公平的机会来帮助你。
**编辑:**澄清,忽略是不好的
xqk2d5yq4#
1.为什么愚者不给予错呢?因为据我所知,calloc位于stdlib. h,但是我没有把它包含在我的程序中,为什么我的程序仍然知道什么是calloc呢?
为了与传统代码兼容,这不是一个错误,因此编译不能中止,因此,您会得到一个警告。在旧的传统C代码中,如果函数返回
int
结果,您可以使用没有声明的函数(如果您没有为函数指定原型,则假定为原型)。旧的K&R代码在C98中仍然有效(我不能保证以后的标准版本,但至少我已经测试了K&R代码,它编译时最多有一些可以忽略的警告),因此它必须编译成工作代码(但仍然可能是无效代码,因为生成的调用calloc的代码将认为返回的是int
---而calloc
实际上返回的是void *
指针类型,在64位体系结构中为64位)请尝试以下代码,然后查看:)
上面的代码在运行unix v7的pdp 11(发布前刚刚测试过)和gcc最新版本中编译没有任何问题。
1.我们应该对警告视而不见吗?因为我的程序即使没有包含stdlib. h也能很好地工作。
我认为你永远不应该对任何警告视而不见。如果你只是
#include <stdlib.h>
,编译器将正确编译源代码而不发出警告。关于警告的重要一点是你应该阅读它们,并理解它们(以及忽视它们的后果)在继续之前。减弱只是一个建议性的信息,它不会中断编译,因为这可能是编译器运行的预期目的(只是为了编译旧的遗留代码,这些代码将生成有效的程序,但这些代码是很久以前编写的)在上面的例子中,你最好做正确的
#include <stdio.h>
,因为如果你不这样做,您的代码将是错误的。它将假定calloc()
函数返回int
(在64位体系结构中大小不同),并且该值可能是截断的指针(一个指针,其中的某些部分由于转换为int
而被截断),并且将不起作用(但最近发生了这种情况,在64位架构下,在32位架构上很好,不正确,但工作正常)所以,你可以服从或不服从,但你只能靠自己了。