1)如果add1.c调用了add2.c中的add2函数,add2.h定义了add2.c中的函数原型,add1.c需包含#include "add2.h"的原因?
add1.c既然调用了函数add2,必须知道函数add2的函数原型,否则gcc -c add1.c,编译时只能使用implicit-function-declaration(隐式函数定义),对于调用者来说,当然有可能调用错(不知道函数参数、返回值)
2)add2.c需包含#include "add2.h"的原因?
add2.h向外界提供了add2.c中的函数接口,在add2.c中包含add2.h,可以有效避免add2.c向外界暴露的接口与add1.c中的调用的函数定义不同
3)add56.c中同时定义了add5、add6,同时add5函数调用了add6函数,如果不包含#include "add56.h",则add6函数定义需要写在add5函数前面,如果包含了#include "add56.h",就不需要考虑这些问题了
4)add2.c可以编译为库,供调用者使用,add2.c可以理解工具库、linux系统常用库的前身,add2.h可以理解为包含接口定义的头文件
5)提前给出函数声明表明当前文件中可能没有函数的实现,但预期在其他地方有实现。这样做允许源文件、库文件之前的相互依赖和调用,提高代码的模块性和重用性。
6)如果在链接时没有找到函数的定义,链接器会报未解析的外部符号之类的错误;如果有多个相同的函数定义,通过会报重复的符号的错误。这些错误都指出了程序结构上的问题,需要程序员解决。
7)在现代编程实践中,头文件通常用于包含函数声明,以及类型定义和宏定义等。通过在多个源文件中包含相同的头文件,可以确保函数声明一致性,避免因声明不一致导致的编译错误。
总之,在调用函数之前需要先有一份函数定义。
以下是AIGC工具给出的回答:
1)避免接口不一致的问题:如果add2.c中的函数定义与头文件中的声明不一致,例如参数个数、类型或者返回类型不同,编译器会报告函数原型不匹配的错误。通过包含头文件,可以确保函数的声明和定义保持一致,避免了这类问题的发生。
2)可维护性:当需要修改函数接口时,只需修改头文件中的函数声明,而不需要修改所有调用改函数的地方。这种做法使得代码更易于维护,同时也减少了因接口变更而导致的错误。
笔者注:其实笔者对这个观点有疑问,函数接口修改之后,所有调用函数的地方也需要修改吧,这里姑且理解为使用#include "add2.h"与直接在add1.c中进行被调用函数声明的优缺点讨论,前者相比于后者的优点是在函数接口发生后,需要改的地方更少
3)模块化设计:头文件的存在使得代码模块化,函数的接口与实现分离,有助于代码的组织和管理
笔者注:函数的接口与实现分离,可以提供更好的可复用性