CSAPP第七章学习笔记_csapp第七章笔记-程序员宅基地

技术标签: 计算机系统  

第7章 链接

链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载到内存并执行。

7.2 静态链接

链接器必须完成的两个任务:

  1. 符号解析,将每个符号引用与符号定义关联。
  2. 重定位,将符号定义和内存位置关联,再修改所有符号的引用,使其指向指定内存位置。

7.3 目标文件

三种形式:

  1. 可重定位目标文件,包含二进制代码和数据的文件,可与其他可重定位文件合并,生成可执行目标文件。

  2. 可执行目标文件,包含二进制代码和数据的文件,可直接复制至内存并执行。

  3. 共享目标文件,特殊的可重定位目标文件,可在加载或运行时动态地加载至内存并链接。

编译器和汇编器生成可重定位目标文件,链接器生成可执行目标文件。

7.4 可重定位目标文件

典型的ELF可重定位目标文件的格式,如下:
在这里插入图片描述

图1 典型的ELF可重定位目标文件
ELF中各节功能及意义:

类型 功能及意义
ELF头 以16字节序列开始,描述生成该文件的系统的字的大小和字节顺序
.text 已编译程序的机器代码
.rodata 只读数据
.data 已初始化的全局和静态变量
.bas 未初始化的全局和静态变量
.symtab 存放程序中定义和引用的函数和全局变量的信息的符号表
.rel .text .text节中位置列表,在组合目标文件和其他文件时,需修改这些位置
.rel .data 被模块引用或定义的所有全局变量的重定位信息
.debug 调试符号表,包含程序中定义的局部变量、定义和引用的全局变量以及原始的C源文件
.line 原始C源程序中的行号和.text节中机器指令之间的映射
.strtab 字符串表,包括.symtab和.debug节中的符号表,以及节头部中的节名字 节点部表

7.5 符号和符号表

链接的上下文中,三种不同的符号:

  1. 模块自身定义并能被其他模块引用的全局符号,如非静态的C函数的全局变量。
  2. 引用其他模块定义的全局符号,即外部符号。
  3. 模块定义和引用的局部符号,如带static属性的C函数和全局变量。

符号表由汇编器构造,.symtab节中包含ELF符号表,符号表包含一个条目的数据,每个条目的格式如下:

typedef struct {
    
	int name;			/*字符串表中的字节偏移*/
	char type : 4,		/*函数或数据 4字节*/
		binding : 4;	/*本地或全局 4字节*/
	char reserved;		/*未定义的符号*/
	short section;		/*节头部表的索引,指定分配到目标文件的某个节*/
	long value;			/*距定义目标的节的起始位置偏移*/
	long size;			/*目标的大小*/
} Elf64_Symbol;

7.6 符号解析

链接器解析符号是将每个引用与它输入的可重定位目标文件的符号表中的一个确定的符号定义关联起来。

解析多重定义的全局符号规则

  1. 不允许有同名的强符号,函数和已初始化的全局变量为强符号,未初始化的全局变量为弱符号。
  2. 若有一个强符号和多个弱符号同名,则选择强符号
  3. 若多个弱符号同名,则随机选择一个

与静态库链接
将所有相关的目标模块打包成一个单独的文件,称为静态库。

相关的函数可以被编译为独立的目标模块,然后封装成一个单独的静态库文件。
链接时,链接器只复制被程序引用的目标模块,从而减少了可执行文件在磁盘和内存中的大小。

静态库使用示例:

  1. main.c
#include <stdio.h>
#include "vector.h"
#include "windows.h"

int x[2] = {
     1, 2 };
int y[2] = {
     3, 4 };
int z[2];

int main() {
    
	addvec(x, y, z, 2);
	printf("z=[%d %d]\n", z[0], z[1]);
	system("pause");
	return 0;
}
  1. vector.h
void addvec(int*, int*, int*, int);
void multivec(int*, int*, int*, int);
  1. addvec.c
