头文件和源文件的连接

文章转载于http://blog.163.com/yui_program/blog/static/18415541520115177852896/

一、源文件如何根据#include来关联头文件



1,系统自带的头文件用尖括号括起来,这样编译器会在系统文件目录下查找。


2,用户自定义的文件用双引号括起来,编译器首先会在用户目录下查找,然后在到C++安装目录(比如VC中可以指定和修改库文件查找路径,Unix和Linux中可以通过环境变量来设定)中查找,最后在系统文件中查找。


#include “"xxx.h”(我一直以为””和<>没什么区别,但是tinyxml.h是非系统下的都文件,所以要用””)


 


二、头文件如何来关联源文件


这个问题实际上是说,已知头文件“a.h”声明了一系列函数,“b.cpp”中实现了这些函数,那么如果我想在“c.cpp”中使用“a.h”中声明的这些在“b.cpp”中实现的函数,通常都是在“c.cpp”中使用#include “a.h”,那么c.cpp是怎样找到b.cpp中的实现呢?


其实.cpp和.h文件名称没有任何直接关系,很多编译器都可以接受其他扩展名。比如偶现在看到偶们公司的源代码,.cpp文件由.cc文件替代了。


在Turbo C中,采用命令行方式进行编译,命令行参数为文件的名称,默认的是.cpp和.h,但是也可以自定义为.xxx等等。


谭浩强老师的《C程序设计》一书中提到,编译器预处理时,要对#include命令进行“文件包含处理”:将file2.c的全部内容复制到#include “file2.c”处。这也正说明了,为什么很多编译器并不care到底这个文件的后缀名是什么----因为#include预处理就是完成了一个“复制并插入代码”的工作。


编译的时候,并不会去找b.cpp文件中的函数实现,只有在link的时候才进行这个工作。我们在b.cpp或c.cpp中用#include “a.h”实际上是引入相关声明,使得编译可以通过,程序并不关心实现是在哪里,是怎么实现的。源文件编译后成生了目标文件(.o或.obj文件),目标文件中,这些函数和变量就视作一个个符号。在link的时候,需要在makefile里面说明需要连接哪个.o或.obj文件(在这里是b.cpp生成的.o或.obj文件),此时,连接器会去这个.o或.obj文件中找在b.cpp中实现的函数,再把他们build到makefile中指定的那个可以执行文件中。


    在Unix下,甚至可以不在源文件中包括头文件,只需要在makefile中指名即可(不过这样大大降低了程序可读性,是个不好的习惯哦^_^)。在VC中,一帮情况下不需要自己写makefile,只需要将需要的文件都包括在project中,VC会自动帮你把makefile写好。


通常,编译器会在每个.o或.obj文件中都去找一下所需要的符号,而不是只在某个文件中找或者说找到一个就不找了。因此,如果在几个不同文件中实现了同一个函数,或者定义了同一个全局变量,链接的时候就会提示“redefined”。


 


内部连接和外部连接:


编译时每个文件会被编译成一个含有必要信息的源文件(又叫编译单元),然后编译单元会联结成一个和主文件同名的.o文件,.o文件把不同的编译单元中产生的符号联系起来,构成一个可执行文件。有两种截然不同的链接:内部的和外部的,将这些编译单元联系起来。


内部连接:对这个定义的访问被局限在当前编译单元,其他编译单元无法访问。


外部连接:可被其他单元访问,因此名称在整个执行文件中必须唯一。


类的定义(同时也是声明),enum,struct,都是内部连接,内联函数,静态的非类成员数据也是


typedef声明的类型也是内联结。


非内联成员函数(包括静态成员)有外部连接,非内联函数,非静态自由函数(非类的成员函数)也是外连接。


声明只对当前编译单元有用,他们不会影响到.o文件


.h文件,由于该文件会被其他.cpp文件包含,但由于声明只是对当前编译单元有效,是不会将符号引入.o文件,所以该文件不能含有任何外部连接的符号(数据成员和函数)的定义。一般情况下也不要包含内连接符号的定义。


综上所诉:


.h文件中能包含:


1。类成员数据的声明,但不能赋值


2.类静态数据成员的定义和赋值,但不建议,只是个声明就好。


3。类的成员函数的声明


4。非类成员函数的声明


5.常数的定义:如:const int a=5;


6.静态函数的定义


7.类的内联函数的定义


不能包含:


1. 所有非静态变量(不是类的数据成员)的声明


2。 默认命名空间声明不要放在头文件,using namespace std;等应放在.cpp中,在.h文件中使用std::string


http://blog.csdn.net/pjw100/archive/2010/01/18/5208879.aspx


 


----------------------------------------------------------------------------------------------------------------------


