linux的静态编译elf无法调试,[翻译]自己动手编写一个Linux调试器系列之4 ELF文件格式与DWARF调试格式 by lantie@15PB...

自己动手编写一个Linux调试器系列之4 ELF文件格式与DWARF调试格式 by lantie@15PB

在上一节中,你已经听说了DWARF调试格式,它是程序的调试信息,是一种可以更好理解源码的方式,而不只是解析程序。今天我们将讨论源代码级调试信息的细节,以准备在本教程后面的部分中使用它。

系列索引准备工作

断点的设置

寄存器和内存

ELF文件格式和DWARVF调试格式

源码和信号

源码级单步

源码级断点

堆栈解除

处理变量

高级主题

ELF文件格式与DWARF格式简介

ELF和DWARF是你可能没有听说过的两个概念信息,但可能已经使用很长时间了。 ELF(可执行和可链接格式)是Linux世界中使用最广泛的对象文件格式; 它指定了一种存储二进制文件的所有不同部分的方式,如代码,静态数据,调试信息和字符串。 它还告诉加载程序如何取得二进制并准备好执行,这涉及二进制文件的不同部分应该放置在内存中,哪些部分需要根据其他信息(重定位)等的位置来修复。 我不会在这些帖子中覆盖更多ELF,但如果你有兴趣,可以看看这个漂亮的信息图表或标准。

DWARF是ELF最常用的调试信息格式。它不一定与ELF相关,但两者是一起发展的,在开发中一起使用也非常好。该格式允许编译器告诉调试器程序源代码如何与将执行的二进制文件相互关系。该信息分为不同的ELF部分,每个部分都有自己的信息来中继。以下是定义的不同部分,取自于非常详细的DWARF调试格式介绍:

.debug_abbrev .debug_info部分中使用的缩写

.debug_aranges 内存地址和编译之间的映射

.debug_frame 调用帧信息

.debug_info 包含DWARF调试信息项(DIE)的核心DWARF数据

.debug_line 行号程序

.debug_loc 位置说明

.debug_macinfo 宏描述

.debug_pubnames 全局对象和函数的查找表

.debug_pubtypes 全局类型的查找表

.debug_ranges DIE引用的地址范围

.debug_str .debug_info使用的字符串表

.debug_types 类型说明

我们对.debug_line和.debug_info部分最感兴趣,所以让我们看看一些DWARF的简单程序。

int main() {

long a = 3;

long b = 2;

long c = a + b;

a = 4;

}

DWARF debug_line表信息

如果你使用编译器(gcc 或 clang)的-g选项编译此程序,并通过dwarfdump运行结果,则应该看到类似于行号的部分:

.debug_line: line number info for a single cu

Source lines (from CU-DIE at .debug_info offset 0x0000000b):

NS new statement, BB new basic block, ET end of text sequence

PE prologue end, EB epilogue begin

IS=val ISA number, DI=val discriminator value

[lno,col] NS BB ET PE EB IS= DI= uri: "filepath"

0x00400670 [ 1, 0] NS uri: "/home/simon/play/MiniDbg/examples/variable.cpp"

0x00400676 [ 2,10] NS PE

0x0040067e [ 3,10] NS

0x00400686 [ 4,14] NS

0x0040068a [ 4,16]

0x0040068e [ 4,10]

0x00400692 [ 5, 7] NS

0x0040069a [ 6, 1] NS

0x0040069c [ 6, 1] NS ET

第一部分描述部分是关于如何理解下面显示列表的一些信息 - 表信息主行号数据从0x00400670开始。本质上,它是将一个代码内存地址映射到一些文件中的行和列号。 NS表示该地址标志着新语句的开始,这通常用于设置断点或步进。 PE标记函数开始的结尾,这有助于设置函数入口断点。 ET标示翻译单元的结尾。实际信息上并不是像这样编码的;真正的编码是一种非常节省空间的程序,可以执行这些程序来建立这个行信息。