int addcnt = 0;

void multivec(int* x, int* y, int* z, int n) {
    
	int i;

	addcnt++;
	for (i = 0; i < n; i++)
		z[i] = x[i] + y[i];
}
  1. multivec.c
int multicnt = 0;

void addvec(int* x, int* y, int* z, int n) {
    
	int i;

	multicnt++;
	for (i = 0; i < n; i++)
		z[i] = x[i] * y[i];
}

分别执行以下指令,则生成可执行目标文件file。

 gcc -c addvec.c multivec.c
 ar rcs libvector.a addvec.o multivec.o
 gcc -c main.c
 gcc -static -o file main.c libvector.a

链接器行为如下图所示:
在这里插入图片描述
图2 与静态库链接

链接器如何使用静态库来解析引用
符号解析阶段,链接器从左到右按照命令行上出现的次序来扫描可重定位目标文件和存档文件。
链接器维护一个可重定位目标文件集合 E E E,一个未解析符号集合 U U U,一个在前面输入文件已经定义的符号集 D D D。初始时,各集合全空。

  • 若扫描到目标文件 f f f,则链接器将 f f f添加到 E E E,修改 U U U D D D来反应 f f f中的符号定义和引用。
  • 若扫描到存档文件(静态库) f f f,则链接器尝试匹配 U U U中未定义的符号和由存档文件定义的符号。若某存档成员 m m m定义了 U U U中的符号,则将 m m m添加至 E E E,并修改 U U U D D D。对存档成员目标文件,依次执行上述过程。
  • 若扫描所有输入文件后, U U U是非空的,则抛出异常。否则,合并和重定位 E E E中目标文件,生成可执行目标文件。

因此,命令行上库和目标文件的次序非常重要。因保证定义一个符号的库在引用这个符号的目标文件之后。
如foo.c调用libx.a中的函数,该库又调用liby.a中的函数,而liby.a有调用libx.a中的函数,则命令行格式为:

 gcc foo.c libx.a liby.a libx.a

即libx.a需重复出现,亦可将libx.a和liby.a合并。

7.7 重定位

重定位就是把程序的逻辑地址空间变换成内存中的实际物理地址空间的过程。

完成符号解析后,代码中的每个符号和一个符号定义完成关联,此时链接器开始重定位。

  • 重定位节和符号定义
    将所有相同类型的节合并为同一类型的新的聚合节。如所有输入模块的.data节被合并为新的.data节,并完成内存地址的赋值。
  • 重定义节中的符号引用 修改代码节和数据节中对每个符号的引用,使其指向正确的内存地址。

重定位条目
汇编器生成目标模块时,对数据和代码在内存中的位置、模块引用的外部定义的函数或全局变量的位置均未知。对这些未知的引用都会生成一个重定位条目,用于指导链接器在合并阶段如何修改这个引用。

ELF重定位条目的格式:

typedef struct {
    
	long offset;		/*需要被修改的引用的节偏移*/
	long type : 32,		/*告知链接器如何修改新的引用*/
		symbol : 32;	/*符号表索引*/
	long addend;		/*有符号常数,对修改引用的偏移做调整*/
}Elf64_Rela;

两种基本的重定位类型:

  1. R _ X 86 _ 64 _ P C 32 R\_X86\_64\_PC32 R_X86_64_PC32:重定位一个使用32位PC相对地址的引用。
  2. R _ X 86 _ 64 _ 32 R\_X86\_64\_32 R_X86_64_32:重定位一个使用32位绝对地址的引用。

重定位符号引用
假设每个节s是一个字节数组,每个重定位条目r是一个类型为Elf64_Rela的结构。
重定位符号引用时,链接器已经为每个节(ADDR(s))和每个符号(ADDR(r.symbol))都选择了运行时的地址。

伪重定位算法:

refptr = s + r.offset;
if (r.type == R_X86_64_PC32) {
    
	refaddr = ADDR(s) + r.offset;
	*refptr = (unsigned)(ADDR(r.symbol) + r.addend - refaddr);
}
if (r.type == R_X86_64_32)
	*refptr = (unsigned)(ADDR(r.symbol) + r.addend);

重定位如下实例程序的引用:

  1. main.c
1	int sum(int* a, int n);
2
3	int array[2] = {
    1, 2};
4
5	int main() {
    
6	int val = sum(array, 2);
7	return val;
8	}
  1. sum.c
1	int sum(int* a, int n) {
    
2		int i, s = 0;
3
4		for(i = 0; i < n; i++)
5			s += a[i];
6		}
7		return s;
8	}

main.o的反汇编代码:

// main.o
1  0000000000000000 <main>:
2    0:  48 83 ec 08              sub   $0x8, %rsp
3    4:  be 02 00 00 00           mov   $0x2, %esi	
4    9:  bf 00 00 00 00           mov   $0x0, %edi
5                           a: R_X86_64_32 array

6    e:  e8 00 00 00 00           callq 13 <main+0x13>
7                           f: R_X86_64_PC32 sum-0x4
8    13: 48 83 c4 08              add   $0x8, %rsp
9    17: c3                       retq
  1. 重定位PC相对引用
    函数main调用sum函数,sum在sum.o中定义。
    已知main首地址
    A D D R ( s ) = A D D R ( . t e x t ) = 0 × 4004 d 0 ADDR(s)=ADDR(.text)=0\times4004d0 ADDR(s)=ADDR(.text)=0×4004d0
    s u m sum sum函数地址
    A D D R ( r . s y m b o l ) = A D D R ( s u m ) = 0 × 4004 e 8 ADDR(r.symbol)=ADDR(sum)=0\times4004e8 ADDR(r.symbol)=ADDR(sum)=0×4004e8
    对于sum条目有:
    r . o f f s e t = 0 x f , r . s y m b o l = s u m , r . t y p e = R _ X 86 _ 64 _ P C 32 , r . a d d e n d = − 4 r.offset=0xf,r.symbol=sum,r.type=R\_X86\_64\_PC32,r.addend=−4 r.offset=0xf,r.symbol=sum,r.type=R_X86_64_PC32,r.addend=4

链接器修改从偏移量0xf开始的32位PC相对引用,使程序指向sum入口地址。:
   r e f a d d r =    A D D R ( s )    +    r . o f f s e t                         =    0 × 4004 d 0    +    0 × f                         =    0 × 4004 d f \,\,\begin{array}{l} refaddr=\,\,ADDR\left( s \right) \,\,+\,\,r.offset\\ \,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\ \ =\,\,0\times 4004d0\,\,+\,\,0\times f\\ \,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\ \ =\,\,0\times 4004df\\ \end{array} refaddr=ADDR(s)+r.offset  =0×4004d0+0×f  =0×4004df
   ∗ r e f a d d r =    ( u n s i g n e d )   ( A D D R ( r . s y m b o l )   +   r . a d d e n d   −   r e f a d d r )                            =    ( u n s i g n e d )   ( 0 × 4004 e 8           +       ( − 4 )       −   0 × 4004 d f )                              =    ( u n s i g n e d )   ( 0 × 5 ) \,\,\begin{array}{l} *refaddr=\,\,\left( unsigned \right) \ \left( ADDR\left( r.symbol \right) \ +\ r.addend\ -\ refaddr \right)\\ \,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,=\,\,\left( unsigned \right) \ \left( 0\times 4004e8\ \ \ \ \ \ \ \ \ +\,\,\,\,\, \left( -4 \right) \ \ \ \ \ -\ 0\times 4004df \right) \,\\ \,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,=\,\,\left( unsigned \right) \ \left( 0\times 5 \right)\\ \end{array} refaddr=(unsigned) (ADDR(r.symbol) + r.addend  refaddr)=(unsigned) (0×4004e8         +(4)      0×4004df)=(unsigned) (0×5)

