019-位运算,静动态库

019-位运算,静动态库

一、位运算

1.位运算概念

位运算是指按二进制位进行的运算。因为在系统软件中,常要处理二进制位的问题。

例如:将一个存储单元中的各二进制位左移或右移一位,两个数按位相加等。

C语言提供位运算的功能,与其他高级语言相比,具有很大的优越性。

运算符含义
按位与
取反
|按位或
<<左移
按位异或
>>右移
2.按位与运算符 &

按位与是指:参加运算的两个数据,按二进制位进行“与”运算。如果两个相应的二进制位都为1,则该位的结果值为1;否则为0。

   00000011(3)
  &00000101(5)
   00000001(1)
3.按位或运算符 |

按位与是指:两个相应的二进制位中只要有一个为1,该位的结果值为1。

   00110000
 | 00001111
   00111111
4.按位异或运算符 ^

异或运算符∧也称XOR运算符。它的规则是:
若参加运算的两个二进制位同号则结果为0(假)
异号则结果为1(真)

   00111001
 ^ 00101010
   00010011 

设 a=3,b=4; 不用中间变量,交换两个数据的值

答案:
a=a^b;
b=b^a;
a=a^b;
5.按位取反 ~

~是一个单目(元)运算符,用来对一个二进制数按位取反,即将0变1,将1变0。例如,~0x15是对十六进制数15(即二进制数00010101)按位求反。

       00010101
(~)                
       11101010 (十六进制数0xEA)
6.左移运算符 <<

左移运算符是用来将一个数的各二进制位全部左移若干位

高位左移后溢出,舍弃。

例如:a=<<2 将a的二进制数左移2位,右补0。
若a=15,即二进制数00001111,
左移2位得00111100,(十进制数60)

7.右移运算符 >>

右移运算符是用来将一个数的各二进制位全部右移若干位,移到右端的低位被舍弃,对无符号数,高位补0

右移一位相当于除以2
右移n位相当于除以2的n次方

例如: a=15时:
a的值用二进制形式表示为00001111, a>>2舍弃低2位11: a>>2=00000011

8.位段

C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域” ( bit field) 。利用位段能够用较少的位数存储数据。

位段定义:

struct  data
{
    unsigned a:2;
    unsigned b:6;
    unsigned c:4;
    unsigned d:4;
    int i;
}data;

