欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C++学习笔记之UC解析

程序员文章站 2022-03-08 23:04:58
c++学习笔记之uc解析。 上次提到了一个源文件要编译成一个可执行文件需要经过 1、预编译 .c->.i gcc -e .c -o .i 2、从c语言编译到汇编语言 .i->.s gcc...

c++学习笔记之uc解析。
上次提到了一个源文件要编译成一个可执行文件需要经过
1、预编译 .c->.i gcc -e .c -o .i
2、从c语言编译到汇编语言 .i->.s gcc -s .i -o .s
3、从汇编语言汇编到机器语言 .s->.o gcc -o .s -o .o
然而只汇编到机器语言是不够的,它依然无法进行执行。
汇编之后的文件叫做目标文件,目标文件可以理解为是一个具备执行条件的文件,但是不具备执行的环境。我们来对比一下可执行文件和目标文件的差异。

linxin@ubuntu:~/biaoc/els$ nm els.o
                 u bianjie
                 u dir
                 u display
                 u els_new
0000000000000191 t get
                 u _global_offset_table_
                 u initmap
0000000000000000 t main
                 u newblock
                 u next
                 u ranking
                 u read_file
0000000000000010 t run
                 u saomiao
                 u srand
                 u __stack_chk_fail
                 u system
                 u time
                 u turn
                 u write_file
linxin@ubuntu:~/biaoc/els$ nm els
0000000000001368 t bianjie
00000000002049e8 b __bss_start
00000000002049e8 b completed.7641
                 w __cxa_finalize@@glibc_2.2.5
0000000000203000 d __data_start
0000000000203000 w data_start
0000000000001841 t delet
00000000000009a0 t deregister_tm_clones
0000000000001f6f t dir
0000000000000f41 t display
0000000000000a30 t __do_global_dtors_aux
0000000000202d50 t __do_global_dtors_aux_fini_array_entry
0000000000203008 d __dso_handle
0000000000202d58 d _dynamic
00000000002049e8 d _edata
000000000000254a t els_new
0000000000203100 d elstype.1809
00000000002049f0 b _end
                 u fclose@@glibc_2.2.5
00000000000025d4 t _fini
                 u fopen@@glibc_2.2.5
0000000000000a70 t frame_dummy
0000000000202d48 t __frame_dummy_init_array_entry
0000000000002a64 r __frame_end__
                 u fread@@glibc_2.2.5
                 u fwrite@@glibc_2.2.5
0000000000000c0b t get
                 u getchar@@glibc_2.2.5
0000000000202f48 d _global_offset_table_
                 w __gmon_start__
00000000000026a0 r __gnu_eh_frame_hdr
0000000000000840 t _init
0000000000202d50 t __init_array_end
0000000000202d48 t __init_array_start
0000000000000d52 t initmap
00000000000025e0 r _io_stdin_used
                 u __isoc99_sscanf@@glibc_2.7
                 w _itm_deregistertmclonetable
                 w _itm_registertmclonetable
00000000000025d0 t __libc_csu_fini
0000000000002560 t __libc_csu_init
                 u __libc_start_main@@glibc_2.2.5
0000000000000a7a t main
0000000000001fdc t newblock
00000000000021a3 t next
                 u printf@@glibc_2.2.5
                 u putchar@@glibc_2.2.5
                 u rand@@glibc_2.2.5
00000000000021f6 t ranking
000000000000245f t read_file
00000000000009e0 t register_tm_clones
0000000000000a8a t run
0000000000001911 t saomiao
                 u sprintf@@glibc_2.2.5
                 u srand@@glibc_2.2.5
                 u __stack_chk_fail@@glibc_2.4
0000000000000970 t _start
                 u strncat@@glibc_2.2.5
                 u system@@glibc_2.2.5
                 u time@@glibc_2.2.5
00000000002049e8 d __tmc_end__
0000000000001b2e t turn
0000000000203020 d turnr.2787
00000000000010c4 t updata
0000000000002321 t write_file