得到下面的重定位形式:

4004de:	e8 05 00 00 00		callq 4004e8 <sum>

运行时,call指令存放在 0 × 4004 d e 0\times4004de 0×4004de处,CPU执行call指令时,PC指向下一条指令即 0 × 4004 e 3 0\times4004e3 0×4004e3。由于相对地址偏移位 0 × 5 0\times5 0×5,读PC新值为 0 × 4004 e 3 + 0 × 5 = 0 × 4004 e 8 0\times4004e3 + 0\times5 = 0\times4004e8 0×4004e3+0×5=0×4004e8,刚好指向sum入口地址。

2.重定位绝对引用
对于array条目:
                 r . o f f s e t    =   0 × a                r . s y m b o l   =   a r r a y                            r . t y p e =   R _ X 86 _ 64 _ 32 r . a d d e n d   =   0 \,\begin{matrix}{} \,\,\,\,\,\,\,\,\,\,\,\,\,\,r.offset\ \ =\ 0\times a\\ \,\,\,\,\,\,\,\,\,\,\,\,\,\,r.symbol\ =\ array\\ \,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,r.type =\ R\_X86\_64\_32\\ r.addend\ =\ 0\\ \end{matrix} r.offset  = 0×ar.symbol = arrayr.type= R_X86_64_32r.addend = 0
已知 m a i n main main首地址 A D D R ( s )   =   A D D R ( . t e x t )   =   0 × 4004 d 0 ADDR\left( s \right) \ =\ ADDR\left( .text \right) \ =\ 0\times 4004d0 ADDR(s) = ADDR(.text) = 0×4004d0 a r r a y array array首地址 A D D R ( r . s y m b o l )   =   A D D R ( a r r a y )   =   0 × 601018 ADDR\left( r.symbol \right) \ =\ ADDR\left( array \right) \ =\ 0\times 601018 ADDR(r.symbol) = ADDR(array) = 0×601018

链接器修改从偏移量 0 × 0\times 0×开始的绝对引用,使程序指向 a r r a y array array的第一个字节。

   r e f a d d r =    A D D R ( s )    +    r . o f f s e t                         =    0 × 4004 d 0    +    0 × a                         =    0 × 4004 d a \,\,\begin{array}{l} refaddr=\,\,ADDR\left( s \right) \,\,+\,\,r.offset\\ \,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\ \ =\,\,0\times 4004d0\,\,+\,\,0\times a\\ \,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\ \ =\,\,0\times 4004da\\ \end{array} refaddr=ADDR(s)+r.offset  =0×4004d0+0×a  =0×4004da
   ∗ r e f a d d r =    ( u n s i g n e d )   ( A D D R ( r . s y m b o l )   +   r . a d d e n d   )                            =    ( u n s i g n e d )   ( 0 × 601018           +       0    )                              =    ( u n s i g n e d )   ( 0 × 601018 ) \,\,\begin{array}{l} *refaddr=\,\,\left( unsigned \right) \ \left( ADDR\left( r.symbol \right) \ +\ r.addend\ \right)\\ \,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,=\,\,\left( unsigned \right) \ \left( 0\times 601018\ \ \ \ \ \ \ \ \ +\,\,\,\,\, 0 \ \ \right) \,\\ \,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,=\,\,\left( unsigned \right) \ \left( 0\times 601018 \right)\\ \end{array} refaddr=(unsigned) (ADDR(r.symbol) + r.addend )=(unsigned) (0×601018         +0  )=(unsigned) (0×601018)

得到下面的重定位形式:

4004d9:	bf 18 10 60 00		mov $0x601018, %edi

已重定位的.text节,如下:

1   00000000004004d0 <main>:
2    4004d0:  48 83 ec 08                 sub    $0x8, %rsp
3    4004d4:  be 02 00 00 00              mov    $0x2, %esi
4    4004d9:  bf 18 10 60 00              mov    $0x601018, %edi
5    4004de:  e8 05 00 00 00              callq  4004e8 <sum>
6    4004e3:  48 83 c4 08                 add    $0x8, %rsp
7    4004e7:  c3                          retq

8  00000000004004e8 <sum>:
9    4004e8:  b8 00 00 00 00              mov    $0x0, %eax
10   4004ed:  ba 00 00 00 00              mov    $0xx, %edx
11   4004f2:  eb 09                       jmp    4004fd <sum+0x15>
12   4004f4:  48 63 ca                    movslq %edx, %rcx
13   4004f7:  03 04 8f                    add    (%rdi, %rcx, 4), %eax
14   4004fa:  83 c2 01                    add    $0x1, %edx
15   4004fd:  39 f2                       cmp    %esi, %edx
16   4004ff:  7c f3                       jl     4004f4 <sum+0xc>
17   400501:  f3 c3                       repz retq

已重定位的.data节,如下:

1  000000000601018 <array>:
2    601018:  01 00 00 00 02 00 00 00

7.8 可执行目标文件

典型的ELF可执行文件中的各类信息,如下:

在这里插入图片描述
图2 典型的ELF可执行目标文件
格式类似于可重定位目标文件格式。.init节中定义_init函数,代码初始化时调用。

可执行文件prog的程序头部表,如下:

Read-only code segment
1  Load off 0x0000000000000000  vaddr 0x0000000000400000    paddr 0x0000000000400000  align 2**21
2    filesz 0x000000000000069c  memsz   0x000000000000069c  flag r-x

Read/write data segment
3  Load off 0x0000000000000df8  vaddr 0x0000000000600df8    paddr 0x0000000000600df8  align 2**21
4    filesz 0x0000000000000228  memsz   0x0000000000000230  flag rw-

off:目标文件中的偏移; vaddr/paddr:内存地址; align:对齐要求; filesz:目标文件中的段大小;memsz:内存中的段大小; flags:运行时访问权限。

  1. 1和2行(代码段),只读权限,开始于内存地址 0 × 400000 0\times400000 0×400000处,总共内存大小 0 × 69 c 0\times69c 0×69c,被初始化为可执行目标文件的头 0 × 69 c 0\times69c 0×69c个字节。

  2. 3和4行(数据段),读写权限,开始于内存地址0x600df8处,总内存大小 0 × 230 0\times230 0×230字节,初始化为从目标文件中偏移 0 × d f 8 0\times df8 0×df8处开始的.data节中的 0 × 228 0\times228 0×228个字节初始化。

对于任何段s,起始地址满足:vaddr mod align = off mod align。优化对齐,便于目标文件中的段高效地传送至内存。

7.9 加载可执行目标文件

系统调用加载器将可执行目标文件的代码和数据从磁盘复制到内存,然后跳转至入口地址来运行程序,这一过程称为加载。

在这里插入图片描述
图3 Linux x86-64运行时内存映像
代码段总是从 0 × 400000 0\times400000 0×400000处开始,后面是数据段。堆在数据段之后,通过调用malloc向上增长。用户栈总是从最大的合法用户地址 2 48 − 1 2^{48}-1 2481处开始。

7.10 动态链接共享库

共享库,用于解决多个进程调用相同静态库造成的内存浪费问题。
共享库是一个目标模块,在运行或加载时,可以加载到任意的内存地址,并在内存中的程序链接起来(动态链接)。

共享库(so)中的代码和数据不会复制到引用它们的可执行文件中。
在内存中,共享库的.text节副本可被不同的正在运行的进程共享。
在这里插入图片描述

图4 动态链接共享库
使用动态链接共享库的命令行参数,如下。注:后缀.so和.dll均可。

 gcc -shared -fpic -o libvector.so addvec.c multivec.c
 gcc -o prog main.c libvector.so

