用户登录
用户注册

分享至

C++学习笔记之UC解析

  • 作者: 乱气质
  • 来源: 51数据库
  • 2021-10-12

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、还可以直接把头文件,放在系统指定的头文件路径之下,不过这么做可能会污染系统头文件,所以一定要这么做的时候,要记得对自定义的头文件的命名一定要规范。

软件
前端设计
程序设计
Java相关