使用树莓派学习Linux系统编程的 --- 库编程(面试重点)
在之前的Linux系统编程中,学习了文件的打开;关闭;读写;进程;线程等概念....
本节补充“Linux库概念 & 相关编程”,这是一个面试的重点!
分文件编程
在之前的学习中,面对较大的项目比如 STM32的小车 或 香橙派实现的智能垃圾桶 ,都使用了分文件编程的思路。
其 实现的核心思想就是:将功能性函数的实现单独写在其他的地方,在main函数中调用那些封装好的功能性函数。
这样做的好处是:
- 分模块的编程思想:在实际工作中面对大型项目,可以让A完成串口开发;B完成网络开发,最后只需要他们提供h文件中的函数接口就可以在主函数中直接调用了,测试时发现哪部分有问题可以直接找负责的人,方便调试
- 代码可移植性更强:因为分文件编程了,串口,网络,语音可能都被封装好了,那么后续如果其他项目需要这些功能就可以直接调用封装好的接口了,最多只需要微调
- main函数更加精简:由于把功能性函数的实现步骤都封装到其他文件了,main函数就可以专注于项目的整体调用逻辑,使得整个main看起来更加清晰,逻辑通畅
具体步骤
- 将功能性函数名和具体实现步骤写在一个.c文件中
- 创建一个同名的.h文件,包含所有可能会被调用的函数原型,去除函数体
- 在main中包含刚刚创建的.h文件
- 在main中调用被封装好的函数接口
- 使用gcc 编译所有相关的.c文件(封装函数的.c文件;main函数所在的.c文件)
- h文件的大概格式:
int add(int x, int y); int min(int x, int y); float div(int x, int y);
- main函数调用h文件的格式:
#include <stdio.h> #include "XXX.h"
Q:为什么同样是调用头文件,有时候使用' <> ',而有时候使用' "" '呢?
A:使用' <> '时,gcc在编译时会去“/usr/include/”或“/usr/local/include/”下找这个头文件;而使用' "" '时,gcc则会优先从代码运行的当前路径去找这个头文件!如果找不到,才会再去“/usr/include/”或“/usr/local/include/”下找这个头文件。
Linux的库
分文件编程的好处已经在刚刚说到,但是实际工作中会出现这种情况:程序员允许别人可以调用他封装好的功能性函数,但是他不希望别人可以看到他具体实现的函数体。在这种情况下,就要引入Linux的库的概念了!
库(程序函数库)是一种可执行的二进制形式、就是将源代码转化为二进制格式,相当于进行了加密,别人可以使用库,但是看不到库中的内容。
库是别人写好的现有的,可以复用的代码,现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。
程序函数库可分为3种类型:静态函数库(static libraries)、共享函数库(shared libraries)、动态加载函数库(dynamically loaded libraries):
- 静态函数库:在程序执行前就加入到目标程序中的库, 文件后缀是.a
- 共享函数库:在程序执行时动态(临时)由目标程序去调用,共享函数库=动态函数库=共享对象库(Linux), 文件后缀是.so
- 动态加载函数库:本质上和共享函数库是一个东西,“动态加载数据库”是windows中的叫法,文件后缀是.dll
因此,对于Linux系统来说可以简单的将库分为 动态库 和 静态库
静态库和动态库的比较
静态数据库(libXXX.a)
优点
- 运行快
- 发布程序无需提供静态库,因为已经在app中,移植方便
缺点
- 程序大
- 更新部署发布麻烦
动态数据库(libXXX.so)
优点
- 程序小
- 升级简单
- 不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享(动态)库的实例
缺点
运行相对慢
需要提供依赖的动态库
静态库的制作(不太常用了)
制作步骤
- 使用以下指令将.c文件生成.o文件
gcc a.c b.c -c
- 使用以下指令将.o文件打包成.a库文件
ar rcs 静态库的名字 原材料
例:ar rcs libXXX.a a.o b.o
这两步完成后,就生成了.a库文件,此时实现功能函数的.c文件和.o文件对于程序运行就不必要了,使得main函数可以调用这个库的条件就是有.h和.a文件,此时代码执行者可以调用库但却无法得知库中函数具体的实现步骤了。
库的使用
gcc XXXXX.c -L 库文件所在目录 -lXXXX -o XXX
//-L:将-L之后跟着的目录作为第一个寻找库文件的目录,寻找的顺序是:-L之后跟着的目录 -->/lib-->/usr/lib-->/usr/local/lib
//-l(小写L):指定库的名字(去掉lib和.a)
//-o:指定生成的最终应用程序的名字
小插曲:gcc编译时“-I(大写i)” 和“-L"的区别:
- -I(大写i):将-I之后跟着的目录作为第一个寻找头文件的目录,寻找的顺序是:-I之后跟着的目录-->/usr/include-->/usr/local/include
- -L:将-L之后跟着的目录作为第一个寻找库文件的目录,寻找的顺序是:-L之后跟着的目录 -->/lib-->/usr/lib-->/usr/local/lib
- 头文件和库文件的关系:库文件可以包含头文件,头文件不可以包含库文件,头文件可视,库文件不可视
由于之前提到过,静态库的优点之一是“发布程序无需提供静态库” ,所以编译完成后,就可以直接运行程序了,不需要任何后缀!
动态库的制作(更常用)
制作步骤
- 使用以下指令生成动态库:
gcc -shared -fpic xxx.c -o libxxx.so
//-shared用来生成动态库
//-fpic选项作用于编译阶段,在生成目标文件时就得使用该选项,以生成位置无关的代码
库的使用
编译的语句其实和静态库相同:
gcc XXXXX.c -L 库文件所在目录 -lXXXX -o XXX
//-L:将-L之后跟着的目录作为第一个寻找库文件的目录,寻找的顺序是:-L之后跟着的目录 -->/lib-->/usr/lib-->/usr/local/lib
//-l(小写L):指定库的名字(去掉lib和.a)
//-o:指定生成的最终应用程序的名字
注意!虽然编译的语句相同,但是回顾动态库的缺点“需要提供依赖的动态库” ,所以编译完成后不能像使用静态库那样直接运行,这是因为动态库是程序运行中临时调用的,解决办法是将动态库拷贝到/usr/lib/下:
sudo cp libXXXX.so /usr/lib/
然后,再直接运行程序就可以了!
将动态库复制到/usr/lib/或/lib/下是因为程序执行时动态库的默认搜索路径就是/lib和/usr/lib;那么如果可以指定动态库的搜索路径,就可以不需要将库复制了,这就是另一种方法:使用环境变量LD_LIBRARY_PATH指定动态库搜索路径
export LD_LIBRARY_PATH="动态库所在的绝对路径"
通过添加这个环境变量,也可以成功运行程序了,但是这样做有一个问题:这个环境变量是临时的,也就是说只有在当前窗口生效,如果此时通过SSH再连接一个窗口,又会找不到动态库了,解决办法是:写一个脚本start.sh:
export LD_LIBRARY_PATH="动态库所在的绝对路径"./可执行文件
然后给脚本一个可执行的权限:
chmod +x start.sh
其实这个脚本的作用就是在每次执行程序前设置一个临时的环境变量。
动态库的实操演示
首先在树莓派家目录下创建一个mjm_code文件夹,学习用的代码全放在这里面:
然后分别创建一个“test_main.c”和一个“test_func.c”来模拟main函数所在的C文件和封装功能函数的C文件:
test_main.c:
#include <stdio.h>
#include "test_func.h"int main()
{int a;int b;int ret;float ret1;printf("请输入第一个数\n");scanf("%d",&a);printf("请输入第二个数\n");scanf("%d",&b);printf("开始计算\n");ret = add(a,b);printf("相加为%d\n",ret);ret = min(a,b);printf("相减为%d\n",ret);ret = mul(a,b);printf("相乘为%d\n",ret);ret1 = div(a,b);printf("相除为%f\n",ret1);return 0;
}
test_func.c:
int add(int a,int b)
{return a+b;
}
int min(int a,int b)
{return a-b;
}
int mul(int a,int b)
{return a*b;
}
float div(int a,int b)
{return (float)a/b;
}
test_func.h:
int add(int a,int b);
int min(int a,int b);
int mul(int a,int b);
float div(int a,int b);
然后尝试编译运行:
没有报错,程序正常运行,到此为止就是一个分文件编程的典型例子。
接下来,尝试将test_func.c做成一个动态库:(库名我这里叫“scalc”)
gcc -shared -fpic test_func.c -o libscalc.so
然后进行编译:(我生成了一个叫“calc”的可执行程序)
gcc test_main.c -L . -lscalc -o calc
//-L后的“.”代表当前路径
然后将生成的.so文件复制到usr/lib/下,并删除当前路径下的.so文件:
1. sudo cp libscalc.so /usr/lib/
2. rm libscalc.so //可以不删,删了是为了证明复制到/usr/lib下之后当前路径的.so就没啥用了
最后尝试运行:
成功运行!此时程序的执行只依赖.h文件和.so文件了,执行者可以调用接口但并不能查看test_func.c中功能函数的具体实现,因为这个.c文件已经被制作成.so的库了。
相关文章:

使用树莓派学习Linux系统编程的 --- 库编程(面试重点)
在之前的Linux系统编程中,学习了文件的打开;关闭;读写;进程;线程等概念.... 本节补充“Linux库概念 & 相关编程”,这是一个面试的重点! 分文件编程 在之前的学习中,面对较大的…...

vs2017打开工程提示若要解决此问题,请使用以下选择启动 Visual Studio 安装程序: 用于 x86 和 x64 的 Visual C++ MFC
下载安装文件。 下载之后点击C项目,他会提示需要安装编译依赖。这个时候需要选择 用于 x86 和 x64 的 Visual C MFCWindows SDK 版本8.1 点击右下角的安装等待即可 error MSB8036: 找不到 Windows SDK 版本8.1。请安装所需的版本的 Windows SDK 或者在项目属性页…...
Redis学习笔记17:基于spring data redis及lua脚本批处理scan指令查询永久有效的key
Redis的KEYS和SCAN指令都可以用于在数据库中搜索匹配指定模式的键。然而,它们之间有一些关键的区别; KEYS指令会在整个数据库中阻塞地执行匹配操作,并返回匹配的键列表。如果数据库很大,或者匹配的键很多,将会对性能产…...

今天遇到Windows 10里安装的Ubuntu(WSL)的缺点
随着技术的发展,越来越多开发者转向使用 Windows Subsystem for Linux(WSL)在 Windows 10 上进行开发,也就是说不用虚拟机,不用准备多一台电脑,只需要在Windows 10/11 里安装 WSL 就能体验 Linux 系统。因此…...
hive sql多表练习
hive sql多表练习 准备原始数据集 学生表 student.csv 讲师表 teacher.csv 课程表 course.csv 分数表 score.csv 学生表 student.csv 001,彭于晏,1995-05-16,男 002,胡歌,1994-03-20,男 003,周杰伦,1995-04-30,男 004,刘德华,1998-08-28,男 005,唐国强,1993-09-10,男 006,陈道…...