那么说,我们想在variable.cpp的第4行设置一个断点,我们该怎么做?我们查找与该文件相对应的条目,然后查找相关的行条目,查找与之对应的地址,并在其中设置断点。在我们的例子中,这是这个条目:

0x00400686 [ 4,14] NS

所以我们要在地址0x00400686设置一个断点。你可以用你已经写过的调试器手工完成,如果你想尝试一下。

相反的工作也是如此。如果我们有一个内存位置 - 例如一个程序计数器值,并且想要找出源代码中的哪个位置,我们只需在行表信息中找到最接近的映射地址,并从中获取行。

DWARF debug_info信息

.debug_info部分是DWARF的核心。它给了我们有关我们的程序中存在的类型,函数,变量,希望和想要得到的信息。本节的基本单位是DWARF信息条目(DWARF Information Entry),简称为DIE。 DIE包含一个标签,告诉您正在表示什么样的源代码级实体,后面是一系列适用于该实体的属性。这是上面发布的简单示例程序的.debug_info部分:

.debug_info

COMPILE_UNIT:

< 0><0x0000000b> DW_TAG_compile_unit

DW_AT_producer clang version 3.9.1 (tags/RELEASE_391/final)

DW_AT_language DW_LANG_C_plus_plus

DW_AT_name /super/secret/path/MiniDbg/examples/variable.cpp

DW_AT_stmt_list 0x00000000

DW_AT_comp_dir /super/secret/path/MiniDbg/build

DW_AT_low_pc 0x00400670

DW_AT_high_pc 0x0040069c

LOCAL_SYMBOLS:

< 1><0x0000002e> DW_TAG_subprogram

DW_AT_low_pc 0x00400670

DW_AT_high_pc 0x0040069c

DW_AT_frame_base DW_OP_reg6

DW_AT_name main

DW_AT_decl_file 0x00000001 /super/secret/path/MiniDbg/examples/variable.cpp

DW_AT_decl_line 0x00000001

DW_AT_type <0x00000077>

DW_AT_external yes(1)

< 2><0x0000004c> DW_TAG_variable

DW_AT_location DW_OP_fbreg -8

DW_AT_name a

DW_AT_decl_file 0x00000001 /super/secret/path/MiniDbg/examples/variable.cpp

DW_AT_decl_line 0x00000002

DW_AT_type <0x0000007e>

< 2><0x0000005a> DW_TAG_variable

DW_AT_location DW_OP_fbreg -16

DW_AT_name b

DW_AT_decl_file 0x00000001 /super/secret/path/MiniDbg/examples/variable.cpp

DW_AT_decl_line 0x00000003

DW_AT_type <0x0000007e>

< 2><0x00000068> DW_TAG_variable

DW_AT_location DW_OP_fbreg -24

DW_AT_name c

DW_AT_decl_file 0x00000001 /super/secret/path/MiniDbg/examples/variable.cpp

DW_AT_decl_line 0x00000004

DW_AT_type <0x0000007e>

< 1><0x00000077> DW_TAG_base_type

DW_AT_name int

DW_AT_encoding DW_ATE_signed

DW_AT_byte_size 0x00000004

< 1><0x0000007e> DW_TAG_base_type

DW_AT_name long int

DW_AT_encoding DW_ATE_signed

DW_AT_byte_size 0x00000008

第一个DIE表示一个编译单元(CU),它基本上是一个源文件,其中包含所有#includes,并且这样解析。以下是它们的含义注释的属性:

DW_AT_producer clang version 3.9.1 (tags/RELEASE_391/final)

this binary

DW_AT_language DW_LANG_C_plus_plus

DW_AT_name /super/secret/path/MiniDbg/examples/variable.cpp

this CU represents

DW_AT_stmt_list 0x00000000

which tracks this CU

DW_AT_comp_dir /super/secret/path/MiniDbg/build

DW_AT_low_pc 0x00400670

this CU

DW_AT_high_pc 0x0040069c

this CU

其他DIE遵循类似的方案,您可以直观地看出不同属性的含义。

现在我们可以尝试用我们新发现的DWARF知识解决一些实际问题。

