C时代的时候编译器比较简单,是固定的编译和链接两个过程,编译一次只处理一个文件,进行预处理之后,头文件会插入到这一个文件里,不同源代码文件的处理时独立的,这样如果头文件里面定义了一个函数的实现,编译的时候所有引用这个头文件的源码文件,生成的obj里都会有这个符号。而链接是通用的链接程序,从汇编时代就用的工具,没有什么高级功能,同一个符号链接时出现两次是会报错的。
但是,我们又说了,每个文件的编译是独立的,所以如果实现不在当前源文件里面,调用的时候编译器就不知道这个函数的类型和签名,没法生成调用代码,所以必须在调用之前先声明一遍。如果不把声明写在头文件里面,就必须在每个用到这个函数的源文件里都声明一遍,很不方便,所以综合之后的解决方案就是实现写源码文件里面,声明写头文件里面。
C++只是沿用了这个设计而已,实际上现在的C++编译器有处理符号重复定义的能力了(例如inline函数可以定义在头文件里面,但不必真的inline,也不需要像static函数一样每个文件生成一个符号),但是传统也是很重要的。
你的另一个问题,C调用DLL明明就是直接引用一个头文件啊……如果启用了预编译指令,一般还可以直接在头文件中指定链接一个外部库;否则需要额外链接一个obj文件,里面负责加载DLL。如果用动态的方式,则需要自己调用相应的API去加载DLL,获取导出点之类。
因为编译出来的二进制码(比如.o,.obj,.lib,.dll)不包含自我描述的符号信息,要复用这种可执行码的话得另外的文件。C#和Java的可执行码自带元数据信息,但是这也意味着运行时的内存需求增加,毕竟这种自我描述的数据对最终用户来说是无用。当然在它们被发明的时候这些空间已经很便宜了,运行的时候浪费个几十K内存不是个事;但是C被发明的时候,64K的内存是四百多美元,给机器添加16或32KB内存的扩展槽,价格是三百美元(1980 Radio Shack Catalog Low-res page 171 of 176),一个程序经常几十个模块,运行的时候为每个模块去浪费几十K的内存是不可能的事情。
C++ 的Module包含元数据信息,不过提出来好几年了到现在还在讨论TS v1,不知道哪年才能进入标准……
本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度,google,bing,sogou 等
© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有