一、用gcc生成静态库和动态库
函数库分为静态库和动态库
静态库
在程序编译时会连接到目标代码中,程序运行则是不需要静态库的存在。
动态库
在程序编译时不会被连接到目标代码中,而是程序运行时载入的。
两者区别:前者是编译连接的,后者是程序运行载入的。
一、hello实例使用库
(一)编译生成例子hello.h、hello.c、main.c
1.hello.h
#ifndef HELLO_H
#define HELLO_H
void hello(const char *name);
#endif//HELLO_H
2.hello.c
#include<stdio.h>
void hello(const char *name)
{
printf("Hello %s!\n",name);
}
3.main.c
#include"hello.h"
int main()
{
hello("everyone");
return 0;
}
(二)将hello.c编译成.o文件
(三)由.o文件生成静态库
创建静态库的工具:ar
静态库文件命名规范:以lib作为前缀,是.a文件
(四)在程序中使用静态库
(5)由.o文件创建动态库文件
创建动态库的工具:gcc
动态库文件命名规范:以lib作为前缀,是.so文件
(6)在程序中使用动态库
二、.a与.so库文件的生成与使用
1.代码
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
#ifdef A_H
#define A_H
void print1(int);
void print2(char *);
#endif
test.c
#include<stdio.h>
#include"A.h"
int main()
{
print1(1);
print2("test");
exit(0);
}
1、静态库.a文件的生成与使用
1.1 生成目标文件(xxx.o)
1.2 生成静态库.a文件
1.3 使用.a库文件,创建可执行程序
2、共享库.so文件的生成与使用
2.1 生成目标文件(xxx.o)
gcc -c -fpic A1.c A2.c
2.2 生成共享库.so文件
gcc -shared *.o -o libsofile.so
./test
文件大小
三、EFF文件格式
ELF 文件格式如下图所示,位于 ELF Header 和 Section Header Table 之间的都是段(Section)。
1. ELF 文件包含下面几个段:
.text:已编译程序的指令代码段。
.rodata:ro 代表 read only,即只读数据(譬如常数 const)。
.data:已初始化的 C 程序全局变量和静态局部变量。
.bss:未初始化的 C 程序全局变量和静态局部变量。
.debug:调试符号表,调试器用此段的信息帮助调试。
可以使用 readelf -S 查看其各个 section 的信息如下
2.反汇编
ELF 由于 ELF 文件无法被当做普通文本文件打开,如果希望直接查看一个 ELF 文件包 含的指令和数据,需要使用反汇编的方法。 使用 objdump -D 对其进行反汇编如下:
四、重温全局变量、局部变量、堆、栈等概念
全局变量
在所有函数外部定义的变量称为全局变量(Global Variable),它的作用域默认是整个程序,也就是所有的源文件。
局部变量
定义在函数内部的变量称为局部变量(Local Variable),它的作用域仅限于函数内部, 离开该函数的内部就是无效的,再使用就会报错。
堆 & 栈
堆(Heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵完全二叉树的数组对象。
栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
代码
#include <stdio.h>
#include <stdlib.h>
//定义全局变量
int init_global_a = 1;
int uninit_global_a;
static int inits_global_b = 2;
static int uninits_global_b;
void output(int a)
{
printf("hello");
printf("%d",a);
printf("\n");
}
int main( )
{
//定义局部变量
int a=2;
static int inits_local_c=2, uninits_local_c;
int init_local_d = 1;
output(a);
char *p;
char str[10] = "lmy";
//定义常量字符串
char *var1 = "1234567890";
char *var2 = "qwertyuiop";
//动态分配
int *p1=malloc(4);
int *p2=malloc(4);
//释放
free(p1);
free(p2);
printf("栈区-变量地址\n");
printf(" a:%p\n", &a);
printf(" init_local_d:%p\n", &init_local_d);
printf(" p:%p\n", &p);
printf(" str:%p\n", str);
printf("\n堆区-动态申请地址\n");
printf(" %p\n", p1);
printf(" %p\n", p2);
printf("\n全局区-全局变量和静态变量\n");
printf("\n.bss段\n");
printf("全局外部无初值 uninit_global_a:%p\n", &uninit_global_a);
printf("静态外部无初值 uninits_global_b:%p\n", &uninits_global_b);
printf("静态内部无初值 uninits_local_c:%p\n", &uninits_local_c);
printf("\n.data段\n");
printf("全局外部有初值 init_global_a:%p\n", &init_global_a);
printf("静态外部有初值 inits_global_b:%p\n", &inits_global_b);
printf("静态内部有初值 inits_local_c:%p\n", &inits_local_c);
printf("\n文字常量区\n");
printf("文字常量地址 :%p\n",var1);
printf("文字常量地址 :%p\n",var2);
printf("\n代码区\n");
printf("程序区地址 :%p\n",&main);
printf("函数地址 :%p\n",&output);
return 0;
}
Ubuntu中代码展示
使用gedit text.c命令创建text.c程序
可以发现,Ubuntu在栈区和堆区的地址值都是从上到下增长的。
STM中代码展示
用之前的串口通信工程进行改写
编译之后在串口软件上观察效果
可以发现,STM32在栈区和堆区的地址值是从上往下的在减小与Ubuntu下刚好相反。
五、总结
本次实验了解了gcc生成静态库和动态库以及静态库.a与.so库文件的生成和使用。并学习了EFF文件格式、了解Ubuntu和STM32下栈区和堆区的区别。还需不断学习加深了解。