:表示机构内位域的定义(即该变量占几个bit空间

说明:

(1) 位段成员的类型不能为浮点型类型。
(2) 若某一位段要从另一个字开始存放,可用以下形式
定义:
unsigned a:1;
unsigned b:2;一个存储单元
unsigned:0;
unsigned c:3;另一存储单元  
a、b、c应连续存放在一个存储单元中,由于用了
长度为0的位段,其作用是使下一个位段从下一个
存储单元开始存放。因此,只将a、b存储在一个存储单元中,c另存在下一个单元(“存储单元”可能是一个字节,也可能是2个字节,视不同的编译系统而异)。

(3) 一个位段必须存储在同一存储单元中,不能跨两个单元。如果第一个单元空间不能容纳下一个位段,则该空间不用,而从下一个单元起存放该位段。
(4) 可以定义无名位段。
(5) 位段的长度不能大于存储单元的长度,也不能定义位段数组。
(6) 位段可以用整型格式符输出。
(7) 位段可以在数值表达式中引用,它会被系统自动地转换成整型数。

二、静态库与动态库

1.什么是库

在 windows 平台和 linux 平台下存在着⼤量的库,它们常常提供⼀些通⽤功 能,例如链表和⼆叉树可以⽤来保存任何数据。本质上说库是⼀种可执⾏的⼆ 进制代码(即已经预先编译好但不可以独⽴执⾏),可以被操作系统载⼊内存执 ⾏。

由于 windows 和 linux 的平台不同(主要是编译器、汇编器和连接器 的不 同),因此⼆者库的⼆进制是不兼容的。 本节仅介绍 linux 下的库,Linux 系统存储库的位置⼀般在:/lib 和 /usr/lib。 在 64 位的系统上有些库也可能被存储在/usr/lib64 下。库的头⽂件⼀般会被 存储在 /usr/include 下或其⼦⽬录下。

静态库:
windows	.lib
linux	.a
    
动态库:
windows	.dll
linux	.so
2.库的种类

linux 下的库有两种:静态库共享库(动态库)

静态库的命令规则为 libxxx.a,共享库,其命令规则为 libxxx.so。

两者的主要区别是:

静态库在链接时将⽤到的⽅法或函数包含到最终⽣成的可执⾏程序中,⽽共享库不包含,只做标记(在编译过程中仅简单的引⽤),在运⾏程序时,才动态加载,因此共享库⽣成的可执⾏程序代码体积较⼩。 共享库(动态库)的好处是:不同的应⽤程序如果调⽤相同的库,那么在内存⾥只需要有⼀份该共享库的实例。

3.静态库的创建与使用

(1)⾸先准备如下四个⽂件:add.c max.c foo.h mymain.c

//---------------------add.c--------------------
int add(int a,int b)
{
	return a+b;
}
//---------------------max.c--------------------
int max(int a,int b)
{
	return x>y ? x : y;
}
//-------------------------foo.h---头⽂件 包含上述两函数的原型声明。
#ifndef _FOO_H_
#define _FOO_H_

int add(int,int);
int max(int,int);
#endif

(2)先将需要⽣成库⽂件的所有“.c“⽂件编译成“.o”⽂件

gcc -c add.c
gcc -c max.c
生成add.o max.o

(3)使⽤ ar 命令将第⼀步编译的所有”.o”⽂件⽣成静态库,其中:

  • c 是创建库
  • r 是将⽅法添加到库中
  • v 显示过程

这⾥的ar其实就是archive的意思,ar命令可以⽤来创建、修改库,也可以从库 中提出单个模块,ar可让你集合许多⽂件,成为单⼀的备存⽂件。在备存⽂件 中,所有成员⽂件皆保有原来的属性与权限。

ar crv libfoo.a add.o max.o

打印如下:
a - add.o
a - max.o

此时就生成了libfoo.a

(4)使⽤静态库

准备⼀个测试主程序mymain.c

//--------------------------mymain.c----------------------------
#include<stdio.h>
#include "foo.h"
int main()
{
	int a=100,b=150;
	printf("result=%d ---%d",add(a,b),max(a,b));
	return 0;
}

以下是使⽤静态库“libfoo.a”和“mymain.c”⽣成可执⾏⽂件的过程,其中:

  • -L 指定库的存储路径
  • -l 指定库的名称(不需要前⾯的‘lib’和扩展名‘.a’)
ljs@ljs-virtual-machine:~/mylib$ gcc mymain.c -L. -lfoo

这⾥的-L.表示要连接的库在当前⽬录中.

如果静态库和主程序不在同⼀个⽬录下载L后指定具体路径:

ljs@ljs-virtual-machine:~/mylib$ gcc mymain.c -L/home/ljs -lfoo

通过-L 设置的路径将被优先搜索,然后才是搜索系统的环境变量中的路径,⽐ 如/usr/lib 等路径。

在链接和执⾏阶段都涉及库的调⽤(访问)。.h⽂件(头⽂件)是库的接⼝。.h ⽂件提供给库的使⽤者,使⽤者通过.h来调⽤库中的API。有了接⼝,函数就知 道去包含这些接⼝的库中寻找实现的代码,找这些库就需要知道库的名字,所 在的位置(路径),否则程序只能通过编译,在链接的时候会出现问题。

需要注意的是:-l后边跟的是库的“核⼼”名字。完整的库名字,⽐如libtest.so(动态库)、 libfoo.a(静态库),其核⼼名字为test、foo,即在makefile中链接库的时候需要去掉完整 名字的⾸尾部。

4.动态库的创建与使⽤

(1)准备待⽣成库的代码和上述⼀样

(2)先将需要⽣成库⽂件的所有“.c“⽂件编译成“.o”⽂件。

(3)使⽤ gcc 命令将第⼀步编译的所有”.o”⽂件⽣成共享库,其中:

-shared :选项是⼀种编译器标志,⽤来创建共享库

-fpic :选项是⼀种编译器标志,表示产⽣与内存位置⽆关的代码,即在产⽣代 码中的过程中全部使⽤相对地址,⽽不要使⽤绝对地址,其中pic代表的是 position independent code。

gcc -shared -fPIC -o libfoo.so add.o max.o

生成libfoo.so

(4)⽤下边代码调⽤动态库中的函数

//--------------------------mymain.c----------------------------
#include<stdio.h>
#include "foo.h"
int main()
{
	int a=100,b=150;
	printf("result=%d ---%d",add(a,b),max(a,b));
	return 0;
}

如果在库的存储路径有同名的共享库和静态库,gcc 默认使⽤共享库。

(5)使⽤共享库

gcc -o mymain mymain.c -L. -lfoo
./mymain

⽣成之后,直接执⾏ main 程序,发现出错,原因是系统加载共享库时,找不 到对应的共享库⽂件”libfoo.so”, 但是该库确实在当前⽬录下存在。这是为什 么呢?因为系统默认只会去存储库的标准位置(/lib 或/usr/lib 等)加载,⽽不会 在当前位置寻找。所以将库拷⻉到/usr/lib 下,再执⾏程序,就可以成功。

sudo cp libfoo.so /usr/lib
./mymain

如果库不在标准位置下,也可以通过设置环境变量”LD_LIBRARY_PATH”来指 定加载库的路径。在linux中 export 命令⽤于设置或显示环境变量。

export LD_LIBRARY_PATH=/home/ljs/mylib:$LD_LIBRARY_PATH

可以通过 ldd 命令查看可执⾏程序使⽤了哪些共享库:

ldd mymain
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Three笔记

有用的话赏点吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值