使用 DWARF 分析函数

如果我们有一个程序计数器值,并想获取PC所在函数的信息。一个简单的算法是:

for each compile unit:

if the pc is between DW_AT_low_pc and DW_AT_high_pc:

for each function in the compile unit:

if the pc is between DW_AT_low_pc and DW_AT_high_pc:

return function information

这可以用于许多情况,但是在成员函数和内联函数存在的情况下,事情会变得更加困难。 例如,使用内联函数,一旦找到范围包含我们的PC的函数,我们将需要对该DIE的子项进行递归,以查看是否存在更好匹配的内联函数。我不会在我的调试器代码中处理内联函数,但如果你喜欢,你可以添加对此的支持。

如何在函数上设置断点

再次申明,如果想要支持成员函数,命名空间等特性可能需要更高级的做法。 对于简单的函数,您可以在不同的编译单元中迭代函数,直到找到具有正确名称的函数。 如果您的编译器足够填写.debug_pubnames部分,您可以更有效地执行此操作。

一旦找到该函数,您可以在DW_AT_low_pc给定的内存地址上设置一个断点。 但是,在函数开始时会中断,但最好在用户代码开始时中断。 由于行表信息可以指定指定函数开头结束的内存地址,因此您可以直接在行表中查找DW_AT_low_pc的值,然后继续阅读,直到找到标记为函数开头结束的条目。 有些编译器不会输出这个信息,所以另外一个选择是在该函数的第二行条目给出的地址上设置一个断点。

假设我们要在我们的示例程序中设置一个断点。 我们搜索main函数,并得到这个DIE:

< 1><0x0000002e> DW_TAG_subprogram

DW_AT_low_pc 0x00400670

DW_AT_high_pc 0x0040069c

DW_AT_frame_base DW_OP_reg6

DW_AT_name main

DW_AT_decl_file 0x00000001 /super/secret/path/MiniDbg/examples/variable.cpp

DW_AT_decl_line 0x00000001

DW_AT_type <0x00000077>

DW_AT_external yes(1)

这告诉我们,函数从0x00400670开始。 如果我们在线表中查看,我们得到这个条目:

0x00400670 [ 1, 0] NS uri: "/super/secret/path/MiniDbg/examples/variable.cpp"

我们想跳过开头,所以我们先读一个条目:

0x00400676 [ 2,10] NS PE

Clang在这个条目中包含了代码开头结束标志,所以我们知道在这里停下来,并在地址0x00400676上设置一个断点。

如何读取变量的内容

读取变量可能非常复杂。 变量是一个难以捉摸的东西,可以在整个函数中存在,可以放在寄存器中,放在内存中,还可以被优化,隐藏在角落里。幸运的是,我们简单的例子是,很简单。 如果我们想要读取变量a的内容,我们来看看它的DW_AT_location属性:

DW_AT_location DW_OP_fbreg -8

这表示局部变量的内存在距离堆栈帧基址的-8的偏移处。 要找出这个基址的位置,我们来看看包含函数的DW_AT_frame_base属性。

DW_AT_frame_base DW_OP_reg6

在x86上的reg6是栈帧指针寄存器,由System V x86_64 ABI指定。现在我们读帧指针的内容,从中减去8,我们已经找到了变量。如果我们想弄明白这个问题,我们需要看看它的类型:

< 2><0x0000004c> DW_TAG_variable

DW_AT_name a

DW_AT_type <0x0000007e>

如果我们在调试信息中查找这个类型,就会得到这个DIE:

< 1><0x0000007e> DW_TAG_base_type

DW_AT_name long int

DW_AT_encoding DW_ATE_signed

DW_AT_byte_size 0x00000008

这告诉我们类型是一个8字节(64位)的有符号整数类型,因此我们可以继续将这些字节解释为int64_t并将其显示给用户。

当然,类型可以比这个复杂得多,因为它们必须能够表达诸如c++类型之类的东西,但这给了你一个关于它们如何工作的基本概念。

