一、最简单的汇编程序

#PURPOSE: Simple program that exits and returns a
#     status code back to the Linux kernel
#
#INPUT:   none
#
#OUTPUT:  returns a status code. This can be viewed
#     by typing
#
#     echo $?
#
#     after running the program
#
#VARIABLES:
#     %eax holds the system call number
#     %ebx holds the return status
#.section .data.section .text.globl _start
_start:movl $1, %eax    # this is the linux kernel command# number (system call) for exiting# a programmovl $4, %ebx    # this is the status number we will# return to the operating system.# Change this around and it will# return different things to# echo $?int $0x80  # this wakes up the kernel to run# the exit command

将这个汇编文件转化为可执行文件需要执行以下两个步骤:首先将这个程序保存成文件hello.s(汇编程序通常以.s作为文件名后缀),用汇编器(Assembler)as把汇编程序中的助记符翻译成机器指令,生成目标文件hello.o:

as hello.s -o hello.o

然后用链接器(Linker,或Link Editor)ld把目标文件hello.o链接成可执行文件hello:

ld hello.o -o hello

链接主要有两个作用,一是修改目标文件中的信息,对地址做重定位,这在5.2中进行了详细解释,二是把多个目标文件合并成一个可执行文件,在第二节 main函数和启动例程中进行了详细解释。我们这个例子虽然只有一个目标文件,但也需要经过链接才能成为可执行文件。

现在执行这个程序,它只做了一件事就是退出,退出状态是4,第 2 节 “自定义函数”讲过在Shell中可以用特殊变量$?得到上一条命令的退出状态:

五、ELF文件

ELF文件格式是一个开放标准,各种UNIX系统的可执行文件都采用ELF格式,它有三种不同的类型:

  • 可重定位的目标文件(Relocatable,或者Object File)
  • 可执行文件(Executable)
  • 共享库(Shared Object,或者Shared Library)

共享库在第4节 共享库部分进行详细介绍,本节以例18.2 “求一组数的最大值的汇编程序”为例讨论目标文件和可执行文件的格式。现在详解解释下这个程序的汇编、链接、运行过程:

  1. 写一个汇编程序保存成文本文件max.s
  2. 汇编器读取这个文本文件转换成目标文件max.o,目标文件由若干个Section组成,我们在汇编程序中声明的.section会成为目标文件中的Section,此外汇编器还会自动添加一些Section(比如符号表)
  3. 然后链接器把目标文件中的Section合并成几个Segment,生成可执行文件max
  4. 最后加载器(Loader)根据可执行文件中的Segment信息加载运行这个程序

ELF格式提供了两种不同的视角,链接器把ELF文件看成是Section的集合,而加载器把ELF文件看成是Segment的集合。如下图所示。

图18.1 ELF文件

左边是从链接器的视角来看ELF文件,开头的ELF Header描述了体系结构和操作系统等基本信息,并指出Section Header Table和Program Header Table在文件中的什么位置,Program Header Table在链接过程中用不到,所以是可有可无的,Section Header Table中保存了所有Section的描述信息,通过Section Header Table可以找到每个Section在文件中的位置。右边是从加载器的视角来看ELF文件,开头是ELF Header,Program Header Table中保存了所有Segment的描述信息,Section Header Table在加载过程中用不到,所以是可有可无的。从上图可以看出,一个Segment由一个或多个Section组成,这些Section加载到内存时具有相同的访问权限。有些Section只对链接器有意义,在运行时用不到,也不需要加载到内存,那么就不属于任何Segment。注意Section Header Table和Program Header Table并不是一定要位于文件的开头和结尾,其位置由ELF Header指出,上图这么画只是为了清晰。

5.1 目标文件

下面用readelf工具读出目标文件max.o的ELF Header和Section Header Table,然后我们逐段分析。

$ readelf -a max.o
ELF Header:Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class:                             ELF32Data:                              2's complement, little endianVersion:                           1 (current)OS/ABI:                            UNIX - System VABI Version:                       0Type:                              REL (Relocatable file)Machine:                           Intel 80386Version:                           0x1Entry point address:               0x0Start of program headers:          0 (bytes into file)Start of section headers:          200 (bytes into file)Flags:                             0x0Size of this header:               52 (bytes)Size of program headers:           0 (bytes)Number of program headers:         0Size of section headers:           40 (bytes)Number of section headers:         8Section header string table index: 5
...

