C语言中,函数不申明也能使用,但会出现warning: implicit declaration of function

本文探讨了在C语言中,函数未经申明就能使用的现象。通过实验,解释了在VC6和Linux环境下,函数未声明使用时的编译行为和警告信息。C语言允许函数隐式声明,但在C++中则需要预先声明。最后强调了良好的编程习惯,建议程序员开启编译警告并处理所有警告。

这段时间,在看中心后台服务软件源码时发现,有很多自定义函数未经申明却能在主程序中被调用,主程序中没有包括上述函数的头文件,我在各个目录中也找不到上述函数的头文件。这就奇怪了,连使用标准库函数printf()都要包括标准输入输出头文件<stdio.h>,何况是自定义函数?这个问题困扰了我很久。前天问中创公司奚钟华,他说:确实没有头文件,它也试过,在原来的目录中编写了一些新的函数,照葫芦画瓢地在Makefile中添加了相应项,生成了库文件,在主程序中就可调用了,其中的机理它也说不清。

今天通过实验,基本明白了个中原因。

一、在VC6中,文件test1.cpp如下:

 

#include "stdafx.h"           // stdafx.h: #include <stdio.h>

 

int Max(int x, int y)

{

       return ((x > y ) ? x : y);

}

 

int main(int argc, char* argv[])

{

       int a = 300, b = 100;

       printf("Max(a, b): %d/n", Max(a, b));

       return 0;

}

 

程序正常运行,因为Max()在被调用前已经定义。

 

Max()移到main()之后:

#include "stdafx.h"           // stdafx.h: #include <stdio.h>

 

int main(int argc, char* argv[])

{

       int a = 300, b = 100;

       printf("Max(a, b): %d/n", Max(a, b));

       return 0;

}

 

int Max(int x, int y)

{

       return ((x > y ) ? x : y);

}

 

编译时出错:

error C2065: 'Max' : undeclared identifier

error C2373: 'Max' : redefinition; different type modifiers

 

main()之前添加Max()的声明:

#include "stdafx.h"           // stdafx.h: #include <stdio.h>

 

int Max(int x, int y);

 

int main(int argc, char* argv[])

{

       int a = 300, b = 100;

       printf("Max(a, b): %d/n", Max(a, b));

       return 0;

}

 

int Max(int x, int y)

{

       return ((x > y ) ? x : y);

}

 

程序正常运行,因为Max()在被调用前已经定义。

 

二、在Linux中,文件test2.c如下:

#include <stdio.h>

 

int Max(int x, int y)

{

       return ((x > y ) ? x : y);

}

 

int main(int argc, char* argv[])

{

       int a = 300, b = 100;

       printf("Max(a, b): %d/n", Max(a, b));

       return 0;

}

 

gcc编译:gcc -Wall -o test2 test2.c,通过。程序正常运行,因为Max()在被调用前已经定义。

 

Max()移到main()之后:

#include <stdio.h>

 

int main(int argc, char* argv[])

{

       int a = 300, b = 100;

       printf("Max(a, b): %d/n", Max(a, b));

       return 0;

}

 

int Max(int x, int y)

{

       return ((x > y ) ? x : y);

}

 

gcc编译:gcc -Wall -o test2 test2.c,出现警告:

warning: implicit declaration of function `Max'

仍然编译通过,程序也能正常运行,因为在C语言中,当函数在调用函数之前没有声明或定义,默认作为隐式声明处理,只要在调用函数之后定义,或在别的模块中定义并编译成库文件,该库文件在调用函数所属模块编译时载入,程序即可正常运行。

 

g++编译:g++ -Wall -o test2 test2.c,出现错误和警告:

error: `Max' was not declared in this scope

warning: unused variable 'Max'

没有生成可执行程序test2。因为g++使用C++的规则:函数在被调用前必须声明或定义。

 

三、在Linux中,采用实际工程的方式(分成若干模块)进一步实验,验证了C语言中函数在被调用前不申明也能使用。

1、在/u01/work/tools目录中,编写4个文件:

Max.c

int Max(int x, int y)

{

       return ((x > y ) ? x : y);

}

 

Min.c

int Min(int x, int y)

{

       return ((x > y ) ? y : x);

}

 

Add.c

int Add(int x, int y)

{

       return (x + y);

}

 

Makefile

#User defined library, by He Yaozhong

CC=gcc