回到该栈帧的基址,Clang编译器可以比较好的跟踪到帧指针寄存器的帧基址。 最近版本的GCC倾向于喜欢DW_OP_call_frame_cfa,它涉及解析.eh_frame ELF部分,这是一个完全不同的文章,在这里我就不详述。 如果你使用GCC的DWARF 2版本而不是更新的版本,命令是gcc -gdwarf-2 那么它将倾向于输出位置列表,这更容易阅读:

DW_AT_frame_base

low-off : 0x00000000 addr 0x00400696 high-off 0x00000001 addr 0x00400697>DW_OP_breg7+8

low-off : 0x00000001 addr 0x00400697 high-off 0x00000004 addr 0x0040069a>DW_OP_breg7+16

low-off : 0x00000004 addr 0x0040069a high-off 0x00000031 addr 0x004006c7>DW_OP_breg6+16

low-off : 0x00000031 addr 0x004006c7 high-off 0x00000032 addr 0x004006c8>DW_OP_breg7+8

上面列表根据程序计数器的位置给出不同的位置。 这个例子是说,如果PC在DW_AT_low_pc处于0x0的偏移量的情况下,那么栈帧基地址是从寄存器7中存储的值加偏移量8,如果它在0x1到0x4之间,那么它的偏移距离一样都是16,等等。

总结一下

这节包含了很多DWARF信息需要好好吸收一下才行。不要担心!有个好消息,就是在接下来的几个章节中,我们将有一个库帮我们完成最麻烦的工作。了解了DWARF的概念,特别是在出现问题或希望支持一些DWARF库的情况下,仍然有用。

如果您想了解更多关于DWARF的信息,那么你可以在此获取标准文档。 在撰写本文时,DWARF 5刚刚被发布,但DWARF 4更受欢迎。

说明

自己动手实践一下

本节内容是整个系列最枯燥的一章,全篇都是在讲述DWARF调试格式的内容。我们可以使用编译器gcc或者clang编译源码时在生成的可执行文件中产生调试信息,并使用DWARF相关的工具dwarfdump查看和解析可执行文件ELF文件格式中的调试信息。

使用gcc的命令可以生成dwarf格式的调试信息

gcc -g 编译生成dwarf调试格式的信息

源码使用的是文章的例子。int main() {

long a = 3;

long b = 2;

long c = a + b;

a = 4;

}使用gcc编译之后,可以使用readelf查看可执行文件中的Seciton信息

root@ubuntu:~/Desktop/test# gcc -g test.c

root@ubuntu:~/Desktop/test# readelf -S a.out

There are 35 section headers, starting at offset 0x1390:

Section Headers:

[Nr] Name Type Address Offset

Size EntSize Flags Link Info Align

[ 0] NULL 0000000000000000 00000000

0000000000000000 0000000000000000 0 0 0

[ 1] .interp PROGBITS 0000000000400238 00000238

000000000000001c 0000000000000000 A 0 0 1

[ 2] .note.ABI-tag NOTE 0000000000400254 00000254

0000000000000020 0000000000000000 A 0 0 4

[ 3] .note.gnu.build-i NOTE 0000000000400274 00000274

0000000000000024 0000000000000000 A 0 0 4

[ 4] .gnu.hash GNU_HASH 0000000000400298 00000298

000000000000001c 0000000000000000 A 5 0 8

[ 5] .dynsym DYNSYM 00000000004002b8 000002b8

0000000000000048 0000000000000018 A 6 1 8

[ 6] .dynstr STRTAB 0000000000400300 00000300

0000000000000038 0000000000000000 A 0 0 1

[ 7] .gnu.version VERSYM 0000000000400338 00000338

0000000000000006 0000000000000002 A 5 0 2

[ 8] .gnu.version_r VERNEED 0000000000400340 00000340

0000000000000020 0000000000000000 A 6 1 8

[ 9] .rela.dyn RELA 0000000000400360 00000360

0000000000000018 0000000000000018 A 5 0 8

[10] .rela.plt RELA 0000000000400378 00000378