论文速览 Arxiv 2023 | DMV3D: 单阶段3D生成方法
注1:本文系“最新论文速览”系列之一,致力于简洁清晰地介绍、解读最新的顶会/顶刊论文 论文速览 Arxiv 2023 | DMV3D: DENOISING MULTI-VIEW DIFFUSION USING 3D LARGE RECONSTRUCTION MODEL 使用3D大重建模型来去噪多视图扩散 论文原文:https://arxiv.org/pdf/2311.09217.pdf…...
访问限制符说明面向对象的封装性
1 问题 Java中4种“访问控制符”分别为private、default、protected、public,它们说明了面向对象的封装性,所以我们要利用它们尽可能的让访问权限降到最低,从而提高安全性。 private表示私有,只有自己类能访问,属性可以…...

python趣味编程-5分钟实现一个贪吃蛇游戏(含源码、步骤讲解)
Python 贪吃蛇游戏代码是用 Python 语言编写的。在这个贪吃蛇游戏中,Python 代码是增强您在创建和设计如何使用 Python 创建贪吃蛇游戏方面的技能和才能的方法。 Python Tkinter中的贪吃蛇游戏是一个简单干净的 GUI,可轻松玩游戏。游戏设计非常简单,用户不会觉得使用和理解…...

如何在虚拟机的Ubuntu22.04中设置静态IP地址
为了让Linux系统的IP地址在重新启动电脑之后IP地址不进行变更,所以将其IP地址设置为静态IP地址。 查看虚拟机中虚拟网络编辑器获取当前的子网IP端 修改文件/etc/netplan/00-installer-config.yaml文件,打开你会看到以下内容 # This is the network conf…...
代码随想录算法训练营第二十九天| 491 递增子序列 46 全排列
目录 491 递增子序列 46 全排列 491 递增子序列 在dfs中进行判断,如果path的长度大于1,则将其添加到res中。 本题nums中的元素的值处于-100与100之间,可以将元素映射0到199之间并且通过布尔数组st来记录此层中元素是否被使用过,…...

