当前位置: 首页 > news >正文

使用树莓派学习Linux系统编程的 --- 库编程(面试重点)

在之前的Linux系统编程中,学习了文件的打开;关闭;读写;进程;线程等概念....

本节补充“Linux库概念 & 相关编程”,这是一个面试的重点

分文件编程

在之前的学习中,面对较大的项目比如 STM32的小车 或 香橙派实现的智能垃圾桶 ,都使用了分文件编程的思路。

其 实现的核心思想就是:将功能性函数的实现单独写在其他的地方,在main函数中调用那些封装好的功能性函数。

这样做的好处是:

  • 分模块的编程思想:在实际工作中面对大型项目,可以让A完成串口开发;B完成网络开发,最后只需要他们提供h文件中的函数接口就可以在主函数中直接调用了,测试时发现哪部分有问题可以直接找负责的人,方便调试
  • 代码可移植性更强:因为分文件编程了,串口,网络,语音可能都被封装好了,那么后续如果其他项目需要这些功能就可以直接调用封装好的接口了,最多只需要微调
  • main函数更加精简:由于把功能性函数的实现步骤都封装到其他文件了,main函数就可以专注于项目的整体调用逻辑,使得整个main看起来更加清晰,逻辑通畅

具体步骤

  1. 将功能性函数名和具体实现步骤写在一个.c文件中
  2. 创建一个同名的.h文件,包含所有可能会被调用的函数原型去除函数体
  3. main中包含刚刚创建的.h文件
  4. main中调用被封装好的函数接口
  5. 使用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
优点
  1. 运行快
  2. 发布程序无需提供静态库,因为已经在app中,移植方便
缺点
  1. 程序大
  2. 更新部署发布麻烦