0000000000000030 0000000000000018 A 5 12 8

[11] .init PROGBITS 00000000004003a8 000003a8

000000000000001a 0000000000000000 AX 0 0 4

[12] .plt PROGBITS 00000000004003d0 000003d0

0000000000000030 0000000000000010 AX 0 0 16

[13] .text PROGBITS 0000000000400400 00000400

00000000000001a2 0000000000000000 AX 0 0 16

[14] .fini PROGBITS 00000000004005a4 000005a4

0000000000000009 0000000000000000 AX 0 0 4

[15] .rodata PROGBITS 00000000004005b0 000005b0

0000000000000004 0000000000000004 AM 0 0 4

[16] .eh_frame_hdr PROGBITS 00000000004005b4 000005b4

0000000000000034 0000000000000000 A 0 0 4

[17] .eh_frame PROGBITS 00000000004005e8 000005e8

00000000000000f4 0000000000000000 A 0 0 8

[18] .init_array INIT_ARRAY 0000000000600e10 00000e10

0000000000000008 0000000000000000 WA 0 0 8

[19] .fini_array FINI_ARRAY 0000000000600e18 00000e18

0000000000000008 0000000000000000 WA 0 0 8

[20] .jcr PROGBITS 0000000000600e20 00000e20

0000000000000008 0000000000000000 WA 0 0 8

[21] .dynamic DYNAMIC 0000000000600e28 00000e28

00000000000001d0 0000000000000010 WA 6 0 8

[22] .got PROGBITS 0000000000600ff8 00000ff8

0000000000000008 0000000000000008 WA 0 0 8

[23] .got.plt PROGBITS 0000000000601000 00001000

0000000000000028 0000000000000008 WA 0 0 8

[24] .data PROGBITS 0000000000601028 00001028

0000000000000010 0000000000000000 WA 0 0 8

[25] .bss NOBITS 0000000000601038 00001038

0000000000000008 0000000000000000 WA 0 0 1

[26] .comment PROGBITS 0000000000000000 00001038

000000000000005d 0000000000000001 MS 0 0 1

[27] .debug_aranges PROGBITS 0000000000000000 00001095

0000000000000030 0000000000000000 0 0 1

[28] .debug_info PROGBITS 0000000000000000 000010c5

0000000000000082 0000000000000000 0 0 1

[29] .debug_abbrev PROGBITS 0000000000000000 00001147

0000000000000053 0000000000000000 0 0 1

[30] .debug_line PROGBITS 0000000000000000 0000119a

000000000000003d 0000000000000000 0 0 1

[31] .debug_str PROGBITS 0000000000000000 000011d7

0000000000000071 0000000000000001 MS 0 0 1

[32] .shstrtab STRTAB 0000000000000000 00001248

0000000000000148 0000000000000000 0 0 1

[33] .symtab SYMTAB 0000000000000000 00001c50

0000000000000678 0000000000000018 34 50 8

[34] .strtab STRTAB 0000000000000000 000022c8

0000000000000224 0000000000000000 0 0 1

可以看出其种有译文中最重要的两个Section,.debug_line和.debug_info

gcc -gdwarf-2 编译生成 DWARF 2 版本调试格式的信息

与上面的命令类似,只是格式版本略有不同

使用dwarfdump可以查看生成的可执行文件的调试信息

dwarfdump -a 查看程序中所有debug开头的调试信息

由于信息量比较大,就不贴图了

dwarfdump -l 查看程序中调试信息的debugline信息

2c1406792b1629440a54c52926f43c42.png

dwarfdump -i 查看程序中调试信息的debuginfo信息

a6b8a40b187385f5898c3431861c4c9b.png

dwarfdump -p 查看程序中调试信息的debug_pubnames信息

2f340d74877d5a6488b201fe1a1ca86e.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/555440.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot踩坑记录 Invalid bound statement (not found)引发的一些列问题