ELF Header中描述了操作系统是UNIX,体系结构是80386。Section Header Table中有8个Section Header,从文件地址200(0xc8)开始,每个Section Header占40字节,共320字节,到文件地址0x207结束。这个目标文件没有Program Header。文件地址是这样定义的:文件开头第一个字节的地址是0,然后每个字节占一个地址。

...
Section Headers:[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 00002a 00  AX  0   0  4[ 2] .rel.text         REL             00000000 0002b0 000010 08      6   1  4[ 3] .data             PROGBITS        00000000 000060 000038 00  WA  0   0  4[ 4] .bss              NOBITS          00000000 000098 000000 00  WA  0   0  4[ 5] .shstrtab         STRTAB          00000000 000098 000030 00      0   0  1[ 6] .symtab           SYMTAB          00000000 000208 000080 10      7   7  4[ 7] .strtab           STRTAB          00000000 000288 000028 00      0   0  1
Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings)I (info), L (link order), G (group), x (unknown)O (extra OS processing required) o (OS specific), p (processor specific)There are no section groups in this file.There are no program headers in this file.
...

从Section Header中读出各Section的描述信息,其中.text和.data是我们在汇编程序中声明的Section,而其它Section是汇编器自动添加的。Addr是这些段加载到内存中的地址(我们讲过程序中的地址都是虚拟地址),加载地址要在链接时填写,现在空缺,所以是全0。Off和Size两列指出了各Section的文件地址,比如.data段从文件地址0x60开始,一共0x38个字节,回去翻一下程序,.data段定义了14个4字节的整数,一共是56个字节,也就是0x38。根据以上信息可以描绘出整个目标文件的布局。

表18.1 目标文件的布局

起始文件地址 Section或Header
起始文件地址 Section或Header
0 ELF Header
0x34 .text
0x60 .data
0x98 .bss(此段为空)
0x98 .shstrtab
0xc8 Section Header Table
0x208 .symtab
0x288 .strtab
0x2b0 .rel.text

这个文件不大,我们直接用hexdump工具把目标文件的字节全部打印出来看。