7.11 从应用程序中加载和链接共享库

动态链接的功能:

  1. 分法软件:利用共享库分发软件更新,可下载并替换当前版本。
  2. 构建高性能服务器:基于动态链接以更有效和完善的方法生成动态内容。

7.12 位置无关代码

可以加载而无需重定位的代码称为位置无关代码(Position-Independent Code,PIC)。

PIC数据引用
无论在内存中的何处加载一个目标模块,数据段和代码段的距离总是保持不变。
因此,代码段中的任何指令和数据段中任何变量之间的距离为常量。

基于上述原理,编译器在数据段开始处创建全局偏移量表(Global Offset Table, GOT),实现对全局变量PIC引用。

PIC函数调用
共享模块在运行时,随机加载到内存的任何位置,编译器无法预测其函数的运行地址。
GNU编译系统使用延迟绑定,将过程地址的绑定推迟到函数的第一次调用时。基于GOT和过程连接表(PLT)的交互实现。

7.13 库打桩机制

允许截获对共享库函数的调用,取而代之执行自己的代码。
打桩可发生在编译、链接以及程序加载和执行时。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Eto_Demerzel/article/details/104440704

智能推荐

软连接ln_软连接 没有l-程序员宅基地

文章浏览阅读139次。软连接ln[root@even tmp]# ln --helpUsage: ln [OPTION]... [-T] TARGET LINK_NAME (1st form)or: ln [OPTION]... TARGET (2nd form)or: ln [OPTION]... TARGET... DIRECTORY (3rd form)or: ln [OPTION]... -t DIRECTORY TARGET... (4th form)In the 1st form, create a li_软连接 没有l