HOME=/u01/work

OS=$(HOME)/tools

 

INCLUDE=$(HOME)/headfile -I$(HOME)/tools

 

CSTARGETS=Max.o Min.o Add.o

 

LIBS=$(HOME)/lib/tools.a

 

all: $(LIBS) clean

 

 

Max.o : Max.c

       $(CC) -I$(INCLUDE) -c $(CCFLAGS) Max.c

 

Min.o : Min.c

       $(CC) -I$(INCLUDE) -c $(CCFLAGS) Min.c

 

Add.o : Add.c

       $(CC) -I$(INCLUDE) -c $(CCFLAGS) Add.c

 

$(LIBS) : $(CSTARGETS)

       cd $(OS); /

       ar ruv $(LIBS) $(CSTARGETS:$HOME/lib/=)

 

clean:

       rm -f *.o

 

/u01/work/tools目录中,使用make工具:

[root@node01 tools]# make

gcc -I/u01/work/headfile -I/u01/work/tools -Wall -c  Max.c

gcc -I/u01/work/headfile -I/u01/work/tools -Wall -c  Min.c

gcc -I/u01/work/headfile -I/u01/work/tools -Wall -c  Add.c

cd /u01/work/tools; /

ar ruv /u01/work/lib/tools.a  Max.o Min.o Add.o

r - Max.o

r - Min.o

r - Add.o

rm -f *.o

生成了/u01/work/lib/tools.a库文件。

 

2、在/u01/work/test2目录中,编写2个文件:

test2.c

#include <stdio.h>

 

int main(int argc, char* argv[])

{

       int a, b;

 

       printf("Please input 2 integer (a, b): /n");

       scanf("%d", &a);

       scanf("%d", &b);

      

       printf("Max(a, b): %d/n", Max(a, b));

       printf("Min(a, b): %d/n", Min(a, b));

       printf("Add(a, b): %d/n", Add(a, b));

      

       return 0;

}

 

Makefile

CC=gcc

HOME=/u01/work

 

INCLUDE=-I$(HOME)/headfile -I.

 

CFLAGS= -L$(HOME)/lib

 

LIBS=$(HOME)/lib/tools.a

 

all : $(OBJS) test2 clean

 

test2 : test2.c

       $(CC) $(INCLUDE) -Wall -o test2 test2.c $(CFLAGS) $(LIBS)

 

clean :

       rm -f *.o

 

/u01/work/test2目录中,使用make工具:

[root@node01 func_pointer]# make

gcc  -I/u01/work/headfile -I.  -Wall -o test2 test2.c -L/u01/work/lib /u01/work/lib/tools.a

test2.c: In function `main':

test2.c:11: warning: implicit declaration of function `Max'

test2.c:12: warning: implicit declaration of function `Min'

test2.c:13: warning: implicit declaration of function `Add'

rm -f *.o

生成了/u01/work/test2/test2可执行文件。运行程序:

[root@node01 func_pointer]# ./test2

Please input 2 integer (a, b):

200

300

Max(a, b): 300

Min(a, b): 200

Add(a, b): 500

 

结果完全正确。

 

四、小结

CC++是强类型语言,变量类型均应在代码执行前确定。

在函数声明方面CC++则不同,C++语言中,在被调用之前未声明或定义是不允许的,而C语言是允许的。初看起来C语言这一特性是灵活、省事,但缺点是:

1、程序可读性差。

2、易出错。函数先声明再调用,是一种纠错机制,如果不声明,则没有用到这种纠错机制,虽然编译、连接通过,程序也能运行,但很可能结果不正确。

 

一个好的程序员,应该养成严谨、规范的编程习惯,编译程序时应打开显示所有警告选项“-Wall”,并且对编译器提出的所有警告都要给予处理,仅以编译、连接通过为目标,这可能会有潜在的危害。

 