SpringBoot踩坑记录 Invalid bound statement (not found)引发的一些列问题 当你开开心心搭建了一个SpringBoot项目&#xff0c;用插件生成了entity、dao、mapper&#xff0c;写下第一个Controller准备试一下&#xff0c;结果却发现一条简单的查询报错了。 {"timestamp…

Java中switch参数传null会引起异常——Java 语法糖

问题 switch 参数不能是null&#xff0c;swicth(null)会报java.lang.NullPointerException异常 查找原因 为什么会这样呢&#xff0c;查找一下原因&#xff1a; 找到编译后的class文件&#xff0c;就明白了 总结&#xff1a; switch 是一个语法糖。switch语句是先计算 par…

linux head命令作用,Linux查看文件内容之head命令

1. head命令简介本文主要介绍head命令的作用与常用使用方法&#xff0c;该命令和tail命令相反&#xff0c;head默认显示用来显示文本开头&#xff0c;而tail默认显示结尾某个数量的文字区块。2. head命令选项-q 隐藏文件名-v 显示文件名-c 显示字节数-n 显示的行数3. 常见使用方…

SpringBoot使用jasypt加解密密码

在我们的服务中不可避免的需要使用到一些秘钥&#xff08;数据库、redis等&#xff09;&#xff1b;使用过SpringBoot配置文件的朋友都知道&#xff0c;资源文件中的内容通常情况下是明文显示&#xff0c;安全性就比较低一些。打开application.properties或application.yml&…

android 写字体投影,android-给字体设置投影

1&#xff0c;在代码中添加文字阴影TextView 有一个方法/*** Gives the text a shadow of the specified radius and color, the specified* distance from its normal position.** attr ref android.R.styleable#TextView_shadowColor* attr ref android.R.styleable#TextView…

android 开源图表动画,Android 图表开发开源库MPAndroidChart-Go语言中文社区

上面是APP中实现的效果图(点击可以放大查看)图1的效果不是用这个实现的&#xff0c;如果感兴趣可以参考我这篇文章 Android渐变圆环总体来说&#xff0c;MPAndroidChart可能是目前Android 开发最好用的一个三方库了&#xff0c;功能非常强大&#xff0c;集成简单。直接导入作为…

电脑开启防火墙后ping不通 及 开启防火墙后,不能远程的解决办法

有些情况下电脑需要开防火墙但是又希望能被远程或者ping通。 一、先查看电脑的网络类型&#xff08;第二步中打开对应网络类型下的协议&#xff09; 二、控制面板 - Windows防火墙 - 高级设置-入站规则 &#xff08;1&#xff09;开启防火墙后&#xff0c;能ping通设置。 &…

android 时间应用程序,Android在首次启动时需要更多时间启动应用程序

在我的项目中&#xff0c;我使用了需要multidex支持的库。 根据我的研究&#xff0c;我发现它会导致应用启动时出现延迟。我在gradle中启用了multidexfunction。 defaultConfig {multiDexEnabled true}我曾经为kitkat设备获得NoClassDefFound Exception&#xff0c;所以我添加了…

Mysql中用between...and...查询日期时注意事项

select count(1) from user where regist_date between 2017-07-25 00:00:00 and 2017-07-25 24:00:00;这条sql语句查询出结果为0。实际上数据库有一条符合该查询条件的数据。 错误原因&#xff1a;2017-07-25 24:00:00 晚上24点即为下一天00点 2017-07-26 00:00:00&#xff0…

android core log,Android 日志系统(Logcat)的实现分析

这篇说一下Android 日志系统的实现&#xff1a;1. Android中的打印分为4个缓冲区和6个打印等级&#xff0c;在frameworks\base\core\java\android\util\Log.java中有定义&#xff1a;四缓冲&#xff1a;public static final int LOG_ID_MAIN 0;public static final int LOG_ID…

android矢量图 内存大,Android内存控制小技巧-使用矢量图来节省你的内存并简化你的开发。...

先上一个 位图和矢量图的 说明。外链网址已屏蔽维基百科里面有更详细的说明 我就不贴地址了。简单来说就是位图 比 矢量图要大。但是位图可以适用于各种场景。但是矢量图就只能显示一些有规律的图形。复杂的做不了或者是很难做。对于android来说&#xff0c;我们一般app里面 会…