LeetCode 2--两数相加 ( Add Two Numbers ) ( C语言版 )_struct listnode* addtwonumbers(struct listnode* l1-程序员宅基地

文章浏览阅读2.3k次,点赞2次,收藏4次。题目描述 : 代码如下 ( 附有解析 ) : /** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */struct ListNode* addTwoNumbers(struct ListNode* l..._struct listnode* addtwonumbers(struct listnode* l1, struct listnode* l2) { }

ssm644基于Vue的健身房会员管理系统+vue-程序员宅基地

文章浏览阅读802次,点赞22次,收藏20次。本课题所开发的应用程序在数据操作方面是不可预知的,是经常变动的,没有办法直接把数据写在文档里,这样不仅仅不安全,也不能实现应用程序的功能。参考自己的学习进度和操作习惯来讲,Oracle数据库是适合的,但是所需要的的安装软件很大,并且有好多不需要的功能都是开启的状态,十分消耗电脑资源,所以没有选择Oracle数据库,而SQL Server数据库虽然学过,但是安装的时候因为电脑上可能有其他的软件存在,经常性的出问题,而安装问题不好解决就需要重新安装操作系统,这样对已经存在的软件来讲又是一种时间上的浪费。

[算法练习及思路-程序员面试金典(Java解法)]No9.字符串轮转-程序员宅基地

文章浏览阅读114次。题号:no9题目名:字符串轮转原题URL:https://leetcode-cn.com/problems/string-rotation-lcci/题目描述字符串轮转。给定两个字符串s1和s2,请编写代码检查s2是否为s1旋转而成(比如,waterbottle是erbottlewat旋转后的字符串)。示例示例 1: 输入:s1 = "waterbottle", s2 = "erbottlewat" 输出:True示例 2: 输入:s1 = "aa", s2 = "aba" 输出:F

LaTex中“图片引用失败_latex模板引入图片失败-程序员宅基地

文章浏览阅读2.1k次。在LaTex中引用图片失败,出现(??)查了一些博客,发现是加图片标签时出的问题:一般来说图片会命名:\caption{图片名}.要想引用成功, 必须要把标签加在图片命名之后:例如:\begin{figure}[H]\centering\includegraphics[width=0.8\textwidth]{kkk.png}\caption{kkk}\label{kk}\end{figure}这样在后文使用(\ref{kk})才能成功引用,用IEEE的模板的时候期刊不用fi_latex模板引入图片失败

推荐系统遇上深度学习(一零四)-[阿里]CTR预估中细粒度特征学习的多交互注意力网络...-程序员宅基地

文章浏览阅读1.1k次。今天给大家介绍的是阿里在WSDM21上发表的一篇文章,题目为《Multi-Interactive A ention Network for Fine-grained Feature Le..._transformer ctr

随便推点

java 剑指 Offer 60. n个骰子的点数+acwing80. 骰子的点数-程序员宅基地

文章浏览阅读87次。动态规划 先看acwing的骰子的点数 在看这道题就好理解的多acwing的那道题是求f[i][j] 表示前i次总和是j的掷法数除以总数就是6的n次方把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。代码案例:输入: 1输出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]

人生若只如初见-纳兰性德_《云荒 只如初见》纳兰词-程序员宅基地

文章浏览阅读2.6k次。木兰词 作者: 纳兰性德 拟古决绝词谏友。人生若只如初见,何事秋风悲画扇。等闲变却故人心,却道故人心易变。 骊山语罢清宵半,泪雨霖铃终不怨。何如薄幸锦衣郎,比翼连枝当日愿。【赏析】 无疑,该阙词章与白氏《长恨歌》皆涉及唐玄宗和杨玉环那段毁誉参半的爱情故事。 站在政治的角度,李隆基荒淫废国。 从感情的意义上来看,虽然唐玄宗迫于三军众怒,无奈将杨贵妃赐死马嵬坡,从此生死诀别、阴_《云荒 只如初见》纳兰词

micro2440与PC同步问题(个人情况,仅供参考)_micro2440同步-程序员宅基地

文章浏览阅读881次。我用的是micro2440的3.5寸开发板,不知道实验室哪一年采购的,因为要用到wince,所以拿来准备学习一下。前期安装软件暂且不提,按照手册来应该没什么问题,可到了用activesync与PC同步的时候悲剧就发生了,烧写完wince系统,连上PC机无任何反应,而且本来很流畅的wince系统,只要一用USB跟PC连接就变得奇卡无比。在网上搜了大篇的资料也没找到解决的办法,迫不得已,只_micro2440同步

java实现国密算法SM3摘要加密_sm3utils jar包下载-程序员宅基地

文章浏览阅读847次。java实现SM3摘要加密_sm3utils jar包下载

面试问题某个项目中遇到过什么问题_面试中,面试官文你在项目中遇到过哪些问题-程序员宅基地

文章浏览阅读4.8w次,点赞4次,收藏21次。这是面试官很喜欢问的一个问题,你是会刁难你的一个地方。如果你在项目中是核心解决了一些技术难题,那这个问题对你来说应该是很好回答的。你可以说出这个项目遇到的技术问题,以及你是怎么解决的。这种问题往往可以从并发,稳定性,容错,性能等方面入手。如果你没有参与这种问题的解决,平时也要多关注其他人是怎么解决的,不要局限在自己的空间里。还有就是你可以给出一个场景,自己设计一种可行的解决方案,予以阐述。总之你要_面试中,面试官文你在项目中遇到过哪些问题

jar包冲突问题_jar:file:/d:/program%20files/apache-maven-3.5.4/re-程序员宅基地

文章浏览阅读472次。slf4j的jar包冲突。SLF4J: Class path contains multiple SLF4J bindings.SLF4J: Found binding in [jar:file:/D:/apache-maven-3.5.3/repository/ch/qos/logback/logback-classic/1.1.11/logback-classic-1.1.11.jar!/..._jar:file:/d:/program%20files/apache-maven-3.5.4/repository/io/springfox/spri

推荐文章

热门文章

相关标签