$ hexdump -C max.o
00000000  7f 45 4c 46 01 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  01 00 03 00 01 00 00 00  00 00 00 00 00 00 00 00  |................|
00000020  c8 00 00 00 00 00 00 00  34 00 00 00 00 00 28 00  |........4.....(.|
00000030  08 00 05 00 bf 00 00 00  00 8b 04 bd 00 00 00 00  |................|
00000040  89 c3 83 f8 00 74 10 47  8b 04 bd 00 00 00 00 39  |.....t.G.......9|
00000050  d8 7e ef 89 c3 eb eb b8  01 00 00 00 cd 80 00 00  |.~..............|
00000060  03 00 00 00 43 00 00 00  22 00 00 00 de 00 00 00  |....C...".......|
00000070  2d 00 00 00 4b 00 00 00  36 00 00 00 22 00 00 00  |-...K...6..."...|
00000080  2c 00 00 00 21 00 00 00  16 00 00 00 0b 00 00 00  |,...!...........|
00000090  42 00 00 00 00 00 00 00  00 2e 73 79 6d 74 61 62  |B.........symtab|
000000a0  00 2e 73 74 72 74 61 62  00 2e 73 68 73 74 72 74  |..strtab..shstrt|
000000b0  61 62 00 2e 72 65 6c 2e  74 65 78 74 00 2e 64 61  |ab..rel.text..da|
000000c0  74 61 00 2e 62 73 73 00  00 00 00 00 00 00 00 00  |ta..bss.........|
000000d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000000f0  1f 00 00 00 01 00 00 00  06 00 00 00 00 00 00 00  |................|
00000100  34 00 00 00 2a 00 00 00  00 00 00 00 00 00 00 00  |4...*...........|
00000110  04 00 00 00 00 00 00 00  1b 00 00 00 09 00 00 00  |................|
00000120  00 00 00 00 00 00 00 00  b0 02 00 00 10 00 00 00  |................|
00000130  06 00 00 00 01 00 00 00  04 00 00 00 08 00 00 00  |................|
00000140  25 00 00 00 01 00 00 00  03 00 00 00 00 00 00 00  |%...............|
00000150  60 00 00 00 38 00 00 00  00 00 00 00 00 00 00 00  |`...8...........|
00000160  04 00 00 00 00 00 00 00  2b 00 00 00 08 00 00 00  |........+.......|
00000170  03 00 00 00 00 00 00 00  98 00 00 00 00 00 00 00  |................|
00000180  00 00 00 00 00 00 00 00  04 00 00 00 00 00 00 00  |................|
00000190  11 00 00 00 03 00 00 00  00 00 00 00 00 00 00 00  |................|
000001a0  98 00 00 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
000001b0  01 00 00 00 00 00 00 00  01 00 00 00 02 00 00 00  |................|
000001c0  00 00 00 00 00 00 00 00  08 02 00 00 80 00 00 00  |................|
000001d0  07 00 00 00 07 00 00 00  04 00 00 00 10 00 00 00  |................|
000001e0  09 00 00 00 03 00 00 00  00 00 00 00 00 00 00 00  |................|
000001f0  88 02 00 00 28 00 00 00  00 00 00 00 00 00 00 00  |....(...........|
00000200  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000210  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000220  00 00 00 00 03 00 01 00  00 00 00 00 00 00 00 00  |................|
00000230  00 00 00 00 03 00 03 00  00 00 00 00 00 00 00 00  |................|
00000240  00 00 00 00 03 00 04 00  01 00 00 00 00 00 00 00  |................|
00000250  00 00 00 00 00 00 03 00  0c 00 00 00 0e 00 00 00  |................|
00000260  00 00 00 00 00 00 01 00  17 00 00 00 23 00 00 00  |............#...|
00000270  00 00 00 00 00 00 01 00  21 00 00 00 00 00 00 00  |........!.......|
00000280  00 00 00 00 10 00 01 00  00 64 61 74 61 5f 69 74  |.........data_it|
00000290  65 6d 73 00 73 74 61 72  74 5f 6c 6f 6f 70 00 6c  |ems.start_loop.l|
000002a0  6f 6f 70 5f 65 78 69 74  00 5f 73 74 61 72 74 00  |oop_exit._start.|
000002b0  08 00 00 00 01 02 00 00  17 00 00 00 01 02 00 00  |................|
000002c0

左边一列是文件地址,中间是每个字节的十六进制表示,右边是把这些字节解释成ASCII码所对应的字符。中间有一个*号表示省略的部分全是0。.data段对应的是这一块:

...
00000060  03 00 00 00 43 00 00 00  22 00 00 00 de 00 00 00  |....C...".......|
00000070  2d 00 00 00 4b 00 00 00  36 00 00 00 22 00 00 00  |-...K...6..."...|
00000080  2c 00 00 00 21 00 00 00  16 00 00 00 0b 00 00 00  |,...!...........|
00000090  42 00 00 00 00 00 00 00
...

.data段将被原封不动地加载到内存中,下一小节会看到.data段被加载到内存地址0x080490a0~0x080490d7。

.shstrtab和.strtab这两个Section中存放的都是ASCII码:

...00 2e 73 79 6d 74 61 62  |B.........symtab|
000000a0  00 2e 73 74 72 74 61 62  00 2e 73 68 73 74 72 74  |..strtab..shstrt|
000000b0  61 62 00 2e 72 65 6c 2e  74 65 78 74 00 2e 64 61  |ab..rel.text..da|
000000c0  74 61 00 2e 62 73 73 00                           |ta..bss.........|
...00 64 61 74 61 5f 69 74  |.........data_it|
00000290  65 6d 73 00 73 74 61 72  74 5f 6c 6f 6f 70 00 6c  |ems.start_loop.l|
000002a0  6f 6f 70 5f 65 78 69 74  00 5f 73 74 61 72 74 00  |oop_exit._start.|
...

可见.shstrtab段保存着各Section的名字,.strtab段保存着程序中用到的符号的名字。每个名字都是以’\0’结尾的字符串。

我们知道,C语言的全局变量如果在代码中没有初始化,就会在程序加载时用0初始化。这种数据属于.bss段,在加载时它和.data段一样都是可读可写的数据,但是在ELF文件中.data段需要占用一部分空间保存初始值,而.bss段则不需要。也就是说,.bss段在文件中只占一个Section Header而没有对应的Section,程序加载时.bss段占多大内存空间在Section Header中描述。在我们这个例子中没有用到.bss段,在第3节 变量的存储布局会看到这样的例子。

我们继续分析readelf输出的最后一部分,是从.rel.text和.symtab这两个Section中读出的信息。

...
Relocation section '.rel.text' at offset 0x2b0 contains 2 entries:Offset     Info    Type            Sym.Value  Sym. Name
00000008  00000201 R_386_32          00000000   .data
00000017  00000201 R_386_32          00000000   .dataThere are no unwind sections in this file.Symbol table '.symtab' contains 8 entries:Num:    Value  Size Type    Bind   Vis      Ndx Name0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 1: 00000000     0 SECTION LOCAL  DEFAULT    1 2: 00000000     0 SECTION LOCAL  DEFAULT    3 3: 00000000     0 SECTION LOCAL  DEFAULT    4 4: 00000000     0 NOTYPE  LOCAL  DEFAULT    3 data_items5: 0000000e     0 NOTYPE  LOCAL  DEFAULT    1 start_loop6: 00000023     0 NOTYPE  LOCAL  DEFAULT    1 loop_exit7: 00000000     0 NOTYPE  GLOBAL DEFAULT    1 _startNo version information found in this file.

.rel.text告诉链接器指令中的哪些地方需要做重定位,在下一小节详细讨论。

.symtab是符号表。Ndx列是每个符号所在的Section编号,例如符号data_items在第3个Section里(也就是.data段),各Section的编号见Section Header Table。Value列是每个符号所代表的地址,在目标文件中,符号地址都是相对于该符号所在Section的相对地址,比如data_items位于.data段的开头,所以地址是0,_start位于.text段的开头,所以地址也是0,但是start_loop和loop_exit相对于.text段的地址就不是0了。从Bind这一列可以看出_start这个符号是GLOBAL的,而其它符号是LOCAL的,GLOBAL符号是在汇编程序中用.globl指示声明过的符号。

现在剩下.text段没有分析,objdump工具可以把程序中的机器指令反汇编(Disassemble),那么反汇编的结果是否跟原来写的汇编代码一模一样呢?我们对比分析一下。

$ objdump -d max.omax.o:     file format elf32-i386Disassembly of section .text:00000000 <_start>:0:   bf 00 00 00 00          mov    $0x0,%edi5:  8b 04 bd 00 00 00 00    mov    0x0(,%edi,4),%eaxc:  89 c3                   mov    %eax,%ebx0000000e <start_loop>:e:  83 f8 00                cmp    $0x0,%eax11: 74 10                   je     23 <loop_exit>13:  47                      inc    %edi14:  8b 04 bd 00 00 00 00    mov    0x0(,%edi,4),%eax1b: 39 d8                   cmp    %ebx,%eax1d: 7e ef                   jle    e <start_loop>1f:  89 c3                   mov    %eax,%ebx21: eb eb                   jmp    e <start_loop>00000023 <loop_exit>:23:   b8 01 00 00 00          mov    $0x1,%eax28: cd 80                   int    $0x80

左边是机器指令的字节,右边是反汇编结果。显然,所有的符号都被替换成地址了,比如je 23,注意没有加$的数表示内存地址,而不表示立即数。这条指令后面的<loop_exit>并不是指令的一部分,而是反汇编器从.symtab和.strtab中查到的符号名称,写在后面是为了有更好的可读性。目前所有指令中用到的符号地址都是相对地址,下一步链接器要修改这些指令,把其中的地址都改成加载时的内存地址,这些指令才能正确执行。

5.2 可执行文件

现在我们按上一节的步骤分析可执行文件max,看看链接器都做了什么改动。

$ readelf -a max
ELF Header:Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class:                             ELF32Data:                              2's complement, little endianVersion:                           1 (current)OS/ABI:                            UNIX - System VABI Version:                       0Type:                              EXEC (Executable file)Machine:                           Intel 80386Version:                           0x1Entry point address:               0x8048074Start of program headers:          52 (bytes into file)Start of section headers:          256 (bytes into file)Flags:                             0x0Size of this header:               52 (bytes)Size of program headers:           32 (bytes)Number of program headers:         2Size of section headers:           40 (bytes)Number of section headers:         6Section header string table index: 3Section Headers:[Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al[ 0]                   NULL            00000000 000000 000000 00      0   0  0[ 1] .text             PROGBITS        08048074 000074 00002a 00  AX  0   0  4[ 2] .data             PROGBITS        080490a0 0000a0 000038 00  WA  0   0  4[ 3] .shstrtab         STRTAB          00000000 0000d8 000027 00      0   0  1[ 4] .symtab           SYMTAB          00000000 0001f0 0000a0 10      5   6  4[ 5] .strtab           STRTAB          00000000 000290 000040 00      0   0  1
Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings)I (info), L (link order), G (group), x (unknown)O (extra OS processing required) o (OS specific), p (processor specific)There are no section groups in this file.Program Headers:Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg AlignLOAD           0x000000 0x08048000 0x08048000 0x0009e 0x0009e R E 0x1000LOAD           0x0000a0 0x080490a0 0x080490a0 0x00038 0x00038 RW  0x1000Section to Segment mapping:Segment Sections...00     .text 01     .data There is no dynamic section in this file.There are no relocations in this file.There are no unwind sections in this file.Symbol table '.symtab' contains 10 entries:Num:    Value  Size Type    Bind   Vis      Ndx Name0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 1: 08048074     0 SECTION LOCAL  DEFAULT    1 2: 080490a0     0 SECTION LOCAL  DEFAULT    2 3: 080490a0     0 NOTYPE  LOCAL  DEFAULT    2 data_items4: 08048082     0 NOTYPE  LOCAL  DEFAULT    1 start_loop5: 08048097     0 NOTYPE  LOCAL  DEFAULT    1 loop_exit6: 08048074     0 NOTYPE  GLOBAL DEFAULT    1 _start7: 080490d8     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start8: 080490d8     0 NOTYPE  GLOBAL DEFAULT  ABS _edata9: 080490d8     0 NOTYPE  GLOBAL DEFAULT  ABS _endNo version information found in this file.

在ELF Header中,Type改成了EXEC,由目标文件变成可执行文件了,Entry point address改成了0x8048074(这是_start符号的地址),还可以看出,多了两个Program Header,少了两个Section Header。

在Section Header Table中,.text和.data段的加载地址分别改成了0x08048074和0x080490a0。.bss段没有用到,所以被删掉了。.rel.text段就是用于链接过程的,做完链接就没用了,所以也删掉了。

多出来的Program Header Table描述了两个Segment的信息。.text段和前面的ELF Header、Program Header Table一起组成一个Segment(FileSiz指出总长度是0x9e),.data段组成另一个Segment(总长度是0x38)。VirtAddr列指出第一个Segment加载到虚拟地址0x08048000(注意在x86平台上后面的PhysAddr列是没有意义的,并不代表实际的物理地址),第二个Segment加载到地址0x080490a0。Flg列指出第一个Segment的访问权限是可读可执行,第二个Segment的访问权限是可读可写。最后一列Align的值0x1000(4K)是x86平台的内存页面大小。在加载时文件也要按内存页面大小分成若干页,文件中的一页对应内存中的一页,对应关系如下图所示。

十八、x86汇编基础相关推荐

  1. 视频教程-三十八课时零基础matlab精通优化算法-Matlab

    三十八课时零基础matlab精通优化算法 图像和算法等领域有多年研究和项目经验:指导发表科技核心期刊经验丰富:多次指导数学建模爱好者参赛. 宋星星 ¥100.00 立即订阅 扫码下载「CSDN程序员学 ...

  2. 2021年大数据常用语言Scala(十八):基础语法学习 Map对象

    目录 Map对象 不可变Map 可变Map Map基本操作 Map对象 Map可以称之为映射.它是由键值对组成的集合.在scala中,Map也分为不可变Map和可变Map. 不可变Map 定义 语法 ...

  3. 二十八、JDBC基础编程练习

    第1关:JDBC更新员工密码 任务描述 本关任务:借助JDBC在库名tsgc中完成对数据表employee中性别为"女"的员工密码修改为"hello": 表结构 ...

  4. Kubernetes生产实践系列之二十九:Kubernetes基础技术之容器关键技术实践

    一.前言 在文章<Kubernetes生产实践系列之二十八:Kubernetes基础技术之容器关键技术介绍>中,对于Docker容器技术依赖的namespace.cgroup和UnionF ...

  5. 最全的MySQL基础【燕十八传世】

    1.课前准备! 开启mysql服务:1).配置环境变量;2).net start mysql 将该sql文件导入到你的数据库中,以下所有操作都是基于该数据库表操作的!!! [此笔记是本人看着视频加上自 ...

  6. 【零基础学Java】—自定义异常(四十八)

    [零基础学Java]-自定义异常(四十八) 自定义异常类:java提供的异常类,不够我们使用,需要自己定义一个异常类 格式: public class XXXException extends Exc ...

  7. 【零基础学Java】—笔记本USB接口案例(二十八)

    [零基础学Java]-笔记本USB接口案例(二十八) 一.笔记本电脑 笔记本电脑(laptop)通常具备使用USB设备的功能,在生产时,笔记本都预留了可以插入USB设备的USB接口,但具体什么是USB ...

  8. 零基础带你学习MySQL—分页查询(十八)

    零基础带你学习MySQL-分页查询(十八)

  9. php燕十八作业,布尔教育燕十八PHP基础巩固视频教程的源码分享

    PHP("超文本预处理器")是一种通用开源脚本语言.语法吸收了C语言.Java和Perl的特点,利于学习,使用广泛,主要适用于Web开发领域.PHP 独特的语法混合了C.Java. ...

  10. 燕十八ajax笔记,燕十八php視频教程笔记(PHP基础部分).doc

    燕十八php視频教程笔记(PHP基础部分) 001-开学典礼 002-变量概念及命名规范 <?php //?$a?是钥匙[107房间],?房间里的内容是值, /* echo?$a,?什么过程? ...

最新文章

  1. 大数据框架对比:Hadoop、Storm、Samza、Spark和Flink——flink支持SQL,待看
  2. Java Threads 多线程10分钟参考手册
  3. 站长必看系列:完全揭密百度和谷歌收录规律
  4. [html] 用一个div模拟textarea的实现
  5. MySQL求类型为GX的平均积分_mysql基本操作
  6. 可以批量转modis投影_SNAP批量处理Sentinel2数据
  7. javascript 西瓜一期 14 回顾 字符与编码 进制转换 数据保存
  8. 使用Python开发SQLite代理服务器
  9. 轧机用弹性阻尼体反力计算_「轴承知识」轧机轴承故障频繁怎么办?必要的检查维护不可少...
  10. 有钱人也开始消费降级了!
  11. win10 联想键盘快捷键关闭_如何关闭联想台式机电脑USB键盘的FN功能
  12. 联想集团杨元庆:联想携手SAP解决用户云计算应用痛点
  13. python 动态执行 内存变化_深入理解python数组的动态扩容机制以及摊销分析
  14. Win10投影到此电脑用不了
  15. IR2104与IR2103区别
  16. 剑指Offe6-反转链表
  17. 写一个自动回复的聊天机器人
  18. MySQL:安装软件和基本操作
  19. c语言的中打印出鸟图形,教你画出一只萌萌哒小鸟【PS教程】
  20. 用公式π/ 4 = 1 – 1 / 3 + 1 / 5 – 1 / 7 + … 求π的近似值,直到最后一项的绝对值小于10的负6次方为止。

热门文章

  1. UDP通信的简单实现(程序)
  2. wamp mysql中文_解决wampServer 中MySQL插入中文乱码问题
  3. android Sqlite操作之-- 自定义ORM关系实体映射类
  4. Android的JNI【实战教程】4⃣️--C调用Java代码
  5. webstrom html校验css语法,webstorm ECMAScript 6 语法检查
  6. mysql 加锁 索引_把MySQL中的各种锁及其原理都画出来
  7. 用JAVA打出的计算机_如何用Java代码操作计算机文件?
  8. easydarwin 安装_在Linux平台上搭建EasyDarwin,编译代码并简单部署
  9. 会展管理系统是计算机软件系统,展商管理系统|会展管理信息系统
  10. ebay 后台HTML有尺寸宽度要求吗,eBay牛仔裤成为尺码问题重灾区,卖家上架需注意这几个方面...