请参阅Dai Yuwen的文章:《头文件不是可有可无的》(http://daiyuwen.freeshell.org/gb/programming/about_header_files.html

<think>我们收到了一个关于编译警告的具体问题:`warning: implicit declaration of function ‘perror’ [-Wimplicit-function-declaration]`。这个警告表示编译器在遇到`perror`函数调用时,没有找到它的声明。根据引用[3]和引用[1]中的类似案例,这通常是因为没有包含正确的头文件。 ### 解决方案 #### 1. **问题原因分析** - `perror`函数是C标准库中用于报告错误信息的函数,它需要包含头文件`<stdio.h>`。 - 当编译器在调用`perror`之前没有看到其声明(即没有包含相应的头文件)时,它会按照C89标准进行隐式声明(假设函数返回`int`类型,参数类型根据实际调用推导)。然而,隐式声明在C99及以后的标准中已被废弃,因此编译器会发出警告。 #### 2. **解决办法** - **添加头文件**:在调用`perror`的源文件顶部添加以下包含指令: ```c #include <stdio.h> ``` - **验证修复**:重新编译代码,警告应消失。 #### 3. **原理说明** - `perror`函数在`<stdio.h>`中声明,其原型为: ```c void perror(const char *s); ``` - 包含头文件后,编译器会正确识别函数签名,避免隐式声明导致的类型匹配风险。 #### 4. **扩展建议** - **检查其他函数**:若出现类似警告(如`strerror`、`inet_addr`等),同样需要检查并添加对应头文件。 - **编译选项**:建议启用严格编译选项(如`-Wall -Werror`)将警告视为错误,确保代码符合现代C标准。 ### 示例代码修复 修复前(触发警告): ```c // 缺少 #include <stdio.h> int main() { FILE *fp = fopen("missing.txt", "r"); if (!fp) { perror("文件打开失败"); // 隐式声明警告 return 1; } fclose(fp); return 0; } ``` 修复后: ```c #include <stdio.h> // 包含perror的声明 int main() { FILE *fp = fopen("missing.txt", "r"); if (!fp) { perror("文件打开失败"); // 正确声明,无警告 return 1; } fclose(fp); return 0; } ``` ### 相关问题 §§相关问题§§ 1. 如何解决 `implicit declaration of function ‘strerror’` 警告? 2. 为什么隐式函数声明在现代C语言中被认为是安全的? 3. 如何配置GCC编译器将特定警告视为错误? </think>### 解决 `warning: implicit declaration of function ‘perror’` 警告 #### 1. **问题原因分析** 这个警告表示编译器在调用 `perror()` 函数时**未找到其声明**,主要原因包括: - **缺少头文件**:`perror()` 是标准I/O函数,需要包含 `<stdio.h>` 头文件 - **函数声明缺失**:编译器遇到未声明的函数时会隐式创建声明(假设返回 `int` 类型),可能导致类型安全问题 - **作用域问题**:函数声明位置正确(如在调用之后声明) - **拼写错误**:极少数情况下可能是函数名拼写错误 该警告属于 `-Wimplicit-function-declaration` 类别,高安全级别项目建议视为错误处理[^1][^3]。 #### 2. **解决方案** ✅ **添加标准头文件(推荐方案)** 在调用 `perror()` 的源文件顶部添加: ```c #include <stdio.h> // 包含 perror() 的声明 ``` 这是最规范且安全的解决方式,因为: - `perror()` 在C标准库中定义于 `<stdio.h>` - 包含头文件可确保函数签名正确(返回 `void` 类型) 🔧 **其他情况处理** 1. **若已包含 `<stdio.h>` 仍报错**: - 检查头文件包含顺序(确保在使用前包含) - 验证函数名拼写:`perror` ≠ `perr_or` - 清理编译缓存:`make clean && make` 2. **需要 GNU 扩展函数时**: 若使用特殊函数(如 `strptime`),可能需要定义宏: ```c #define _GNU_SOURCE // 启用GNU扩展 #include <stdio.h> ``` 编译时添加标志:`gcc -D_GNU_SOURCE file.c`[^5] #### 3. **验证修复** 重新编译后观察警告是否消失: ```bash gcc -Wall -Wextra your_program.c -o output ``` 使用 `-Werror` 可将其升级为错误(推荐生产环境使用): ```bash gcc -Werror=implicit-function-declaration your_program.c ``` #### 4. **技术原理** - **隐式声明风险**:当编译器遇到未声明函数时,会默认创建签名 `int func()`,若实际返回非 `int` 类型(如 `perror` 返回 `void`),可能导致未定义行为 - **编译流程**: ```mermaid graph LR A[预处理] --> B[词法分析] B --> C[语法分析] C --> D[遇到undeclared函数] D --> E[创建隐式声明 int func()] E --> F[发出警告] ``` > **最佳实践**:始终显式声明函数,避免依赖隐式声明机制[^1][^4] ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值