第四周作业
一.学习并掌握可执行程序的编译、组装过程。
1.用 gcc 生成 .a 静态库和 .so 动态库
1.1编辑生成例子程序 hello.h、hello.c 和 main.c
dxc@dxc-virtual-machine:~$ mkdir test1 //创建test1目录
dxc@dxc-virtual-machine:~$ cd test1 //进入test1目录,创建文件将处于该文件夹内
dxc@dxc-virtual-machine:~/test1$ touch hello.h //依次创建头文件、源程序、主程序
dxc@dxc-virtual-machine:~/test1$ touch hello.c
dxc@dxc-virtual-machine:~/test1$ touch main.c
1.2编译文件
dxc@dxc-virtual-machine:~/test1$ gedit hello.h //进入头文件编辑界面并输入代码
#ifndef HELLO_H
#define HELLO_H
void hello(const char *name);
#endif //HELLO_H
dxc@dxc-virtual-machine:~/test1$ gedit hello.c //进入源程序编辑界面并输入代码
#include <stdio.h>
void hello(const char *name)
{
printf("Hello %s!\n", name);
}
dxc@dxc-virtual-machine:~/test1$ gedit main.c //进入主程序编辑界面并输入代码
#include "hello.h"
int main()
{
hello("everyone");
return 0;
}
1.3将 hello.c 编译成.o 文件
dxc@dxc-virtual-machine:~/test1$ gcc -c hello.c //生成 hello.o 文件
dxc@dxc-virtual-machine:~/test1$ ls //运行 ls 命令查看是否生存了 hello.o 文件
如图已生成
1.4创建静态库
dxc@dxc-virtual-machine:~/test$ ar -crv libmyhello.a hello.o //创建静态库文件libmyhello.a
dxc@dxc-virtual-machine:~/test$ ls //查看结果
如图已将创建完成
1.5在程序中使用静态库
方法一:
dxc@dxc-virtual-machine:~/test1$ gcc -o hello main.c -L. -lmyhello
方法二:
dxc@dxc-virtual-machine:~/test1$ gcc main.c libmyhello.a -o hello
方法三:
dxc@dxc-virtual-machine:~/test$ gcc -c main.c //生成 main.o
dxc@dxc-virtual-machine:~/test$ gcc -o hello main.o libmyhello.a //生成可执行文件
dxc@dxc-virtual-machine:~/test$ ./hello
1.6创建动态库文件
dxc@dxc-virtual-machine:~/test1$ gcc -shared -fPIC -o libmyhello.so hello.o //创建动态库文件 libmyhello.so
dxc@dxc-virtual-machine:~/test1$ ls //查看动态库文件是否生成
hello hello.c hello.h hello.o libmyhello.so main.c main.o
1.7使用动态库
dxc@dxc-virtual-machine~/test1$ gcc -o hello main.c -L. -lmyhello //生成目标文件
dxc@dxc-virtual-machine:~/test1$ ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory
dxc@dxc-virtual-machine:~/test1$ sudo mv libmyhello.so /usr/lib //sudo可以获得管理权限移动文件
dxc@dxc-virtual-machine:~/test1$ ./hello
Hello everyone!
二、静态库.a 与.so 库文件的生成与使用
1.1创建新目录及四个文件
1.2输入代码
编译a1.c文件
#include <stdio.h>
void print1(int arg){
printf("A1 print arg:%d\n",arg);
}
编译a2.c文件
#include <stdio.h>
void print2(char *arg){
printf("A2 printf arg:%s\n", arg);
}
编译a.h文件
#ifndef A_H
#define A_H
void print1(int);
void print2(char *);
#endif
编译test.c文件
#include <stdlib.h>
#include "A.h"
int main(){
print1(1);
print2("test");
exit(0);
}
2.3静态库.a 文件的生成与使用
2.3.1生成目标文件(xxx.o)
2.3.2生成静态库.a 文件
3.改编第一次作业的程序代码创建静态库和动态库并生成可执行文件程序,记录文件的大小,并与之前做对比
3.1代码修改
man1代码:
#include<stdio.h>
#include"sub1.c"
#include"sub2.c"
void main()
{
int a=4,b=2;
float c;
float d;
c=x2x(a,b);
d=x2y(a,b);
printf("%f\n",c);
printf("%f\n",d);
}
编译生成sub2.c文件
#include<stdio.h>
float x2y (int a,int b)
{
float d;
d=a*b;
return d;
}
3.2编译生成.o文件
dxc@dxc-virtual-machine:~/testttt$ gcc -c sub2.c
dxc@dxc-virtual-machine:~/testttt$ ls
3.3由.o文件创建静态库
dxc@dxc-virtual-machine:~/testttt$ ar -crv libmain1.a sub1.o sub2.o //创建静态库文件
dxc@dxc-virtual-machine:~/testttt$ ls //查看是否生成静态库文件
3.4使用静态库
dxc@dxc-virtual-machine:~/testttt$ gcc main1.c libmain1.a -o main2 //创建可执行文件main2
运行main2,查看图片大小
3.5由.o 文件创建动态库文件
dxc@dxc-virtual-machine:~/testttt$ gcc -shared -fPIC -o libmain1.so sub1.o sub2.o //生成动态库文件libmain1.so
dxc@dxc-virtual-machine:~/testttt$ ls //查看是否生成动态库文件
3.6使用动态库
dxc@dxc-virtual-machine:~/testttt$ gcc -o main3 main1.c -L. -lmain1 //生成可执行程序文件main3
dxc@dxc-virtual-machine:~/testttt$ ./main3 //运行main3
运行main3,查看图片大小
可以看到两个文件的大小,静态库文件比动态库文件大了64字节
二.请说明gcc编译工具集中各软件的用途,了解EFF文件格式
1.简介
GCC 是编译工具,它的意思是 GNU C Compiler 。经过了这么多年的发展,GCC 已经不仅仅能支持 C 语言;它现在还支持 Ada 语言、C++ 语言、Java 语言、Objective C 语言、Pascal 语言、COBOL语言,以及支持函数式编程和逻辑编程的 Mercury 语言等等。而 GCC 也不再单只是 GNU C 语言编译器的意思了,而是变成了 GNU Compiler Collection 也即是 GNU 编译器家族的意思了。另一方面,说到 GCC 对于操作系统平台及硬件平台支持,概括起来就是一句话:无所不在。
ELF文件格式:
ELF 文件的段
位于 ELF Header 和 Section Header Table 之间的都 是段(Section)。一个典型的 ELF 文件包含下面几个段:
text:已编译程序的指令代码段。
rodata:ro 代表 read only,即只读数据(譬如常数 const)。
data:已初始化的 C 程序全局变量和静态局部变量。
bss:未初始化的 C 程序全局变量和静态局部变量。
debug:调试符号表,调试器用此段的信息帮助调试。
反汇编 ELF
由于 ELF 文件无法被当做普通文本文件打开,如果希望直接查看一个 ELF 文件包 含的指令和数据,需要使用反汇编的方法。
例如对一个hello.c文件进行反汇编:
①使用 objdump -D 对其进行反汇编:
objdump -D hello
②使用 objdump -S 将其反汇编并且将其 C 语言源代码混合显示出来:
gcc -o hello -g hello.c
objdump -S hello
2.编译
gcc编译如下代码
一键编译完成
发现生成了目标文件test,实质上,上述编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编 (Assembly)和连接(Linking)。
3.预处理
输入以下代码:
gcc -E test.c -o test.i
应用vi test.i查看
4.编译
预处理之后,可直接对生成的test.i文件编译,生成汇编代码:
gcc -S test.i -o test.s
5.汇编
生成的汇编代码文件test.s,gas汇编器负责将其编译为目标文件,如下:
gcc -c test.s -o test.o
6.连接
gcc连接器是gas提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。
将上面生成的17.o文件与C标准输入输出库进行连接,最终生成程序17(因为我当时test文件没有指定所以后面改为17名称的文件)
gcc 17.o -o 17
最后输入./17得到结果
3.库文件连接
开发软件时,完全不使用第三方函数库的情况是比较少见的,通常来讲都需要借助许多函数库的支持才能够完成相应的功能。从程序员的角度看,函数库实际上就是一些头文件(.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。
3.1编译成可执行文件
首先我们要进行编译test.c为目标文件,这个时候需要执行
gcc –c –I /usr/dev/mysql/include test.c –o test.o
3.2链接
最后我们把所有目标文件链接成可执行文件:
gcc –L /usr/dev/mysql/lib –lmysqlclient test.o –o test
Linux下的库文件分为两大类分别是动态链接库(通常以.so结尾)和静态链接库(通常以.a结尾),二者的区别仅在于程序执行时所需的代码是在运行时动态加载的,还是在编译时静态加载的。
3.3强制链接时使用静态链接库
默认情况下, GCC在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库,如果需要的话可以在编译时加上-static选项,强制使用静态链接库。
在/usr/dev/mysql/lib目录下有链接时所需要的库文件libmysqlclient.so和libmysqlclient.a,为了让GCC在链接时只用到静态链接库,可以使用下面的命令:
gcc –L /usr/dev/mysql/lib –static –lmysqlclient test.o –o test
静态库链接时搜索路径顺序:
- ld会去找GCC命令中的参数-L
- 再找gcc的环境变量LIBRARY_PATH
- 再找内定目录 /lib /usr/lib /usr/local/lib
- 这是当初compile gcc时写在程序内的动态链接时、执行时搜索路径顺序:
- 编译目标代码时指定的动态库搜索路径
- 环境变量LD_LIBRARY_PATH指定的动态库搜索路径
- 配置文件/etc/ld.so.conf中指定的动态库搜索路径
- 默认的动态库搜索路径/lib
- 默认的动态库搜索路径/usr/lib
有关环境变量:
LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径
LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径
三.opencv练习
1.安装opencv
在官网上下载source版本的opencv然后解压安装
安装参考链接: https://blog.csdn.net/ssj925319/article/details/109231145(https://www.csdn.net/.
2.图片示例
首先创建一个代码存放文件夹 code ,然后进入文件夹中。
touch code
cd code
创建一个 test1.cpp 文件。
gedit test1.cpp
将下面的代码复制粘贴进去。
test1.cpp:
#include <opencv2/highgui.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
CvPoint center;
double scale = -3;
IplImage* image = cvLoadImage("lena.jpg");
argc == 2? cvLoadImage(argv[1]) : 0;
cvShowImage("Image", image);
if (!image) return -1; center = cvPoint(image->width / 2, image->height / 2);
for (int i = 0;i<image->height;i++)
for (int j = 0;j<image->width;j++) {
double dx = (double)(j - center.x) / center.x;
double dy = (double)(i - center.y) / center.y;
double weight = exp((dx*dx + dy*dy)*scale);
uchar* ptr = &CV_IMAGE_ELEM(image, uchar, i, j * 3);
ptr[0] = cvRound(ptr[0] * weight);
ptr[1] = cvRound(ptr[1] * weight);
ptr[2] = cvRound(ptr[2] * weight);
}
Mat src;Mat dst;
src = cvarrToMat(image);
cv::imwrite("test.png", src);
cvNamedWindow("test",1); imshow("test", src);
cvWaitKey();
return 0;
}
这里直接执行编译
g++ test1.cpp -o test1 `pkg-config --cflags --libs opencv`
./test1
3.摄像头示例
3.1虚拟机获取摄像头权限
使用快捷键 Win + R ,输入 services.msc ,并回车。
设置虚拟机
摄像头可以看到已经连接好了
3.2播放视频
创建一个 test2.cpp 文件。
gedit test2.cpp
在test2文件中输入以下代码:
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
//从摄像头读取视频
VideoCapture capture("man.mp4");
//循环显示每一帧
while(1){
Mat frame;//定义一个Mat变量,用于存储每一帧的图像
capture >> frame;//读取当前帧
if(frame.empty())//播放完毕,退出
break;
imshow("读取视频帧",frame);//显示当前帧
waitKey(30);//掩饰30ms
}
system("pause");
return 0;
}
然后进行编译
g++ test2.cpp -o test2 `pkg-config --cflags --libs opencv`
因为这里我没有下载视频所以无法播放
3.3录制视频
创建一个 test3.cpp 。
gedit test3.cpp
在文件中输入以下代码:
/*********************************************************************
打开电脑摄像头,空格控制视频录制,ESC退出并保存视频RecordVideo.avi
*********************************************************************/
#include<iostream>
#include <opencv2/opencv.hpp>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std;
int main()
{
//打开电脑摄像头
VideoCapture cap(0);
if (!cap.isOpened())
{
cout << "error" << endl;
waitKey(0);
return 0;
}
//获得cap的分辨率
int w = static_cast<int>(cap.get(CV_CAP_PROP_FRAME_WIDTH));
int h = static_cast<int>(cap.get(CV_CAP_PROP_FRAME_HEIGHT));
Size videoSize(w, h);
VideoWriter writer("RecordVideo.avi", CV_FOURCC('M', 'J', 'P', 'G'), 25, videoSize);
Mat frame;
int key;//记录键盘按键
char startOrStop = 1;//0 开始录制视频; 1 结束录制视频
char flag = 0;//正在录制标志 0-不在录制; 1-正在录制
while (1)
{
cap >> frame;
key = waitKey(100);
if (key == 32)//按下空格开始录制、暂停录制 可以来回切换
{
startOrStop = 1 - startOrStop;
if (startOrStop == 0)
{
flag = 1;
}
}
if (key == 27)//按下ESC退出整个程序,保存视频文件到磁盘
{
break;
}
if (startOrStop == 0 && flag==1)
{
writer << frame;
cout << "recording" << endl;
}
else if (startOrStop == 1)
{
flag = 0;
cout << "end recording" << endl;
}
imshow("picture", frame);
}
cap.release();
writer.release();
destroyAllWindows();
return 0;
}
编译执行程序
g++ test3.cpp -o test3 `pkg-config --cflags --libs opencv`
./test3