如何通过solc编译solidity编写的以太坊智能合约
曲水流觞113
发表于 2022-11-6 23:42:53
184
0
0
solidity编写的以太坊智能合约可通过命令行编译工具solc来进行编译,成为以太坊虚拟机中的代码。solc编译后最终部署到链上形成我们所见到的各种智能合约。$ a" |4 D4 ~; r0 `% ^- R- h
作为一个solidity命令行编译工具,我们来看看官网都怎么说solc。
' Q+ H) { [7 Y- @
solc的安装很简单:6 P4 [7 B0 d" X* x$ p7 @! r: Y( t
npminstall-gsolc
//或者2 i a# K1 D! _/ g
~+ w" M0 [4 |
npminstall-gsolc-cli5 j6 E" | D1 H* T& I
//或者
sudoapt-getinstallsolc0 F: ~$ M& t4 @0 Q& m
) E( v# o5 Z# N2 V+ r& F
安装完成后我们来看,solc--help,solc--help命令显示所有的solc命令选项。编译器可以生成各种输出,比如最终的二进制合约文件、语法树的汇编或者需要预计的要花费的gas等。solc--binsourceFile.sol,可以编译后输出一个名为sourceFile.sol的智能合约文件。如果你想从solc获得更丰富的一些输出变量,你可以使用solc-ooutputDirectory--bin--ast--asmsourceFile.sol。2 m" N. d2 Y G- ?
" L% \' j0 ^, W
你在部署以太坊智能合约之前可以用solc--optimize--binsourceFile.sol优化一下。默认情况下solc编译器会帮你优化200次。你也可以设置--runs=1,这样就按照最小化的方式进行编译,如果你希望多次交易不太在乎成本,那你可以设置成你想要的次数:)。9 A' P( T: Z1 \6 g# L
' O- R# b) p. R5 j" \+ y {+ U
命令行编译器会自动读取需要导入的文件,也可以通过使用prefix=path来指定路径,例如:3 H1 O2 S# f# b2 i8 Z$ I/ R
1 r/ q7 {& ?4 F( {5 N
solcgithub.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/=/usr/local/lib/fallbackfile.sol
' f1 g$ Y Y/ @, O9 W% Y# S
这样编译器就会从指定目录github.com/ethereum/dapp-bin/下的/usr/local/lib/dapp-bin/目录开始搜索,如果没有找到文件,它将查看/usr/local/lib/fallback。solc将只读取你指定的这两个路径的,因此像import"/etc/passwd";必须要通过/=重新映射才起作用。如果有多个匹配,则选择具有最长公共前缀的进行匹配。2 w. Q( Y0 f8 j4 ~! v7 |! a
* M' t& m: B# a9 z: n
出于安全上的考虑,编译器限制了它可以访问的一些目录。在命令行中指定的源文件的路径(及其子目录)和命令行指定的路径外其他所有内容都会被拒绝。--allow-paths/sample/path,/another/sample/path来切换。
如果智能合约使用了libraries,你会注意到字节码包含了__LibraryName______的子字符串。您可以使用solc作为链接器,这意味着它将在这些点为您插入库地址。" S5 R4 Q E/ Y$ T
- D/ P- ?2 ^6 q4 ]. r9 j6 ]
可以通过添加库--libraries"Math:0x12345678901234567890Heap:0xabcdef0123456"到您的命令,以提供每个库的地址,或者使用文件中的说明字符串(每行一个库),并使用--librariesfileName运行solc。
如果用选项--link调用Solc,则所有输入文件都被解释为未链接的二进制文件(HEX编码),在上面给出的__LibraryName____格式中,将其链接到适当地址(如果从stdin读取输入,则将其写入stdout)。在这种情况下,除了库外,所有选项都被忽略(包括-o)。
如果用--standard-json调用SOLC,它就将标准的JSON输入(如下所述),并返回JSON输出。8 Q* G& n3 Z# J' P3 `/ u2 \- I
: u; @) e9 y: T- l
#solc编译器输入输出JSON描述
( M0 [" b; X! H" I% Q
这些JSON格式通过编译器API使用,可以通过SOLC获得。内容都是可以修改的,一些对象是可选的(如前所述),其目的是向后兼容。5 V/ \, A* N& Q/ L6 g1 w
编译器的API需要一个JSON格式的输入,然后以JSON格式输出编译结果。
注意不允许注释。下面示例中的注释,是官网为了学习者更好的理解标注的。
& v6 `0 u$ x, T( ?+ _$ q" S
输入格式说明:
{
//Required:Sourcecodelanguage,suchas"Solidity","serpent","lll","assembly",etc.
, v! A( M9 S% e. ~6 F
language:"Solidity",- k! e* i$ x- h+ f
4 q9 a3 G. U5 Z% d4 b$ Q
//Required! Q0 o6 `2 `2 C+ B. H, m1 Q F- Q
+ h) U# Z8 m; j5 P; Y1 B
sources:2 }5 ^# V! }8 i! R0 m D5 E' W1 x( B8 Z
{
//Thekeysherearethe"global"namesofthesourcefiles,. v) `8 G+ i) k+ P" d6 v
' s, M1 ]# W$ f- O
//importscanuseotherfilesviaremappings(seebelow).9 S0 p( C1 }. }5 k8 }3 Y3 C8 s
"myFile.sol":
{
//Optional:keccak256hashofthesourcefile
8 z: n0 V9 v, R, [6 z; N
//ItisusedtoverifytheretrievedcontentifimportedviaURLs.
+ W; \0 T( P: t5 L) p. X5 t( G
"keccak256":"0x123...",
$ K1 W+ R* M! D. C
//Required(unless"content"isused,seebelow):URL(s)tothesourcefile.
//URL(s)shouldbeimportedinthisorderandtheresultcheckedagainstthe
& y) f* m, }( N
//keccak256hash(ifavailable).Ifthehashdoesn'tmatchornoneofthe6 H5 Q5 s7 ?5 T4 U
2 U) H9 G: Q0 j, k3 ?' h8 d4 T
//URL(s)resultinsuccess,anerrorshouldberaised.
6 x* \. ], o8 Y) w$ x, C
"urls":
0 W O/ [- X3 F- s" a% {6 }
[" k6 Q: G6 s4 u' K3 N2 r
"bzzr://56ab...",7 T m$ |3 @4 V! W5 u E* |# g
"ipfs://Qma...",
"file:///tmp/path/to/file.sol"
+ b. H( J, B) \: q6 F
]
},0 M" t ~4 l# s
"mortal":
* f) t8 G( h- S- V% Q2 k
{8 `0 X: k2 z0 t# m. c' n, X3 \
//Optional:keccak256hashofthesourcefile! Y; j: D- Y5 k/ h9 g
( d; U; B! \. v& x0 Q5 K5 Z
"keccak256":"0x234...",8 r9 ~7 A' [ W1 j [
//Required(unless"urls"isused):literalcontentsofthesourcefile
"content":"contractmortalisowned{functionkill(){if(msg.sender==owner)selfdestruct(owner);}}". w9 E# m" s" X1 a& [
}0 y5 O0 p- M8 {. J6 \4 Y$ a# O1 T$ m
1 H. p( Z4 Y% d) M( {6 `
},* w' E) o/ ]2 ^( w4 q+ w+ A
' x8 R. I0 H4 g4 Y3 h [7 \
//Optional
# q' ? b1 a: T, V w" l8 H! g% M
settings:
{
& q) r% O4 t& X* \( p% c/ S9 l9 C) S
//Optional:Sortedlistofremappings& I! F+ q# A- \, l7 {% W! B" L% z/ }8 m
remappings:[":g/dir"],
% s# t- x9 c, O
//Optional:Optimizersettings& }8 t+ r W6 z/ ~
optimizer:{6 }$ f: d4 b4 \; v* S2 R
//disabledbydefault
enabled:true,
//Optimizeforhowmanytimesyouintendtorunthecode.3 d4 ?% q1 h N% E2 ^
( E" `4 K7 y( _+ L
//Lowervalueswilloptimizemoreforinitialdeploymentcost,highervalueswilloptimizemoreforhigh-frequencyusage.4 j; h+ o7 L/ A' }: l# L
& O- e" T- s( J: w& x8 a1 m
runs:200$ ]5 g/ n1 {- N% q4 U
+ g% p! \1 s* ^& z+ G
},
+ K; R" K- b( [4 k# s* t6 M* X$ x( V
evmVersion:"byzantium",//VersionoftheEVMtocompilefor.Affectstypecheckingandcodegeneration.Canbehomestead,tangerineWhistle,spuriousDragon,byzantiumorconstantinople* I- i: v: `& g8 B; ]6 ~9 ~
//Metadatasettings(optional)% Q W4 I( W3 W
$ u- Z5 I7 V/ j" p" [+ b
metadata:{
//UseonlyliteralcontentandnotURLs(falsebydefault)
s, V+ b" D2 P/ L3 f- B/ {& t
useLiteralContent:true7 \# l9 A, A. ~ O( e* ?
2 |* }& I+ C$ T7 C3 N
},
//Addressesofthelibraries.Ifnotalllibrariesaregivenhere,itcanresultinunlinkedobjectswhoseoutputdataisdifferent.
, t& b7 Z6 l- E
libraries:{9 k9 Y( }9 V; g4 u2 D( |/ M
! W0 ]: H& L& Q2 ~3 R. j5 i5 K
//Thetoplevelkeyisthethenameofthesourcefilewherethelibraryisused.
# K' p1 t: C6 n+ d
//Ifremappingsareused,thissourcefileshouldmatchtheglobalpathafterremappingswereapplied.
& k# w$ P' g2 N2 L! r1 [
//Ifthiskeyisanemptystring,thatreferstoagloballevel.
/ ~. q% n# H) O5 A; T' {8 ^
"myFile.sol":{
"MyLib":"0x123123..."$ N% k1 J+ p) P/ {- }* Q6 ]
}
}
) o' \$ ^' E e. O [8 `8 n- X
//Thefollowingcanbeusedtoselectdesiredoutputs.
; ~" ^9 Z# O( l: G% P+ U
//Ifthisfieldisomitted,thenthecompilerloadsanddoestypechecking,butwillnotgenerateanyoutputsapartfromerrors.
/ r( w# c& }0 ?+ o$ K
//Thefirstlevelkeyisthefilenameandthesecondisthecontractname,whereemptycontractnamereferstothefileitself,/ g3 B1 w, Z" v& K% U9 |
//whilethestarreferstoallofthecontracts.
//& V! R& u6 ?' X9 a5 B
//Theavailableoutputtypesareasfollows:
//abi-ABI& L& H* w: v6 e* R- u
//ast-ASTofallsourcefiles
' [9 U$ x/ }4 A" o3 Z% L2 i! w
//legacyAST-legacyASTofallsourcefiles
//devdoc-Developerdocumentation(natspec)
! j. q. g; D. k0 i. A" `
//userdoc-Userdocumentation(natspec)
//metadata-Metadata6 e- ~, W% T g3 _# i4 E
5 d# d0 K5 R( O) B/ j6 A
//ir-Newassemblyformatbeforedesugaring
4 F: T1 F5 s+ V; G
//evm.assembly-Newassemblyformatafterdesugaring o) x" |, F' v5 P0 R3 V1 @% u
//evm.legacyAssembly-Old-styleassemblyformatinJSON8 l: q( ?/ b0 q# g( @+ x5 |
//evm.bytecode.object-Bytecodeobject; m: ]( S+ C& X# q6 t( X* o
" q" F, R1 I3 ^ m# a
//evm.bytecode.opcodes-Opcodeslist! d/ x8 Y+ r/ [6 r r( t
5 b! R0 V$ V' z; V1 V& s3 c
//evm.bytecode.sourceMap-Sourcemapping(usefulfordebugging)
//evm.bytecode.linkReferences-Linkreferences(ifunlinkedobject)
//evm.deployedBytecode*-Deployedbytecode(hasthesameoptionsasevm.bytecode)
: }# U! U0 e+ t9 c% {5 K! a
//evm.methodIdentifiers-Thelistoffunctionhashes* m5 ~3 P7 G' O) T' c) e
' a% u7 j- c& [, N( l
//evm.gasEstimates-Functiongasestimates" A3 }- ~9 K. F4 k5 W! [
//ewasm.wast-eWASMS-expressionsformat(notsupportedatm)
//ewasm.wasm-eWASMbinaryformat(notsupportedatm), C) F7 p4 d$ h- { M* @ ^
% Q9 Q% M( l$ C6 s, E" Z
//! z$ C' x% S5 @/ T
//Notethatusingausing`evm`,`evm.bytecode`,`ewasm`,etc.willselectevery
' L! | o" b4 [5 H
//targetpartofthatoutput.Additionally,`*`canbeusedasawildcardtorequesteverything.
//
' O" {" O2 |$ {+ e) N
outputSelection:{
//Enablethemetadataandbytecodeoutputsofeverysinglecontract.) g3 R P8 K- U/ P t
"*":{; V' i* X7 h- y- D T# N
- Y8 V2 Z+ t' u H( I# H$ J# i0 E
"*":["metadata","evm.bytecode"]
},
//EnabletheabiandopcodesoutputofMyContractdefinedinfiledef.; m+ P' D6 t: A1 @( F9 `
0 [6 l7 p; U7 C% h0 ~
"def":{
3 ~3 M1 w. s4 }( B( m
"MyContract":["abi","evm.bytecode.opcodes"]6 }5 G% d7 Q7 p" U3 ~6 O
1 f+ r2 q% x* }; i% Y( s3 `2 o
},9 m! y/ @7 J. [# R% X
; M C! d! s" t+ R! p
//Enablethesourcemapoutputofeverysinglecontract.
1 w# s% c# y+ y+ w- ]: V# a* K
"*":{
"*":["evm.bytecode.sourceMap"]
},
//EnablethelegacyASToutputofeverysinglefile.
"*":{
T- F, i! N; N6 n7 J8 c) X0 Y
"":["legacyAST"]) X) s4 \5 V8 [! G% x7 d
}8 P: i6 f% B/ ^# {
}6 ?2 {, S3 L o# D8 n; {9 k
+ E# m3 x$ X E4 T
}
}; K! }' R$ H9 v- S+ y: u+ b
9 [ z# h: o& U& B- C& O0 S6 M
输出格式说明5 h) L. ~! M. C7 {! p9 S; x P
{
% @$ u/ e& q \" S+ Z3 _
//Optional:notpresentifnoerrors/warningswereencountered7 |4 D8 d' ~2 X# v" L9 c
errors:[& s: e* i' l* X. d' A" K
) k! \; J( { @5 g d9 @0 r/ c% _& K# H
{
4 G# K6 y& k0 `! T1 {
//Optional:Locationwithinthesourcefile.
sourceLocation:{
! G- O. ]$ \( E! Y7 l
file:"sourceFile.sol",' Y4 |" E+ J& Z9 M2 R; K8 i' C3 f3 Y: D
$ t. M4 ~/ f0 k' U
start:0,) V1 e4 G. Q5 ~
end:100
4 |2 l1 e4 t2 d- f
],) p+ P/ |$ V2 p1 c$ w
9 }2 }8 L: n. v; ~" w% s9 Q9 M3 r
//Mandatory:Errortype,suchas"TypeError","InternalCompilerError","Exception",etc.
//Seebelowforcompletelistoftypes.7 h! X" ~2 w* P q( G# r; p
/ w& }6 w* d3 P6 M, F. N# ]* F4 l
type:"TypeError",; J7 h2 K; w0 O* }* z# l' f- y- e) u
- H: Z0 m. s; U
//Mandatory:Componentwheretheerrororiginated,suchas"general","ewasm",etc.- M d! O, E$ Z, X- w2 ^1 c
component:"general",
3 K, W- A \0 v" J) o
//Mandatory("error"or"warning"): e* C2 g3 [; F0 w6 I% ]
% R% v/ c8 Z( c- H7 @4 L' A, x
severity:"error",! j0 ^( q- T8 @/ j3 M
//Mandatory
2 ^4 r. ^0 R' a. Y' b
message:"Invalidkeyword"1 n# X+ ~! \! H
//Optional:themessageformattedwithsourcelocation) n1 `' A; B/ Z0 f
formattedMessage:"sourceFile.sol:100:Invalidkeyword"3 h m E9 n" D
1 q- M1 q, f! X9 A8 F2 d( A4 k
}
],
//Thiscontainsthefile-leveloutputs.Incanbelimited/filteredbytheoutputSelectionsettings.
/ ]1 y9 O1 @+ h; Z6 G4 i6 _& n0 j
sources:{1 p5 E f" Z( r- _+ u$ V6 P
"sourceFile.sol":{" q% Q. }3 u+ h/ ~9 q$ G3 c
//Identifier(usedinsourcemaps)
) r6 U* r* U6 e/ u
id:1,1 a7 l; X x2 v% t, h* \2 A
//TheASTobject5 c# I' Y0 B5 f; J+ H' ?) V1 z
ast:{},
% F9 L$ y+ P3 R, `% Z* B
//ThelegacyASTobject
legacyAST:{}
; |3 `! Z/ f Z6 g) F* J
}4 ]% q w, Z. Z8 Z
' h6 A3 b2 _3 b
},
2 z, {; j/ S! {8 e( N6 ?: ]
//Thiscontainsthecontract-leveloutputs.Itcanbelimited/filteredbytheoutputSelectionsettings.1 m) V; ?) n. F' Y8 z! @5 ~
contracts:{
"sourceFile.sol":{
//Ifthelanguageusedhasnocontractnames,thisfieldshouldequaltoanemptystring.
2 \5 d; T) b+ t$ a& ~/ `
"ContractName":{4 I) C ~) S+ M+ }
//TheEthereumContractABI.Ifempty,itisrepresentedasanemptyarray.
//Seehttps://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
abi:[],0 B! |6 k# X% Q* w' b
4 I6 k5 f, a4 B# k& N- U- V2 U l
//SeetheMetadataOutputdocumentation(serialisedJSONstring)
- w" F7 `- S6 b# Q$ C
metadata:"{...}",) p& V0 e/ ^0 B4 y
z/ w+ L+ H! F5 [: ?! S
//Userdocumentation(natspec)2 `# `3 J8 q9 Y/ E0 D! f. z
userdoc:{},
/ y: F3 u- W! T0 A4 x& t/ _! d8 W1 \
//Developerdocumentation(natspec)& l( r) ?" n1 W# j) Y3 a3 [
devdoc:{},' H9 V" R) S5 R+ ?! e0 x
) \) S3 C" L. r! _. S: n3 @* M
//Intermediaterepresentation(string)
# V) U8 ]8 e; U( k# c" U
ir:"",( P' g! m0 ~3 B; r9 k$ {
6 h" D0 I$ e! _9 R+ _2 P* X
//EVM-relatedoutputs
3 R7 m3 d9 m3 S
evm:{ v) v+ K' n. Q- |3 d0 r
//Assembly(string)
2 n: A2 b/ F0 a5 u
assembly:"",
//Old-styleassembly(object)
$ I8 w6 ^9 ]2 @% O& G7 ?
legacyAssembly:{},) `9 V% s' f6 p
//Bytecodeandrelateddetails.+ |, |% j. m: ?! J& T! ~. }3 S& K) N) U
bytecode:{/ X* Q b2 T2 L0 p
+ Y6 F/ f1 I+ Q9 D. [
//Thebytecodeasahexstring., d( g- ^1 H, a5 x2 \
object:"00fe",
//Opcodeslist(string)
opcodes:"", V3 p/ c3 u* |
//Thesourcemappingasastring.Seethesourcemappingdefinition.
1 @4 k4 f& T. Y3 g
sourceMap:"",
, }" L5 U- A7 a" X$ v/ S8 g; a
//Ifgiven,thisisanunlinkedobject.+ P* x1 w0 g! R( ?: t/ c
linkReferences:{
5 T/ P7 U' f4 B/ x: l7 {
"libraryFile.sol":{
# P6 X: f# L4 b( N$ y
//Byteoffsetsintothebytecode.Linkingreplacesthe20byteslocatedthere.
"Library1":[
. B0 S: H' y* y9 k b, O
{start:0,length:20},4 a( H) O7 u( b+ p
{start:200,length:20}
]
}
}
},* ^- ~3 b3 t3 K n( C
( l$ _. K$ G9 {0 ~' R7 ]& h* {
//Thesamelayoutasabove.
deployedBytecode:{},
//Thelistoffunctionhashes
" u# N9 k* c4 S7 a9 v7 y
methodIdentifiers:{! K' u$ k: d. f, \
; b Z& q+ E: k2 w* G; ^3 s% v1 d
"delegate(address)":"5c19a95c"/ h. k" t' X5 V9 ^1 W+ `* g
1 t# n) Z! c+ ]8 |
},: j4 w4 j* C' j# e B' V& x
//Functiongasestimates3 R- v. b+ `8 X- V4 X5 C$ l3 b9 ?
gasEstimates:{
# M" e* [; I' g) T
creation:{' W! U x5 L+ z; h5 m/ [
8 V5 S _' _# a. E; w* X8 w+ w6 ?4 F
codeDepositCost:"420000",
executionCost:"infinite",
' r2 r4 X# k1 w: y. u2 X: T
totalCost:"infinite"
},/ s( H+ M0 o2 f- b& o1 q2 O
external:{
- v, v Y( |2 E! d! f
"delegate(address)":"25000": V- ~7 c5 s6 p1 L }
' c+ N+ y# T0 ~+ d9 \! d
},2 ?" \# ?; G0 |' \: H
9 ^2 H# R+ x/ i$ f# o! p- {; I
internal:{
" |& R, a$ B$ \; M9 \ ?7 h
"heavyLifting()":"infinite"
}- [8 x3 y9 P! Y; `; Z
}
},
//eWASMrelatedoutputs
ewasm:{' o/ Y2 h- f/ j. Y' i( u" v% G8 k
9 {& W: {2 a3 o( ?" K8 h
//S-expressionsformat
( G! z5 p9 n' H& L. t' V
wast:"",7 s, c) R9 x' T' a; O
) S* o1 B3 |. w& Z% A, o
//Binaryformat(hexstring)
, Q7 d. G- Z# U/ D& w5 Q+ _
wasm:""' O5 A6 B- Q) z+ H& u: X' \* U& X# P
}
}
d6 `; i& O3 N3 q7 z
} N1 z m* Y5 S8 \
% d1 n E: G- P' W4 N
}
/ h, Q7 N1 `: V; z
}
错误类型说明:& R8 O4 X9 k2 J- w8 V7 o
( }5 w8 W S8 h/ M" {
3 z* {6 f0 D2 m. ~8 z
) Q& e0 K* M' w
JSONError:JSON错误,JSON输入不符合要求的格式,例如输入不是JSON对象,不支持语言,等等。
IOError:IO错误,IO和导入处理错误,如提供的源中的不可解析URL或hash不匹配。9 x u* m/ [0 N" _9 P. x+ f* Q
) S; S& W. \2 Q! d& X% m
ParserError:语法f分析错误,源代码不符合语言规则。
" v1 j8 a5 ]* x4 [ u
DocstringParsingError:文档解析错误,无法解析注释块中的NATSPEC标记。
SytRealError:语法错误,如continue在for循环之外使用。' X7 s: y) L/ r9 L. L
5 U1 F: q& T) M( |9 n+ j
DeclarationError:声明错误,无效、不可解析或冲突的标识符名称。例如未找到标识符
TypeError:类型错误,如无效类型转换、无效赋值等。
UnimplementedFeatureError:编译器不支持该特性,但希望在将来的版本中得到支持。
InternalCompilerError:编译器中触发内部错误,这应该作为一个问题来反馈。
3 o& y7 a8 K2 [- N) H
Exception:例外,编译过程中未知的故障,这应该作为一个问题反馈。
: Q- g! L7 c9 v/ J* W
CompilerError:编译错误,编译器堆栈的使用无效,这应该作为一个问题来反馈。1 t) F( Q$ Q T$ F4 x% r# Y, o1 e
/ @: d" {2 y/ a# p% S
FatalError:致命错误,这应该作为一个问题来反馈。# w1 ]' v; D( e+ ^: U* B: P
! S8 k+ r/ y, I
Warning:警告并没有停止编译,但如果可能的话,应该加以处理。$ r9 ^/ a* R2 O9 m8 C
. H. i# I0 K+ e
T0 w0 |8 A: d; {* D
原文请访问:solc
1 }# h r9 S5 U5 s
如果你希望马上开始学习以太坊DApp开发,推荐访问一个在线教程:8 ?$ O; q# G& f) ~, Z( Z
0 h& n& M9 L0 h% u; Z3 n
以太坊智能合约,主要介绍智能合约与dapp应用开发,适合入门。
7 L: z; W6 q* u: R
以太坊开发,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
成为第一个吐槽的人