动态数据库(libXXX.so
优点
  1. 程序小
  2. 升级简单
  3. 不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享(动态)库的实例
缺点
  1. 运行相对慢

  2. 需要提供依赖的动态库

静态库的制作(不太常用了)

制作步骤
  • 使用以下指令将.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系统编程中&#xff0c;学习了文件的打开&#xff1b;关闭&#xff1b;读写&#xff1b;进程&#xff1b;线程等概念.... 本节补充“Linux库概念 & 相关编程”&#xff0c;这是一个面试的重点&#xff01; 分文件编程 在之前的学习中&#xff0c;面对较大的…...

vs2017打开工程提示若要解决此问题,请使用以下选择启动 Visual Studio 安装程序: 用于 x86 和 x64 的 Visual C++ MFC

下载安装文件。 下载之后点击C项目&#xff0c;他会提示需要安装编译依赖。这个时候需要选择 用于 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指令都可以用于在数据库中搜索匹配指定模式的键。然而&#xff0c;它们之间有一些关键的区别&#xff1b; KEYS指令会在整个数据库中阻塞地执行匹配操作&#xff0c;并返回匹配的键列表。如果数据库很大&#xff0c;或者匹配的键很多&#xff0c;将会对性能产…...

今天遇到Windows 10里安装的Ubuntu(WSL)的缺点

随着技术的发展&#xff0c;越来越多开发者转向使用 Windows Subsystem for Linux&#xff08;WSL&#xff09;在 Windows 10 上进行开发&#xff0c;也就是说不用虚拟机&#xff0c;不用准备多一台电脑&#xff0c;只需要在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&#xff0c;它们说明了面向对象的封装性&#xff0c;所以我们要利用它们尽可能的让访问权限降到最低&#xff0c;从而提高安全性。 private表示私有&#xff0c;只有自己类能访问&#xff0c;属性可以…...

python趣味编程-5分钟实现一个贪吃蛇游戏(含源码、步骤讲解)

Python 贪吃蛇游戏代码是用 Python 语言编写的。在这个贪吃蛇游戏中,Python 代码是增强您在创建和设计如何使用 Python 创建贪吃蛇游戏方面的技能和才能的方法。 Python Tkinter中的贪吃蛇游戏是一个简单干净的 GUI,可轻松玩游戏。游戏设计非常简单,用户不会觉得使用和理解…...

如何在虚拟机的Ubuntu22.04中设置静态IP地址

为了让Linux系统的IP地址在重新启动电脑之后IP地址不进行变更&#xff0c;所以将其IP地址设置为静态IP地址。 查看虚拟机中虚拟网络编辑器获取当前的子网IP端 修改文件/etc/netplan/00-installer-config.yaml文件&#xff0c;打开你会看到以下内容 # This is the network conf…...

代码随想录算法训练营第二十九天| 491 递增子序列 46 全排列

目录 491 递增子序列 46 全排列 491 递增子序列 在dfs中进行判断&#xff0c;如果path的长度大于1&#xff0c;则将其添加到res中。 本题nums中的元素的值处于-100与100之间&#xff0c;可以将元素映射0到199之间并且通过布尔数组st来记录此层中元素是否被使用过&#xff0c;…...

(动手学习深度学习)第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在内存中总是会增长&#xff1b;它不会收缩。因此&#xff0c;如果map导致了一些内存问题&#xff0c;你可以尝试不同的选项&#xff0c;比如强制 Go 重新创建map或使用指针。 在 Go 中使用map时&#xff0c;我们需要了解map增长和收缩的一些重要特性。让我们深入探讨这一点…...

前缀和(c++,超详细,含二维)

前缀和与差分 当给定一段整数序列a1,a2,a3,a4,a5…an; 每次让我们求一段区间的和&#xff0c;正常做法是for循环遍历区间起始点到结束点&#xff0c;进行求和计算&#xff0c;但是当询问次数很多并且区间很长的时候 比如&#xff0c;10^5 个询问和10^6区间长度&#xff0c;相…...

详解FreeRTOS:二值信号量和计数信号量(高级篇—2)

目录 1、二值信号量 1.1、二值信号量运行机制 1.2、创建二值信号量 1...

持续集成交付CICD:Jenkins通过API触发流水线

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

【Python】12 GPflow安装

概述 GPflow 是一个基于TensorFlow 在 Python 中构建高斯过程模型的包。高斯过程是一种监督学习模型。 高斯过程的一些优点是&#xff1a; 不确定性是高斯过程的固有部分。高斯过程可以在不知道答案时告诉您。适用于小型数据集。如果您的数据有限&#xff0c;高斯过程可以从…...

Ubuntu源码编译gdal3.6.2

在华为云申请了一台Ubuntu v18的机器,乱七八糟的不要装。 apt install build-essential pkg-config -y cmake-3.21.1 apt-get install openssl libssl-dev 过程参考&#xff1a;Yukon for PostgreSQL_格來羙、日出的博客-CSDN博客 zlib-1.2.9(不需要) 如果用系统的后面gd…...

【LeetCode】160. 相交链表

160. 相交链表 难度&#xff1a;简单 题目 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中…...

数据集笔记:NGSIM (next generation simulation)

1 数据集介绍 数据介绍s Next Generation Simulation (NGSIM) Open Data (transportation.gov) 数据地址&#xff1a;Next Generation Simulation (NGSIM) Vehicle Trajectories and Supporting Data | Department of Transportation - Data Portal 时间2005年到2006年间地…...

解决docker运行elastic服务端启动不成功

现象&#xff1a; 然后查看docker日志&#xff0c;发现有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] 解决办法&#xff1a; 1. 宿主机&#xff08;运行doc…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...

elementUI点击浏览table所选行数据查看文档

项目场景&#xff1a; table按照要求特定的数据变成按钮可以点击 解决方案&#xff1a; <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...

认识CMake并使用CMake构建自己的第一个项目

1.CMake的作用和优势 跨平台支持&#xff1a;CMake支持多种操作系统和编译器&#xff0c;使用同一份构建配置可以在不同的环境中使用 简化配置&#xff1a;通过CMakeLists.txt文件&#xff0c;用户可以定义项目结构、依赖项、编译选项等&#xff0c;无需手动编写复杂的构建脚本…...

Unity中的transform.up

2025年6月8日&#xff0c;周日下午 在Unity中&#xff0c;transform.up是Transform组件的一个属性&#xff0c;表示游戏对象在世界空间中的“上”方向&#xff08;Y轴正方向&#xff09;&#xff0c;且会随对象旋转动态变化。以下是关键点解析&#xff1a; 基本定义 transfor…...