目录
1)阅读、理解和学习材料“用gcc生成静态库和动态库.pdf”和“静态库.a与.so库文件的生成与使用.pdf”,请在Linux系统(Ubuntu)下如实仿做一遍。
2)在第一次作业的程序代码基础进行改编,除了x2x函数之外,再扩展写一个x2y函数(功能自定),main函数代码将调用x2x和x2y ;将这3个函数分别写成单独的3个 .c文件,并用gcc分别编译为3个.o 目标文件;将x2x、x2y目标文件用 ar工具生成1个 .a 静态库文件, 然后用 gcc将 main函数的目标文件与此静态库文件进行链接,生成最终的可执行程序,记录文件的大小。
二. Gcc不是一个人在战斗。请说明gcc编译工具集中各软件的用途,了解EFF文件格式。学习任务如下:
阅读、理解和学习材料“Linux GCC常用命令.pdf”和“GCC编译器背后的故事.pdf”,如实仿做一遍
三. 每一个程序背后都站着一堆优秀的代码库。通过学习opencv图像库编程,了解如何借助第三方库函数完成一个综合程序设计。“学了opencv,妈妈再不担忧你不会图像编程啦!”。
(1) 第 1 步:编辑生成例子程序 hello.h、hello.c 和 main.c。 先创建一个作业目录,保存本次练习的文件。 #mkdir test1 #cd test1
然后用 vim文本编辑器编辑生成所需要的 3 个文件
hello.h
#ifndef HELLO_H
#define HELLO_H
void hello(const char *name);
#endif //HELLO_H
hello.c
#include
void hello(const char *name)
{
printf("Hello %s!\n", name);
}
main.c
#include "hello.h"
int main()
{
hello("everyone");
return 0;
}
(2) 第 2 步:将 hello.c 编译成.o 文件
无论静态库,还是动态库,都是由.o 文件创建的。因此,我们必须将源程序 hello.c 通过 g cc 先编译成.o 文件。在系统提示符下键入以下命令得到 hello.o 文件。 # gcc -c hello.c # 我们运行 ls 命令看看是否生存了 hello.o 文件。 # ls hello.c hello.h hello.o main.c # 在 ls 命令结果中,我们看到了 hello.o 文件,本步操作完成。 下面我们先来看看如何创建静态库,以及使用它。
(3) 第 3 步:由.o 文件创建静态库。
静态库文件名的命名规范是以 lib 为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将
创建的静态库名为 myhello,则静态库文件名就是 libmyhello.a。在创建和使用静态库时,
需要注意这点。创建静态库用 ar 命令。在系统提示符下键入以下命令将创建静态库文件
libmyhello.a。
# ar -crv libmyhello.a hello.o
#
我们同样运行 ls 命令查看结果:
# ls
hello.c hello.h hello.o libmyhello.a main.c
#
ls 命令结果中有 libmyhello.a。
(4) 第 4 步:在程序中使用静态库。 静态库制作完了,如何使用它内部的函数呢?只需要在使用到这些公用函数的源程序中包 含这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明静态库名,gcc 将会从 静态库中将公用函数连接到目标文件中。注意,gcc 会在静态库名前加上前缀 lib,然后追 加扩展名.a 得到的静态库文件名来查找静态库文件。 在程序 3:main.c 中,我们包含了静态库的头文件 hello.h,然后在主程序 main 中直接调用 公用函数 hello。下面先生成目标程序 hello,然后运行 hello 程序看看结果如何。
先生成 main.o: gcc -c main.c 再生成可执行文件: gcc -o hello main.o libmyhello.a 动态库连接时也可以这样做。 # ./hello Hello everyone!
(5) 第 5 步:由.o 文件创建动态库文件。 动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀 lib,但其 文件扩展名为.so。例如:我们将创建的动态库名为 myhello,则动态库文件名就是 libmyh ello.so。用 gcc 来创建动态库。 在系统提示符下键入以下命令得到动态库文件 libmyhello.so。 # gcc -shared -fPIC -o libmyhello.so hello.o (-o 不可少)
(6) 第 6 步:在程序中使用动态库; 在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含 这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明动态库名进行编译。我 们先运行 gcc 命令生成目标文件,再运行它看看结果。 # gcc -o hello main.c -L. -lmyhello
nano生成例子程序y1.c、y2.c、y.h、test.c,此处存于文件夹jingmove2(随便取文件名)
分别写入:
y1.c#include
void print1(int arg){
printf("Y1 print arg:%d\n",arg);
}
y2.c#include
void print2(char *arg){
printf("Y2 printf arg:%s\n", arg);
}
y.h#ifndef Y_H
#define Y_H
void print1(int);
void print2(char *);
#endif
test.c#include
#include "Y.h"
int main(){
print1(1);
print2("test");
exit(0);
}
2. 静态库.a文件的生成和使用
依次输入图示命令即可成功。
3. 共享库.so文件的生成和使用
依次输入图示命令:
这时出现错误,输入ldd test命令查看链接情况:
找不到对应的.so文件,移动.so文件位置:
成功!
4. 静动态库生成文件大小的比较
GCC: GCC(GNU C Compiler)是编译工具。本文所要介绍的将 C/C++语言编写的程序 转换成为处理器能够执行的二进制代码的过程即由编译器完成。 Binutils: 一组二进制程序处理工具,包括:addr2line、ar、objcopy、objdump、as、ld、 ldd、readelf、 size 等。这 一组工具 是开发和 调试不可 缺少的工具 ,分别简 介 如下: (1) addr2line:用 来将程序 地址转 换成其所 对应的程 序源文 件及所对 应的代 码 行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对 应的源代码位置。 (2) as:主要用于汇编,有关汇编的详细介绍请参见后文。 (3) ld:主要用于链接,有关链接的详细介绍请参见后文。 (4) ar:主要用于创建静态库。为了便于初学者理解,在此介绍动态库与静态库 的概念: 如果 要将 多个 .o 目标 文件 生成 一个 库文 件, 则存 在两 种类 型的 库, 一种 是 静态库,另一种是动态库。 在 windows 中 静态 库是 以 .lib 为 后缀 的文 件 ,共 享库 是以 .dll 为 后缀 的 文 件 。 在 linux 中 静 态 库 是 以 .a 为 后 缀 的 文 件 , 共 享 库 是 以 .so 为 后 缀 的文件。 静 态 库 和 动 态 库 的 不 同 点 在 于 代 码 被 载 入 的 时 刻 不 同 。 静 态 库 的 代 码 在 编 译 过 程 中 已 经 被 载 入 可 执 行 程 序 , 因 此 体 积 较 大 。 共 享 库 的 代 码 是 在 可 执 行 程 序 运 行 时 才 载 入 内 存 的 , 在 编 译 过 程 中 仅 简 单 的 引 用 , 因 此 代 码 体 积 较 小 。 在 Linux 系 统 中 , 可 以 用 ldd 命 令 查 看 一 个 可 执 行 程 序 依 赖 的 共 享 库。 如 果 一 个 系 统 中 存 在 多 个 需 要 同 时 运 行 的 程 序 且 这 些 程 序 之 间 存 在 共 享 库,那么采用动态库的形式将更节省内存。 (5) ldd:可以用于查看一个可执行程序依赖的共享库。 (6) objcopy:将一种对象文件翻译成另一种格式,譬如将.bin 转换成.elf、或 者将.elf 转换成.bin 等。 (7) objdump:主要的作用是反汇编。有关反汇编的详细介绍,请参见后文。 (8) readelf:显示有关 ELF 文件的信息,请参见后文了解更多信息。 (9) size:列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小 等,请参见后文了解使用 size 的具体使用实例。 C 运行库 C 语言标准主要由两部分组成:一部分描述 C 的语法,另一部分描述 C 标准库。 C 标准库定义了一组标准头文件,每个头文件中包含一些相关的函数、变量、类 型声明和宏定义,譬如常见的 printf 函数便是一个 C 标准库函数,其原型定义 在 stdio 头文件中。 C 语言标准仅仅定义了 C 标准库函数原型,并没有提供实现。因此,C 语言编译 器通常需要一个 C 运行时库(C Run Time Libray,CRT)的支持。C 运行时库又 常简称为 C 运行库。与 C 语言类似,C++也定义了自己的标准,同时提供相关支 持库,称为 C++运行时库。 (一)准备工作 由于 GCC 工具链主要是在 Linux 环境中进行使用,因此本文也将以 Linux 系统作 为工作环 境。为了能够 演示编译的整个 过程,先创建一 个工作目录 test0,然后 用文本编辑器生成一个 C 语言编写的简单 Hello.c 程序为示例,其源代码如下所 示:
#include
//此程序很简单,仅仅打印一个 Hello World 的字符串。
int main(void)
{
printf("Hello World! \n");
return 0;
}
1.预处理 预处理的过程主要包括以下过程: (1) 将所有的#define 删除,并且展开所有的宏定义,并且处理所有的条件预编 译指令,比如#if #ifdef #elif #else #endif 等。 (2) 处理#include 预编译指令,将被包含的文件插入到该预编译指令的位置。 (3) 删除所有注释“//”和“/* */”。 (4) 添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号。 (5) 保留所有的#pragma 编译器指令,后续编译过程需要使用它们。 使用 gcc 进行预处理的命令如下: $ gcc -E hello.c -o hello.i
2.编译 编译过程就是对预处理完的文件进行一系列的词法分析,语法分析,语义分析及 优化后生成相应的汇编代码。 使用 gcc 进行编译的命令如下: $ gcc -S hello.i -o hello.s // 将预处理生成的 hello.i 文件编译生成汇编程序 hello.s // GCC 的选项-S 使 GCC 在执行完编译后停止,生成汇编程序 上述命令生成的汇编程序 hello.s 的代码片段如下所示,其全部为汇编代码。 // hello.s 代码片段 main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $.LC0, %edi call puts movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc
1.安装
去官网下载opencv,在本教程中选用的时opencv3.4.2,其他版本的配置方法异曲同工。
下载链接Releases - OpenCV,选择sources版本
更新源
sudo apt update
安装更新包
sudo apt-get install build-essential cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev libjasper
sudo add-apt-repository "deb http://security.ubuntu.com/ubuntu xenial-security main"
sudo apt update
sudo apt-get install build-essential cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev libjasper
安装常用图像工具包
sudo apt-get install libjpeg8-dev libtiff5-dev libjasper-dev libpng12-dev
2.4 安装视频I/O包
sudo apt install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev
2.5 安装gtk2.0
sudo apt install libgtk2.0-dev
2.6 优化函数包
sudo apt-get install libatlas-base-dev gfortran
3.解压下载下来的zip包,进入解压的文件夹中
unzip opencv-3.4.1.zip
cd opencv-3.4.2/
4.安装完cmake之后执行命令 ,创建编译文件夹
mkdir build
cd build/
5.cmake一下
sudo cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local -D PYTHON_DEFAULT_EXECUTABLE=$(which python3) -D WITH_TBB=ON -D WITH_EIGEN=ON ..
6.编译并执行
sudo make
sudo make install
7.sudo make install 执行完毕后OpenCV编译过程就结束了,接下来就需要配置一些OpenCV的编译环境首先将OpenCV的库添加到路径,从而可以让系统找到
sudo gedit /etc/ld.so.conf.d/opencv.conf
执行此命令后打开的可能是一个空白的文件,不用管,只需要在文件末尾添加
/usr/local/lib
执行如下命令使得刚才的配置路径生效
sudo ldconfig
8.配置bash
sudo gedit /etc/bash.bashrc
1
在最末尾添加
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig
export PKG_CONFIG_PATH
source ~/.bashrc
9.12.至此所有的配置都已经完成
下面用一个小程序测试一下
找到
cd到opencv-3.4.2/samples/cpp/example_cmake目录下
我们可以看到这个目录里官方已经给出了一个cmake的example我们可以拿来测试下
按顺序执行
cmake .
make
./opencv_example