调用工控机CAN卡时出现:对‘canInitializeLibrary’未定义的引用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huoxingrenhdh/article/details/84757297

1.问题背景:

我在linux中想要使用kavaser can卡将相关报文发送出来,在官网下载驱动包linuxcan后,按照readme提示,成功安装驱动,并通过加载虚拟通道,sudo modprobe virtualcan,在canlib\examples\listchannels中显示增加的can通道,可参考can 卡配置博客,测试can卡驱动成功安装!

2.测试过程

当我想要调用can的基本测试程序时:

#include <canlib.h>
#include <stdio.h>
int main(void)
{
  canHandle hnd;
  canInitializeLibrary();
  hnd = canOpenChannel(0, canOPEN_EXCLUSIVE);
  if (hnd < 0) {
    char msg[64];
    canGetErrorText((canStatus)hnd, msg, sizeof(msg));
    fprintf(stderr, "canOpenChannel failed (%s)\n", msg);
    exit(1);
  }
  canSetBusParams(hnd, canBITRATE_500K, 0, 0, 0, 0, 0);
  canSetBusOutputControl(hnd, canDRIVER_NORMAL);
  canBusOn(hnd);
  char buffer[1];
  canWrite(hnd, 123, buffer, 6, 0);
  canWriteSync(hnd, 500);
  canBusOff(hnd);
  canClose(hnd);
  return 0;
}

3.问题报错

/tmp/ccQnezCp.o:在函数‘main’中:
basictest.cpp:(.text+0x18):对‘canInitializeLibrary’未定义的引用
basictest.cpp:(.text+0x27):对‘canOpenChannel’未定义的引用
basictest.cpp:(.text+0x46):对‘canGetErrorText’未定义的引用
basictest.cpp:(.text+0x9a):对‘canSetBusParams’未定义的引用
basictest.cpp:(.text+0xad):对‘canSetBusOutputControl’未定义的引用
basictest.cpp:(.text+0xb7):对‘canBusOn’未定义的引用
basictest.cpp:(.text+0xd5):对‘canWrite’未定义的引用
basictest.cpp:(.text+0xe4):对‘canWriteSync’未定义的引用
basictest.cpp:(.text+0xee):对‘canBusOff’未定义的引用
basictest.cpp:(.text+0xf8):对‘canClose’未定义的引用
collect2: 错误:ld 返回 1

4.问题分析

主要是C/C++编译为obj文件的时候并不需要函数的具体实现,只要有函数的原型即可。但是在链接为可执行文件的时候就必须要具体的实现了。如果错误是未声明的引用,那就是找不到函数的原型。

5.解决方式

这几个报错的函数是通过加载动态链接库canlib来实现的,所以需要在编译的时候在最后添加-lcanlib,动态连接这个库,如:

g++ basictest.cpp -o 2 -lcanlib

当然如果还是不行 那就需要专门指定动态链接库的具体路径,如:

g++ basictest.cpp -o 3 -L /home/hdh/can/linuxcan/canlib/ -lcanlib

6.深入探索问题

6.1 gcc和g++区别

先看结论:一般都用g++,他最牛逼!
两者的主要区别:
(1)对于.c文件gcc当做c语言处理,g++当做c++处理;
对于.cpp文件gcc和g++均当做c++处理;
(2)g++编译时实际上是调用gcc进行编译;
(3)gcc不能自动链接库文件,一般用g++来链接库文件,非要用gcc的话,一般使用gcc -lstdc++命令;
(4)extern “c”对于gcc和g++没有区别;
(5)实际使用时只需安装gcc和g++中的一个就行了,如果使用gcc,编译直接用gcc就行了,链接要加上-lstdc++参数;如果使用g++,编译时实际还是调用gcc,链接直接使用g++即可;

6.2 gcc与g++具体的编译流程

