【无标题】GCC背后的故事级C程序地址分配

GCC背后的故事级C程序地址分配

一、用gcc生成静态库和动态库

1.定义

​ 静态库即静态链接库。静态库在编译的时候会被直接拷贝一份,复制到目标程序里,这段代码在目标程序里就不会再改变了。静态库的好处很明显,编译完成之后,库文件实际上就没有作用了。目标程序没有外部依赖,直接就可以运行。当然其缺点也很明显,就是会使用目标程序的体积增大。

动态库即动态链接库。动态库在编译时不会被拷贝到目标程序中,目标程序中只会存储指向动态库的引用。等到程序运行时,动态库才会被真正加载进来。动态库的优点是,不需要拷贝到目标程序中,不会影响目标程序的体积,而且同一份库可以被多个程序使用(因为这个原因,动态库也被称作共享库)。同时,编译时才载入的特性,也可以让我们随时对库进行替换,而不需要重新编译代码。动态库带来的问题主要是,动态载入会带来一部分性能损失,使用动态库也会使得程序依赖于外部环境。如果环境缺少动态库或者库的版本不正确,就会导致程序无法运行。

2.生成

第一:分别在Linux上生成hello.h\hello.ch和main.c三个文件:

如上图所示

第二:得到hello.o文件

#gcc-c hello.c

第三:使用静态库

输入命令

gcc -o hello main.c -L. –lmyhello

然后输入命令

./hello

得到结果

Hello everyone!

第四:生成动态库并使用

输入一下命令得到动态库文件

gcc -shared -fPIC -o libmyhello.so hello.o

之后继续输入 ls命令看看是否生成动态库文件
成功后我们会看见一个.so文件
6.调用动态库
输入命令

gcc -o hello main.c -L. -1myhello

./hello
会出现如下提示
./hello: error while loading shared libraries: libmyhello.so: cannot open shar
ed object file: No such file or directory
意思是找不到动态库文件,因为程序运行时会在/usr/lib等目录中查找所需要的动态库文件,我们先把.so文件复制到目录/usr/lib
输入一下命令

\# mv libmyhello.so /usr/lib

\# ./hello

Hello everyone!

二、GCC常用命令和背后故事

第一:预处理
(1)预处理的过程主要包括以下过程:将所有的#define 删除,并且展开所有的宏定义,并且处理所有的条件预编 译指令,比如#if #ifdef #elif #else #endif 等。
处理#include 预编译指令,将被包含的文件插入到该预编译指令的位置。
删除所有注释“//”和“/* */”。
添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号。
输入命令

gcc -E test!

可查看预处理文件
生成的.i文件可以作为普通文本打开查看。
输入以下命令

cat hello.i

第二:编译

编译过程就是对预处理完的文件进行一系列的词法分析,语法分析,语义分析及优化后生成相应的汇编代码

`使用以下命令

gcc -text.i -o test.o

k可查看hello.s 文件

第三:汇编

每一个汇编语句几乎都对应一条处理器指令,汇编相对于编译过程比较简单,通过调用 Binutils 中的汇编器 as 根据汇编指令和处理器指令的对照表一一翻译即可。

汇编命令:

gcc -c hello.s -o hello.o

(注意:汇编过程调用对汇编代码进行处理,生成处理器能识别的指令,保存在后缀为.o 的目标文件中。当程序由多个源代码文件构成时,每个文件都要先完成汇编工作,生成.o 目标 文件后,才能进入下一步的链接工作。)

第四:链接

分为静态库链接和动态库链接

静态库链接命令:

gcc hello.c -o hello
size hello ldd hello //

动态库链接命令:

gcc -static hello.c -o hello size hello ldd hello

gcc其他常用命令
-c:把源程序编译为目标代码,生成以同名的.o为后缀名的目标文件。例如:
gcc test.c -o test

-S:生成一个后缀名为.s的汇编语言文件。例如:
gcc -S test.i -o test.s

-e对文件进行预处理,预处理的结果送到标准输出(如显示器)中。例如:
gcc -E test.c

-x language:强制编译器用指定的语言编译器来编译某个源程序。

-o:在默认状态下,在当前目录生成一个名为a.out的可执行程序。例如:

gcc test.o -o test

三、程序常量变量位置解析

定义

1、全局变量
全局变量是指在函数外部定义的变量,这些变量可以被程序中的所有函数访问。在C语言中,全局变量可以被定义在函数外部的任何位置,但是一般情况下,我们会将它们定义在所有函数之前。下面详细介绍全局变量的定义和使用方法。
全局变量的作用域为整个程序,也就是说,它们可以被程序中的所有函数访问。但是,如果在函数内部定义了与全局变量同名的局部变量,那么在该函数内部,局部变量会覆盖全局变量。

2、局部变量

局部变量,也称内部变量,是指在一个函数内部或复合语句内部定义的变量。局部变量是相对于全局变量而言的。局部变量的作用域是定义该变量的函数或定义该变量的复合语句。也就是说,局部变量只在定义它的函数或复合语句范围内有效,只能在定义它的函数或复合语句内才能使用它们。

3、堆

堆是一个完全二叉树;

第一点,堆必须是一个完全二叉树。完全二叉树要求,除了最后一层,其他层的节点个数都是满的,最后一层的节点都靠左排列,自然堆也具有完全二叉树的所有性质。

第二点,堆中的每个节点的值必须大于等于(或者小于等于)其子树中每个节点的值。实际上,我们还可以换一种说法,堆中每个节点的值都大于等于(或者小于等于)其左右子节点的值。这两种表述是等价的。

堆的创建

void HeaPCreate(Heap* hp, HPDataType* a, int n) { hp->arry = (HPDataType*)malloc(sizeof(HPDataType)*n); if (NULL == hp->arry) { assert(hp); return; } memcpy(hp->arry, a, n * 4); hp->capacity = n; hp->size = n; for (int root = (n - 2) / 2; root >= 0; root--) { AjustdownHeap(hp,root); } }

4、栈

栈是只允许在一端进行插入或删除操作的线性表

地址分配

1、栈区— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

2、堆区 — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。它与数据结构中的堆是两回事,分配方式类似于链表。

3、全局区(静态区)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 程序结束后有系统释放

4、文字常量区—常量字符串就是放在这里的。 程序结束后由系统释放

5、RAM

RAM又称随机存取存储器,存储的内容可通过指令随机读写访问。RAM中的存储的数据在掉电是会丢失,因而只能在开机运行时存储数据。其中RAM又可以分为两种,一种是Dynamic RAM(DRAM动态随机存储器),另一种是Static RAM(SRAM,静态随机存储器)。

在ubuntu 中运行以下代码

`#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] = “yaoyao”;//栈
//定义常量字符串
char *var1 = “1234567890”;
char *var2 = “abcdefghij”;
//动态分配——堆区
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;
}`

编译后得

此文所需图片补充
请添加图片描述
请添加图片描述
请添加图片描述请添加图片描述
g请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值