编译器不管头文件的,头文件只是用来被cpp文件包含的,被包含之后,它就成了那个cpp文件的一部分了,而编译器只编译.cpp文件,不会去单独编译一个头文件的。编译器这样做之后,针对每个编译过的cpp文件生成一个obj文件。然后连接器把所有这些obj文件连接成一个程序,或能是exe或dll(或做成静态的lib)。如果在连接的过程中,有些实体(比如变量或函数)找不到定义,则会报link错误。


 


我们通常把类的定义都放在头文件,而其成员函数以及静态成员变量的定义都放在一个主名相同,扩展名不同的cpp文件——这只是一种风格和传统,而不是C++语言规定的。


如果你不嫌乱,你当然可以把所有的代码都放到一个cpp文件中,整个工程就一个源文件。


你也可以把一个类的10个函数分别在10个不同的cpp文件中实现,甚至有4个实现在cpp文件中,另外3个是打包在以前做好的静态库(lib)中,还有3个内联定义在头文件中——都没问题。只要每个cpp文件都能顺利通过编译,而且连接器在连接时都能找到这些定义就OK了。
下面是一个简单的 C++ 封装 Modbus TCP 客户端和服务端的示例代码,包括头文件源文件: ```c++ // modbus_tcp_client.h #include <modbus/modbus.h> class ModbusTCPClient { public: ModbusTCPClient(const char* ip, int port); ~ModbusTCPClient(); void connect(); void disconnect(); void read_coil(int address, int count, uint8_t* dest); void write_coil(int address, int count, const uint8_t* data); private: modbus_t* ctx; const char* ip; int port; }; // modbus_tcp_client.cpp #include "modbus_tcp_client.h" #include <stdexcept> ModbusTCPClient::ModbusTCPClient(const char* ip, int port) : ip(ip), port(port) { ctx = modbus_new_tcp(ip, port); if (ctx == nullptr) { throw std::runtime_error("Failed to create modbus context"); } } ModbusTCPClient::~ModbusTCPClient() { modbus_free(ctx); } void ModbusTCPClient::connect() { if (modbus_connect(ctx) == -1) { throw std::runtime_error("Failed to connect to modbus server"); } } void ModbusTCPClient::disconnect() { modbus_close(ctx); } void ModbusTCPClient::read_coil(int address, int count, uint8_t* dest) { int rc = modbus_read_bits(ctx, address, count, dest); if (rc == -1) { throw std::runtime_error("Failed to read coil"); } } void ModbusTCPClient::write_coil(int address, int count, const uint8_t* data) { int rc = modbus_write_bits(ctx, address, count, data); if (rc == -1) { throw std::runtime_error("Failed to write coil"); } } // modbus_tcp_server.h #include <modbus/modbus.h> class ModbusTCPServer { public: ModbusTCPServer(int port); ~ModbusTCPServer(); void start(); void stop(); void add_coil(int address, bool value); private: modbus_t* ctx; int port; }; // modbus_tcp_server.cpp #include "modbus_tcp_server.h" #include <stdexcept> ModbusTCPServer::ModbusTCPServer(int port) : port(port) { ctx = modbus_new_tcp(NULL, port); if (ctx == NULL) { throw std::runtime_error("Failed to create modbus context"); } } ModbusTCPServer::~ModbusTCPServer() { modbus_free(ctx); } void ModbusTCPServer::start() { if (modbus_listen(ctx, 1) == -1) { throw std::runtime_error("Failed to start modbus server"); } } void ModbusTCPServer::stop() { modbus_close(ctx); } void ModbusTCPServer::add_coil(int address, bool value) { int rc = modbus_write_bit(ctx, address, value); if (rc == -1) { throw std::runtime_error("Failed to add coil"); } } ``` 下面是一个简单的主函数调用示例,用于测试这些类: ```c++ #include "modbus_tcp_client.h" #include "modbus_tcp_server.h" #include <iostream> int main() { try { // 创建 modbus TCP 服务器并启动 ModbusTCPServer server(502); server.start(); // 创建 modbus TCP 客户端并连接到服务器 ModbusTCPClient client("127.0.0.1", 502); client.connect(); // 在服务器上添加一个线圈 server.add_coil(0, true); // 从服务器上读取线圈的值 uint8_t value; client.read_coil(0, 1, &value); std::cout << "Coil value: " << (int)value << std::endl; // 在服务器上修改线圈的值 server.add_coil(0, false); // 向服务器上写入线圈的值 uint8_t data = 0; client.write_coil(0, 1, &data); // 断开客户端连接并停止服务器 client.disconnect(); server.stop(); } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; return 1; } return 0; } ``` 在上面的代码中,我们先创建了一个 modbus TCP 服务器并启动,然后创建了一个 modbus TCP 客户端并连接到服务器,然后在服务器上添加一个线圈,并从客户端上读取该线圈的值,并在服务器上修改该线圈的值,并向服务器上写入该线圈的值,最后断开客户端连接并停止服务器。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值