(1)预处理:编译处理宏定义等宏命令(eg:#define)——生成后缀为“.i”的文件   
(2)编译:将预处理后的文件转换成汇编语言——生成后缀为“.s”的文件  
(3)汇编:由汇编生成的文件翻译为二进制目标文件——生成后缀为“.o”的文件   
(4)连接:多个目标文件(二进制)结合库函数等综合成的能直接独立执行的执行文件——生成后缀为“.out”的文件

6.3 实际测试

#include <stdio.h>

int main()
{
    int a =10;
    printf("g++ test ok!");
}

1.预处理:生成.i的预处理文件,只激活预处理,这个不生成文件,需要把它重定向一个输出文件。

hdh@hdh-Aspire-4750:~/TCP/bianyitest$ ls
bianyi.cpp
hdh@hdh-Aspire-4750:~/TCP/bianyitest$ g++ -E bianyi.cpp -o bianyi.i
hdh@hdh-Aspire-4750:~/TCP/bianyitest$ ls
bianyi.cpp  bianyi.i

2.编译:生成.s的编译文件,只激活预处理和编译,把文件编译成汇编代码。

hdh@hdh-Aspire-4750:~/TCP/bianyitest$ g++ -S bianyi.i -o bianyi.s
hdh@hdh-Aspire-4750:~/TCP/bianyitest$ ls
bianyi.cpp  bianyi.i  bianyi.s

3.汇编:生成.o的汇编文件,只激活预处理、编译和汇编,把程序做成obj文件。

hdh@hdh-Aspire-4750:~/TCP/bianyitest$ g++ -c bianyi.s -o bianyi.o
hdh@hdh-Aspire-4750:~/TCP/bianyitest$ ls
bianyi.cpp  bianyi.i  bianyi.o  bianyi.s

4.链接:生成链接文件,激活预处理、编译、汇编和链接。

hdh@hdh-Aspire-4750:~/TCP/bianyitest$ g++ bianyi.o -o bianyi
hdh@hdh-Aspire-4750:~/TCP/bianyitest$ ls
bianyi  bianyi.cpp  bianyi.i  bianyi.o  bianyi.s

5.常用:

g++ -c bianyi.cpp  //生成.o文件
g++ bianyi.cpp -o bianyi  //生成可执行文件

6.4 库文件连接

开发软件时,完全不使用第三方函数库的情况是比较少见的,通常来讲都需要借助许多函数库的支持才能够完成相应的功能。从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(so、或lib、dll)的集合。。虽然Linux下的大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下;Windows所使用的库文件主要放在Visual Stido的目录下的include和lib,以及系统文件夹下。但也有的时候,我们要用的库不再这些目录下,所以GCC在编译时必须用自己的办法来查找所需要的头文件和库文件。

例如我们的程序test.c是在linux上使用c连接mysql,这个时候我们需要去mysql官网下载MySQL Connectors的C库,下载下来解压之后,有一个include文件夹,里面包含mysql connectors的头文件,还有一个lib文件夹,里面包含二进制so文件libmysqlclient.so

其中inclulde文件夹的路径是/usr/dev/mysql/include,lib文件夹是/usr/dev/mysql/lib

6.4.1 编译成可执行文件

首先我们要进行编译test.c为目标文件,这个时候需要执行

gcc –c –I /usr/dev/mysql/include test.c –o test.o

6.4.2 链接

最后我们把所有目标文件链接成可执行文件:

gcc –L /usr/dev/mysql/lib –lmysqlclient test.o –o test

Linux下的库文件分为两大类分别是动态链接库(通常以.so结尾)和静态链接库(通常以.a结尾),二者的区别仅在于程序执行时所需的代码是在运行时动态加载的,还是在编译时静态加载的。

6.4.3 强制链接时使用静态链接库

默认情况下, GCC在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库,如果需要的话可以在编译时加上-static选项,强制使用静态链接库。

在/usr/dev/mysql/lib目录下有链接时所需要的库文件libmysqlclient.so和libmysqlclient.a,为了让GCC在链接时只用到静态链接库,可以使用下面的命令:

gcc –L /usr/dev/mysql/lib –static –lmysqlclient test.o –o test

静态库链接时搜索路径顺序:

  1. ld会去找GCC命令中的参数-L
  2. 再找gcc的环境变量LIBRARY_PATH
  3. 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的

动态链接时、执行时搜索路径顺序:

  1. 编译目标代码时指定的动态库搜索路径
  2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径
  3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径
  4. 默认的动态库搜索路径/lib
  5. 默认的动态库搜索路径/usr/lib

有关环境变量:
LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径
LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径

7.第三方库使用方法

7.1 最基础

g++ -c -I /home/hdh/can/linuxcan/canlib bigdata.cpp -obigdata.o
g++ bigdata.o -o 1 -L /home/hdh/can/linuxcan/canlib/ -lcanlib

7.2 最简单

g++ bigdata.cpp -o 1 -lcanlib

7.3 最根本

8.参考优秀博客列表:

https://blog.csdn.net/na_beginning/article/details/53236968
https://blog.csdn.net/u013457167/article/details/80222557
https://blog.csdn.net/txl199106/article/details/39398439
https://blog.csdn.net/killwho/article/details/53785910
http://www.linuxdiyf.com/linux/16492.html

猜你喜欢

转载自blog.csdn.net/huoxingrenhdh/article/details/84757297