C语言编译与链接过程详解
C语言编译与链接过程详解
源文件
main.c
#include <stdio.h>extern int data;
extern int add(int a,int b);int a1;
int a2 = 0;
int a3 = 10;static int b1;
static int b2 = 0;
static int b3 = 20;int main()
{int c1;int c2 = 0;int c3 = 30;static int d1;static int d2 = 0;static int d3 = 40;c1 = data;c2 = add(a1,a2);while(1);return 0;
}
add.c
int data = 3;
int add(int a,int b)
{return a+b;
}
两大过程:编译、链接
一、编译过程:
-
预处理 (.i)
-
处理#开头的预处理指令:#include #define #ifndef #if #else 等等
-
去注释、加行号、生成文件索引等等
命令:gcc -E main.c -o main.i,生成 .i 文件
-
-
编译 (.s)
将 .i 文件编译生成 .s 汇编文件
命令:gcc -S main.i 生成 .s 文件
-
汇编 (.o)
将汇编文件翻译成二进程可重定位文件,即 .o 文件
命令:gcc -c main.s 生成 .o 文件
PS:gcc命令只是一些后台程序的包装,它会根据不同的参数调用其他程序:
-
预编译和编译合并成了一个步骤,使用的是程序cc1,也可以通过如下命令生成.s文件
cc1 hello.c
等同于 gcc -S hello.c -o hello.s
-
汇编器 as
-
链接器 ld
分析二进制可重定位文件
main.c文件
#include <stdio.h>int a1;
int a2 = 0;
int a3 = 10;static int b1;
static int b2 = 0;
static int b3 = 20;int main(void)
{int c1;int c2 = 0;int c3 = 30;static int d1;static int d2 = 0;static int d3 = 40;return 0;
}
编译命令:在64位的机器上编译32位的.o文件
*gcc -m32 -fno-PIC -c .c
-m32指定编译生成32位文件;-fno-PIC去除和位置无关的段(只留下.text .data .bss .comment 等)
1. 读取 elf 文件头
$ readelf -h main.o
ELF 头:Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 类别: ELF32数据: 2 补码,小端序 (little endian)版本: 1 (current)OS/ABI: UNIX - System VABI 版本: 0类型: REL (可重定位文件)系统架构: ARM版本: 0x1入口点地址: 0x0程序头起点: 0 (bytes into file)Start of section headers: 268 (bytes into file)标志: 0x5000000, Version5 EABI本头的大小: 52 (字节)程序头大小: 0 (字节)Number of program headers: 0节头大小: 40 (字节)节头数量: 10字符串表索引节头: 7
(1) 魔数
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
(2) REL (可重定位文件)
(3) 入口点地址: 0x0
(4) Start of section headers: 268 (bytes into file)
(5) 本头的大小: 52 (字节)
2. 获取 elf 文件的 section headers(段头) 信息 (供链接使用)
$ readelf -S main.o
There are 12 section headers, starting at offset 0x2ec:节头:[Nr] Name Type Addr Off Size ES Flg Lk Inf Al[ 0] NULL 00000000 000000 000000 00 0 0 0[ 1] .text PROGBITS 00000000 000034 000044 00 AX 0 0 1[ 2] .rel.text REL 00000000 00026c 000020 08 I 9 1 4[ 3] .data PROGBITS 00000000 000078 00000c 00 WA 0 0 4[ 4] .bss NOBITS 00000000 000084 000014 00 WA 0 0 4[ 5] .comment PROGBITS 00000000 000084 00002a 01 MS 0 0 1[ 6] .note.GNU-stack PROGBITS 00000000 0000ae 000000 00 0 0 1[ 7] .eh_frame PROGBITS 00000000 0000b0 00003c 00 A 0 0 4[ 8] .rel.eh_frame REL 00000000 00028c 000008 08 I 9 7 4[ 9] .symtab SYMTAB 00000000 0000ec 000140 10 10 14 4[10] .strtab STRTAB 00000000 00022c 000040 00 0 0 1[11] .shstrtab STRTAB 00000000 000294 000057 00 0 0 1
Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), I (info),L (link order), O (extra OS processing required), G (group), T (TLS),C (compressed), x (unknown), o (OS specific), E (exclude),p (processor specific)
有12个段头,起始段头偏移为 0x2ec
可以看到每个段的偏移与大小
3. 打印出段的内容
~ $ objdump -s main.omain.o: 文件格式 elf32-i386Contents of section .text:0000 8d4c2404 83e4f0ff 71fc5589 e55183ec .L$.....q.U..Q..0010 14c745ec 00000000 c745f01e 000000a1 ..E......E......0020 00000000 8945f48b 15000000 00a10000 .....E..........0030 000083ec 085250e8 fcffffff 83c41089 .....RP.........0040 45ecebfe E...
Contents of section .data:0000 0a000000 14000000 28000000 ........(...
Contents of section .comment:0000 00474343 3a202855 62756e74 7520372e .GCC: (Ubuntu 7.0010 352e302d 33756275 6e747531 7e31382e 5.0-3ubuntu1~18.0020 30342920 372e352e 3000 04) 7.5.0.
Contents of section .eh_frame:0000 14000000 00000000 017a5200 017c0801 .........zR..|..0010 1b0c0404 88010000 20000000 1c000000 ........ .......0020 00000000 44000000 00440c01 00471005 ....D....D...G..0030 02750043 0f03757c 06000000 .u.C..u|....
4. 读取 .o 文件符号表
~ $ objdump -t main.o
main.o: 文件格式 elf32-littleSYMBOL TABLE:
00000000 l df *ABS* 00000000 main.c
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000004 l O .bss 00000004 b1
00000008 l O .bss 00000004 b2
00000004 l O .data 00000004 b3
00000008 l O .data 00000004 d3.1881
0000000c l O .bss 00000004 d2.1880
00000010 l O .bss 00000004 d1.1879
00000000 l d .note.GNU-stack 00000000 .note.GNU-stack
00000000 l d .eh_frame 00000000 .eh_frame
00000000 l d .comment 00000000 .comment
00000004 O *COM* 00000004 a1
00000000 g O .bss 00000004 a2
00000000 g O .data 00000004 a3
00000000 g F .text 00000044 main
00000000 *UND* 00000000 data
00000000 *UND* 00000000 add
标出了每个符号处于那个段,占多大内存,其中 a1 标记为 *COM* 表示它是弱符号(未初始化的非静态全局变量,可能其他文件里也定义了同名的)
data 和 add 这两个符号被标记为 *UND* ,表示未定义的符号,在本文件中找不到定义,链接时会从其他文件中寻找
5. 根据 section headers(段头) 信息,画出二进制可重定位文件的组成(.o文件)
可以发现bss段和comment段的起始卫视相同,但实际计算得出bss段在.o文件中并没有存储,但是符号表中对bss段有记录。
得出结论:bss段保存的都是未初始化 / 初始化为0的全局变量,和未初始化 / 初始化为0的静态局部变量,所以他们的默认值都为0 ,故为了节省.o文件的空间,无需存储,但是需要在符号表中记录,在最后执行可执行文件后,将bss段的符号存到虚拟地址空间中。
二、链接过程:
在64位x86机器上编译-链接生成32位目标文件和可执行文件的命令
编译:gcc -m32 -fno-PIC -c *.c
手动链接:ld -e main -melf_i386 *.o -o run生成如下文件:$ lsadd.c add.o main.c main.o run
PS:
-m32指定编译生成32位文件;
-fno-PIC去除和位置无关的段(只留下.text .data .bss .comment 等)
-e 指定程序入口,-e后跟着符号即可,也可以把add函数作为程序入口,即 -e add
-melf_i386指定链接生成32位的,x86架构的可执行文件
链接过程的本质主要是将多个目标文件“粘”在一起,实质上拼合的是目标文件之间对地址的引用,即函数名和全局变量
符号表就是.o文件的一个段,symtab,查看符号表命令
readelf -s main.o
objdump -t main.o
nm main.o
符号表中包含什么,主要关注1和2:
-
- 定义在本目标文件中的全局符号,例如变量名、函数名等
-
- 引用的其他目标文件中的符号,没有在本文件中定义,一般叫做外部符号
-
- 段名,如 “.text”, “.data” 等
-
- 局部符号,只在编译单元内部可见,调试器可以使用这些符号来分析程序或崩溃时的核心转储文件,链接过程中链接器往往忽略它们
$ objdump -t main.omain.o: 文件格式 elf32-i386SYMBOL TABLE:
00000000 l df *ABS* 00000000 main.c
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000004 l O .bss 00000004 b1
00000008 l O .bss 00000004 b2
00000004 l O .data 00000004 b3
00000008 l O .data 00000004 d3.1877
0000000c l O .bss 00000004 d2.1876
00000010 l O .bss 00000004 d1.1875
00000000 l d .note.GNU-stack 00000000 .note.GNU-stack
00000000 l d .eh_frame 00000000 .eh_frame
00000000 l d .comment 00000000 .comment
00000004 O *COM* 00000004 a1
00000000 g O .bss 00000004 a2
00000000 g O .data 00000004 a3
00000000 g F .text 00000016 main
1. 合并所有 .o 文件的段
如上图所示,text段合并,data段合并,bss段合并的同时,需要将弱符号转化为强符号(或者弱符号被强符号替换),bss段大小增加
并且发现链接后,生成的可执行文件的每个段都分配了内存地址(虚拟内存)
2. 合并符号表、符号解析、重定位
- 合并符号表
可以看出,可执行文件的符号表就是将多个.o文件的符号表简单的合并起来
- 符号解析
将弱符号(*COM*)转化为强符号
在其他文件中找到本文件中未定义的符号(*UND*)
- 重定位
为符号分配虚拟内存地址,符号的地址是根据段的地址加上自身的偏移计算的
可执行文件分析
1. 查看文件头
$ readelf -h run
ELF 头:Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 类别: ELF32数据: 2 补码,小端序 (little endian)版本: 1 (current)OS/ABI: UNIX - System VABI 版本: 0类型: EXEC (可执行文件)系统架构: Intel 80386版本: 0x1入口点地址: 0x80480a1程序头起点: 52 (bytes into file)Start of section headers: 4676 (bytes into file)标志: 0x0本头的大小: 52 (字节)程序头大小: 32 (字节)Number of program headers: 3节头大小: 40 (字节)节头数量: 9字符串表索引节头: 8
入口点地址:0x80480a1。
2. 查看段信息
$ readelf -S run
There are 9 section headers, starting at offset 0x1244:节头:[Nr] Name Type Addr Off Size ES Flg Lk Inf Al[ 0] NULL 00000000 000000 000000 00 0 0 0[ 1] .text PROGBITS 08048094 000094 000051 00 AX 0 0 1[ 2] .eh_frame PROGBITS 080480e8 0000e8 00005c 00 A 0 0 4[ 3] .data PROGBITS 0804a000 001000 000010 00 WA 0 0 4[ 4] .bss NOBITS 0804a010 001010 000018 00 WA 0 0 4[ 5] .comment PROGBITS 00000000 001010 000029 01 MS 0 0 1[ 6] .symtab SYMTAB 00000000 00103c 000170 10 7 14 4[ 7] .strtab STRTAB 00000000 0011ac 000059 00 0 0 1[ 8] .shstrtab STRTAB 00000000 001205 00003f 00 0 0 1
每个段都分配了虚拟地址。
3. 查看 program headers
$ readelf -l runElf 文件类型为 EXEC (可执行文件)
Entry point 0x80480a1
There are 3 program headers, starting at offset 52程序头:Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg AlignLOAD 0x000000 0x08048000 0x08048000 0x00144 0x00144 R E 0x1000LOAD 0x001000 0x0804a000 0x0804a000 0x00010 0x00028 RW 0x1000GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10Section to Segment mapping:段节...00 .text .eh_frame 01 .data .bss 02
二进制可重定位文件只有 “section headers”,只有可执行文件里有 “program headers”,“program headers” 中显示了各个段的虚拟地址、对齐字节(一页4K)
按段的属性合并,只读(text+rodata)、可读可写(data+bss)等等
使用 readelf -l main 查看ELF的 “Segment” (供装载使用)
PS:因为我们是自己链接的,没有链接C库,所以段里的内容比较少
* 如果直接运行 gcc main.c -o main,则会默认链接C库,查看可执行文件的每个段时就有很多内容了
* 可执行文件是被 execve 加载到进程中的
* 可执行文件之所以可以运行,因为其指定了入口地址(main)、program headers(指定加载的虚拟地址)
* 描述 “Segment” 的结构叫 ”程序头” ,它描述了ELF文件该如何被操作系统映射到进程的虚拟空间。
相关文章:
C语言编译与链接过程详解
C语言编译与链接过程详解 源文件 main.c #include <stdio.h>extern int data; extern int add(int a,int b);int a1; int a2 0; int a3 10;static int b1; static int b2 0; static int b3 20;int main() {int c1;int c2 0;int c3 30;static int d1;static int …...
Qt信号和槽 定时器
文章目录 1 信号和槽1.1 信号和槽的概念1.2 信号和槽的应用1.3 信号和槽的连接1.4 信号和槽的特性1.5 生活中的类似例子1.6 信号和槽的优势 2 信号和槽的使用2.1 控件的信号和槽2.2 自定义信号和槽2.3 信号和槽的参数传递 3 定时器3.1 QTimer类的基本使用3.2 QTimer类的成员函数…...
zemax对称式目镜
两个几乎对称的双胶合透镜相对放置,可以达到25度的半视场 为了加工方便,这两个透镜组采用相同的结构 对称式目镜要求各组透镜自行校正色差,这样倍率色差也随之而校正。 它还能校正两种像差,慧差和象散。 对称目镜的结构更紧&…...
层次架构、面向服务架构(四十四)
层次架构设计 表现层、中间层、数据访问层、数据架构规划、物联网层次架构、层次式架构案例分析。 层次结构缺点就是效率问题,上一层调用下一层。 1、着重写中间层 组件设计:面向接口编程,分为接口和实现类。 实体设计:实体表…...
Ubuntu22无法自动进入lightdm图像界面
问题:Ubuntu22无法自动进入lightdm图像界面,必须手动运行 lightdm start解决方案: 方案一: 运行一个终端输入 cat /etc/X11/default-display-manager /etc/init/lightdm.conf不接受lightdm作为设置,但是,/…...
01BFS最短距离的原理和C++实现
时间复杂度 O(n),n是边数。 使用前提 边的权只有两种:0,1。 典型场景 n个端点的无向图,编号范围[0,n)。Edges0表示{{n1,n2},...{n3,n4}}表示n1和n2,n3和n4之间有路联接。Edges1表示{{n1,n2},...{n3,n4}}表示n1和n2,n3和n4之间…...
【洛谷 P5266】【深基17.例6】学籍管理 题解(映射+分支)
【深基17.例6】学籍管理 题目描述 您要设计一个学籍管理系统,最开始学籍数据是空的,然后该系统能够支持下面的操作(不超过 1 0 5 10^5 105 条): 插入与修改,格式1 NAME SCORE:在系统中插入姓…...
10.03
代码 #include <iostream>using namespace std; class cz { private:int num1; //实部int num2; //虚部 public:cz(){}cz(int a,int b):num1(a),num2(b){}cz(const cz &other):num1(other.num1),num2(other.num2){}~cz(){}const cz operator(const cz &othe…...
链表单向链表跳跃链表
单向链表 link list t数组的局限:编译期就需要知道大小; 内存连续,插入困难 // 链表节点类 包含一个信息 和指向下一个 节点的指针clas IntLLNode{public:IntLLNode(){// 默认构造函数 没有info信息nextPtr_ 0;// 空指针}IntLLNode(int …...
博客无限滚动加载(html、css、js)实现
介绍 这是一个简单实现了类似博客瀑布流加载功能的页面,使用html、css、js实现。简单易懂,值得学习借鉴。👍 演示地址:https://i_dog.gitee.io/easy-web-projects/infinite_scroll_blog/index.html 代码 index.html <!DOCT…...
腾讯云南京服务器性能如何?南京服务器测速IP地址
腾讯云服务器南京地域怎么样?南京地域很不错,正好处于中间的位置,南方北方用户均可以选择,网络延迟更低速度更快,并且目前南京地域有活动,南京地域可用区可选南京一区、南京二区和南京三区,腾讯…...
MySQL和Oracle中,语法的不同点以及如何在xml中书写日期比较大小
众所周知mysql和oracle的语法有点相识,又有点不同。 在MySQL和Oracle中,语法的不同点有以下几个方面: 数据类型:MySQL和Oracle支持的数据类型有所不同,比如MySQL支持的数据类型包括:整型、浮点型、字符型、…...
谈谈Redis分布式锁
目录 一、回顾分布式锁 (一)理解分布式锁的定义 (二)分布式锁的约束条件 (三)分布式锁常见实现方式 基于数据库的分布式锁 基于缓存的分布式锁 基于分布式一致性算法的分布式锁 基于文件系统的分布…...
Redis的java客户端-RedisTemplate光速入门
一.创建springboot项目 二.引入2个依赖 <!-- redis依赖-->这个已经引入了,因为创建的时候勾选了<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><…...
格点数据可视化(美国站点的日降雨数据)
获取美国站点的日降雨量的格点数据,并且可视化 导入模块 from datetime import datetime, timedelta from urllib.request import urlopenimport cartopy.crs as ccrs import cartopy.feature as cfeature import matplotlib.colors as mcolors import matplotli…...
YoloV8改进策略:LSKNet加入到YoloV8中,打造更适合小目标的YoloV8
文章目录 摘要论文:LSKNet:大选择核网络在遥感目标检测中的应用1、简介2、相关工作2.1、遥感目标检测框架2.2、大核网络2.3、注意力/选择机制3、方法3.1、LSKNet架构3.2、大核卷积3.3、空间核选择4、实验4.1、数据集4.2、实现细节4.3、消融实验4.4、主要结果4.5、分析5、结论…...
力扣-303.区域和检索-数组不可变
Idea 需计算数组nums在下标right 和 left-1 的前缀和,然后计算两个前缀和的差即可。 需要注意的是,当left为0的时候,如果还是left-1则会发生数组访问越界错误。 AC Code class NumArray { public:vector<int> sum;NumArray(vector<…...
web:[极客大挑战 2019]LoveSQL
题目 打开页面显示如下 查看源代码,查到一个check.php,还是get传参 尝试账号密码输入 题目名为sql,用万能密码 1or 11# 或 admin or 11 给了一段乱码,也不是flag 查看字段数 /check.php?usernameadmin order by 3%23&pass…...
数据结构—快速排序(续)
引言:在上一篇中我们详细介绍了快速排序和改进,并给出了其中的一种实现方式-挖坑法 但其实快速排序有多种实现方式,这篇文章再来介绍其中的另外两种-左右指针法和前后指针法。有了上一篇挖坑法的启示,下面的两种实现会容易许多。 …...
Snapdragon Profiler分析Android GPU
Snapdragon Profiler(骁龙分析器)是一款性能分析软件,在Windows、 Mac、和 Linux平台上都可以运行,主要是用来分析使用了高通骁龙处理器的Android设备。 Snapdragon Profiler通过USB连接这些Android设备,开发者可以用…...
Cannot download sources:IDEA源码无法下载
问题 Swagger的相关包,无法看到注释; 在class文件的页面,点击下载源码,源码下载不了,IDEA报下面的错误。 报错 Cannot download sources Sources not found for: io.swagger.core.v3:swagger-annotations:2.2.9 解决…...
从零开始学习 Java:简单易懂的入门指南之IO字符流(三十一)
IO流之字符流 1. 字符流1.1 字符输入流【Reader】1.2 FileReader类构造方法读取字符数据 1.3 字符输出流【Writer】1.4 FileWriter类构造方法基本写出数据关闭和刷新写出其他数据 2. IO异常的处理JDK7前处理JDK7的处理JDK9的改进 3. 综合练习练习1:拷贝文件夹练习2&…...
监狱工具管理系统-监狱劳动工具管理系统
监狱劳动工具管理系统(智工具DW-S308)是依托互3D技术、云计算、大数据、RFID技术、数据库技术、AI、视频分析技术对工具进行统一管理、分析的信息化、智能化、规范化的系统。 当前各级监狱工器具管理更多的是借助于传统的人工管理方法和手段,数据的采集和录入一直以…...
蓄水池算法
题目: 假设有一组数据流元素有 N 个(事先不知道 N 具体值),我们希望选择 n 个样本(N > n),使用怎样的策略进行抽样可以使得数据流中每个元素被选择的概率恰为 n / N 结论: 创建大…...
作业 day4
完成父子进程通信...
erlang练习题(四)
题目一 传入列表 L1[K|]、L2[V|]、L3[{K,V}|_],L1和L2一一对应,L1为键列表,L2为值列表,L3为随机kv列表, 将L1和L2对应位合并成KV列表L4,再将L3和L4相加,相同key的value相加 如:L…...
YoloV5实时推理最短的代码
YoloV5实时推理最简单代码 import cv2 import torch# 加载YOLOv5模型 model torch.hub.load(ultralytics/yolov5, yolov5s)# 使用CPU或GPU进行推理 device cuda if torch.cuda.is_available() else cpu model.to(device)# 打开摄像头(默认摄像头) cap…...
Tensorflow、Pytorch和Ray(张量,计算图)
1.深度学习框架(Tensorflow、Pytorch) 1.1由来 可以追溯到2016年,当年最著名的事件是alphago战胜人类围棋巅峰柯洁,在那之后,学界普遍认为人工智能已经可以在一些领域超过人类,未来也必将可以在更多领域超过…...
TinyWebServer学习笔记-让程序跑起来
目标:通过这个HTTP项目熟悉网络编程 系统:Ubuntu20.04 首先,学习的第一步就是先让程序跑起来,使用git将项目下载到虚拟机内: git clone https://github.com/qinguoyi/TinyWebServer.git 提前把MySQL数据库安装好&am…...
_tkinter.TclError: no display name and no $DISPLAY environment variable 解决
启动kohya_ss时可能会发生错误: _tkinter.TclError: no display name and no $DISPLAY environment variable 解决办法: 1、apt-get install xvfb //安装xvfb // 启动虚拟显示器 2、Xvfb :99 -screen 0 1024x768x16 & export DISPLAY:99 ps aux…...
网站做百科/公司做网络推广怎么做
点击下载:多功能导航Demo.rar 点击下载:发一个选项卡动态增删的效果.rar 点击下载:仿yahoo的特色服务内容切换效果.rar 点击下载:给图片加上边框[1].(仿胶卷).rar 点击下载:国外网站的一个效果.rar 点击下载࿱…...
网站建设的问题/站长工具的使用seo综合查询运营
目录 概述 1应用问题 1数据来源 2实现算法 3 4.1 软件界面 3 4.2 优化算法 4 4.3 实现细节 8实验结果与分析 9 5.1 目标函数值可视化 9 5.2 结果 9 5.2.1 暴力算法 10 5.2.2 梯度下降法 11 5.2.3 模拟退火算法 11 5.3 参数调整 12 5.4 分析与结论 12 1.概述 本次实验中&#…...
免费微网站模板/大型营销型网站制作
自己写过的程序,统计一下你写过多少行代码。包括空行和注释,但是要分别列出来1.打开文件方法1.1 以读文件的模式打开一个文件对象,使用Python内置的open()函数,传入文件名和标示符1.2 Python引入了with语句来自动帮我们调用close(…...
wordpress 主题大学/百度怎么投广告
时 间:2015-02-05 08:17:11作 者:摘 要:连接SQL Server 数据库出错的解决方案正 文:经常有人反映说SQL Server 客户端连接不上。现在将这类问题归纳如下:一、SQL Server 实例(服务)未启动打开“SQL Server 配置管理器”(或者“管理工具”中的“服务”)ÿ…...
wordpress 无广告视频插件下载/推广营销是什么
❑ 从 10.7.2 beta 11C6X 开始,启用 iCloud Find My Mac 功能,必须需要 Recovery HD 的存在。由于这个分区比较特殊,常规方法不能够创建它。导致很多同学为此,需要重装 OS X Lion。当然,重装是一个非常常规,…...
如何创建自己网站/交换链接名词解释
当把java项目打包成jar后,如何运行main函数呢? 第一种:指定运行类: 1 java -cp test.jar com.ming.test.Test 第二种:在MANIFEST.MF里配置了Main-Class,可以直接执行jar文件 Main-Class: com.ming.test.Test 然后打包执行以下命令…...