idea 快捷键代码行标记并快捷跳到标记行

idea有一个组合快捷键 标记代码行&#xff08;左边的行数会出现一个1&#xff0c;代表着已经标记了&#xff09; ctrlshift1 迅速跳到标记行 ctrl1 这边给大家送上显示GIF

鸿蒙之火是什么意思,鸿蒙突然火了!鸿蒙是什么?

原标题&#xff1a;"鸿蒙"突然火了&#xff01;"鸿蒙"是什么&#xff1f;什么是鸿蒙&#xff1f;鸿蒙是什么&#xff1f;很多人都在发问&#xff0c;仿佛一夜之间就火了的鸿蒙有何独特之处&#xff1f;鸿蒙是真的来了。鸿蒙是文化&#xff0c;鸿蒙也是科技…

String类的indexOf方法的用法和举例

String类的indexof方法的作用和返回值&#xff1a; 今天在这里重点回顾一下&#xff0c;希望对大家能有所帮助&#xff1a; indexOf&#xff08;int&#xff0c;ch&#xff09; 先看第一个indexOf它返回值是int&#xff0c;在看它的参数&#xff08;int&#xff0c;ch&#x…

Java字符串池(String Pool)深度解析

在工作中&#xff0c;String类是我们使用频率非常高的一种对象类型。JVM为了提升性能和减少内存开销&#xff0c;避免字符串的重复创建&#xff0c;其维护了一块特殊的内存空间&#xff0c;这就是我们今天要讨论的核心&#xff0c;即字符串池&#xff08;String Pool&#xff0…

用dl元素编辑html个人信息,html dl dt dd标签元素语法结构与使用

dl dt dd认识及dl dt dd使用方法一、dl dt dd认识 - TOPhtml 是一组合标签&#xff0c;使用了dt dd最外层就必须使用dl包裹&#xff0c;此组合标签我们也又叫表格标签&#xff0c;与table表格类似组合标签&#xff0c;故名我们也叫dl表格(扩展阅读&#xff1a;table tr td、…

千牛怎么注销卖家店铺_新店铺怎么拥有亚马逊购物车?亚马逊小卖家如何尽快获得黄金购物车?...

我做跨境电商也有六年的时间了&#xff0c;在电商这个行业也有自己的一些经验。经验也许没有其他大卖家丰富&#xff0c;但会将我知道的都进行分享。如果有不懂得亚马逊问题可以我(V&#xff1a;772024802)。我这里给大家安排一堂直播课&#xff0c;可以系统的帮你解决做亚马逊…

mysql中like是否使用索引

一.测试数据 CREATE TABLE test_orderdetail (id int(11) NOT NULL AUTO_INCREMENT,order_id int(11) DEFAULT NULL,product_name varchar(100) DEFAULT NULL,cnt int(11) DEFAULT NULL,create_date datetime DEFAULT NULL,PRIMARY KEY (id),KEY idx_orderid_productname (pro…

电子科大16秋计算机应用基础在线作业一,电子科大16秋《计算机应用基础(本科)》在线作业3...

电子科技大学电子科大16秋《计算机应用基础(本科)》在线作业3一、单选题(共35 道试题&#xff0c;共70 分。)1. 在Windows,打开一个菜单后,其中某菜单项会出现下属级联菜单的标识是()。A. 菜单右侧有一组英文提示B. 菜单右侧有一个黑色三角形C. 菜单左侧有一个黑色圆点D. 菜单左…

wince手机投屏代码_除了 iOS,这些手机系统你肯定没用过

今年是2020年&#xff0c;智能手机市场经过大浪淘沙&#xff0c;留存下来的品牌可以说都是精品&#xff0c;国外的像苹果、三星、索尼、LG&#xff0c;国内有华为、小米、OV、魅族....都是有特色有竞争力的。华为在海外的巨幅广告牌但是大家有没有发现&#xff0c;如此多的手机…