可以看到,在目标文件中所有的标记都是自定义的函数,其中有些函数的属性是t,有些是u
t:表示这个文件调用了这些函数,而且这些函数的定义在本文件内
u:表示这个文件调用了这些函数,但是这些函数不在本文件内
而在可执行文件中,所有自定义的函数的属性都变成了t,还增加了许多没有见过的标记。
这就是可执行文件内容和目标文件内容的差异,也描述了链接过程,做了什么。
链接的过程大概如下,有一堆.o的二进制文件要进行链接的时候,首先找到main函数所在,然后一条一条的从mian函数所在文件向下扫描,当碰到一个u属性的函数,就到定义这个函数的文件里,把这个文件所有的内容全部加载。一直重复这个动作,直到main函数结束。然后再加上运行时文件,最后生成一个可执行文件。
那么什么是运行时文件?
这些文件更像是一种引导,用户写好的程序其实是一个独立的个体,想要再操作上运行,必须具备相应的环境,而这些运行时文件,就相当于给客户的程序装上一个运行的环境。引导我们写好的程序进入操作系统运行。
这就是一个链接的过程:
链接分为静态链接和动态链接。
上述的过程是一个静态链接的过程。
静态链接发生在生成可执行文件的时候
动态链接发生在可执行文件运行的时候
在我们的可执行文件中,依然存在着一些u属性的函数,这些函数,就是运行时会动态链接上来的函数。

大型软件的组织
一个团队进行一个大型软件的开发,需要遵循一定的标准。标准里规定某一部分完成某一个具体的工作,需要为其提供什么要的参数,需要其返回什么样的参数。这些标准都通过头文件里,让每一个参与开发的人员都知道自己需要做什么。
头文件中可以包含任意不需要内存的东西,具体可以包括以下几种:
#ifndef head_h
#define head_h
类型的定义
变量的声明
宏定义
文件的包含
#endif
在这里有个非常颠覆三观的概念,函数也是一种类型
struct str{
int (*func)(struct str *this);
}

gdb调试工具的使用,(这个东西超级好用)
对一个拥有调试信息的程序可以使用gdb工具对其进行调试
对源文件编译的时候,加上-g|-ggdb的选项就可以对最后生成的可执行文件加入调试信息
在bash中输入 : gdb 带有调试信息的可执行程序名
gdb a.out(可执行文件)
gdb 调试命令
l 列出程序列表
b (后面跟函数的名字,或者行数) 设置断点
r 开始运行
n 继续下一行执行
p (后面跟变量的名字,查看变量当前的值)
s 继续下一步执行
q 退出
可以使用man来查看gdb拥有的参数,
它可以运行到指定的程序函数|行数,可以输出当前这个变量内的值。
还有非常多的花样可以玩

一个静态库的制作和使用
1、把所有要放进静态库的文件,编译成目标文件 gcc -c .c
2、将所有的目标文件都打包进静态库 ar -r libels(lib+库名) els1.o els2.o els3.o(要放入静态库的目标文件名称)
3、编译时,使用静态库 gcc els.c -l.(静态库所在路径) -lels(-l+库名,不是库文件名)
ar也是个有趣的指令,可以通过man查看它的资料,非常的多。

#include”“,
这里有几种解决头文件位置的方法。
1、在include后面的”“中存放的时头文件的位置,可以把头文件的位置放进去
#include”els/els.h” ,表示头文件在当前同级目录els下
2、在进行编译的时候,可以临时的为编译器增加一条寻找头文件的路径
gcc -c els.c -iels
这样就可以把路径加进去了,可以来看以下加与不加的效果

linxin@ubuntu:~/biaoc$ gcc -c els.c -v
...
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-linux-gnu/7/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/7/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
end of search list.
...


linxin@ubuntu:~/biaoc$ gcc -c els.c -iels -v
...
#include "..." search starts here:
#include <...> search starts here:
 els
 /usr/lib/gcc/x86_64-linux-gnu/7/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/7/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
end of search list.
...

加了指令之后,系统指定的头文件目录路径就多了一条
3、还可以直接把头文件,放在系统指定的头文件路径之下,不过这么做可能会污染系统头文件,所以一定要这么做的时候,要记得对自定义的头文件的命名一定要规范。