(动手学习深度学习)第13章 实战kaggle竞赛:CIFAR-10
导入相关库 import collections import math import os import shutil import pandas as pd import torch import torchvision from torch import nn from d2l import torch as d2l下载数据集 d2l.DATA_HUB[cifar10_tiny] (d2l.DATA_URL kaggle_cifar10_tiny.zip,2068874e4…...

Go 语言中的map和内存泄漏
map在内存中总是会增长;它不会收缩。因此,如果map导致了一些内存问题,你可以尝试不同的选项,比如强制 Go 重新创建map或使用指针。 在 Go 中使用map时,我们需要了解map增长和收缩的一些重要特性。让我们深入探讨这一点…...

前缀和(c++,超详细,含二维)
前缀和与差分 当给定一段整数序列a1,a2,a3,a4,a5…an; 每次让我们求一段区间的和,正常做法是for循环遍历区间起始点到结束点,进行求和计算,但是当询问次数很多并且区间很长的时候 比如,10^5 个询问和10^6区间长度,相…...
详解FreeRTOS:二值信号量和计数信号量(高级篇—2)
目录 1、二值信号量 1.1、二值信号量运行机制 1.2、创建二值信号量 1...

持续集成交付CICD:Jenkins通过API触发流水线
目录 一、理论 1.HTTP请求 2.调用接口的方法 3.HTTP常见错误码 二、实验 1.Jenkins通过API触发流水线 三、问题 1.如何拿到上一次jenkinsfile文件进行自动触发流水线 一、理论 1.HTTP请求 (1)概念 HTTP超文本传输协议,是确保服务器…...

【Python】12 GPflow安装
概述 GPflow 是一个基于TensorFlow 在 Python 中构建高斯过程模型的包。高斯过程是一种监督学习模型。 高斯过程的一些优点是: 不确定性是高斯过程的固有部分。高斯过程可以在不知道答案时告诉您。适用于小型数据集。如果您的数据有限,高斯过程可以从…...
Ubuntu源码编译gdal3.6.2
在华为云申请了一台Ubuntu v18的机器,乱七八糟的不要装。 apt install build-essential pkg-config -y cmake-3.21.1 apt-get install openssl libssl-dev 过程参考:Yukon for PostgreSQL_格來羙、日出的博客-CSDN博客 zlib-1.2.9(不需要) 如果用系统的后面gd…...
【LeetCode】160. 相交链表
160. 相交链表 难度:简单 题目 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。 图示两个链表在节点 c1 开始相交: 题目数据 保证 整个链式结构中…...

数据集笔记:NGSIM (next generation simulation)
1 数据集介绍 数据介绍s Next Generation Simulation (NGSIM) Open Data (transportation.gov) 数据地址:Next Generation Simulation (NGSIM) Vehicle Trajectories and Supporting Data | Department of Transportation - Data Portal 时间2005年到2006年间地…...

解决docker运行elastic服务端启动不成功
现象: 然后查看docker日志,发现有vm.max_map_count报错 ERROR: [1] bootstrap checks failed [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] 解决办法: 1. 宿主机(运行doc…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...

Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...