{"content":{"title":"Solidity 数组与字符串简介","body":"在本节中，我们将介绍数组数据结构和字符串数据结构。这些行为与我们之前讨论的 Solidity 数据类型有所不同，所以我们将在这里讨论它们。\r\n\r\n数组声明的语法\r\n\r\n让我们看一个处理数组并返回数组的函数。这里有很多内容需要解读！\r\n\r\n首先，应该清楚的是，声明一个数字数组的语法是 `uint256[]`。我们稍后会讨论 `calldata` 和 `memory`。\r\n\r\n```solidity\r\ncontract ExampleContract {\r\n    function useArrayForUint256(uint256[] calldata input)\r\n        public\r\n        pure\r\n        returns (uint256[] memory) {\r\n            return input;\r\n    }\r\n}\r\n```\r\n\r\n如果你想要一个地址或布尔值的数组，则为以下内容：\r\n\r\n```solidity\r\ncontract ExampleContract {\r\n    function booleanArrayExample(bool[] calldata input)\r\n        public\r\n        pure\r\n        returns (bool[] memory) {\r\n            return input;\r\n    }\r\n\r\n    function addressArrayExample(address[] calldata input)\r\n        public\r\n        pure\r\n        returns (address[] memory) {\r\n            return input;\r\n    }\r\n}\r\n```\r\n\r\n那么 `calldata` 和 `memory` 是什么呢？首先，如果不包括它们，代码将无法编译。以下是两段无法编译的代码示例。\r\n\r\n```solidity\r\ncontract BadContract1 {\r\n\r\n    // 参数缺少 calldata\r\n    function useArrayForUint256(uint256[] input)\r\n        public\r\n        pure\r\n        returns (uint256[] memory) {\r\n            return input;\r\n    }\r\n}\r\n```\r\n\r\n```solidity\r\ncontract BadContract2 {\r\n\r\n    // 返回类型缺少 memory\r\n    function useArrayForUint256(uint256[] calldata input)\r\n        public\r\n        pure\r\n        returns (uint256[]) {\r\n            return input;\r\n    }\r\n}\r\n```\r\n\r\n那么 `calldata` 和 `memory` 是什么？\r\n\r\n如果你熟悉 C 或 C++，这个概念将是直观的。在 Solidity 中，内存就像 C、C++ 或 Rust 中的堆。数组可以具有无限大小，因此将其存储在执行栈中（如果你不知道那是什么也没关系），可能会导致 _stackoverflow_ 错误（不要与著名的论坛混淆！）。\r\n\r\nCalldata 是 Solidity 特有的。它是当有人将交易发送到区块链时实际的“交易数据”。\r\n\r\nCalldata 意味着“引用以太坊交易本身的数据。”这个概念相对高级，所以如果你现在无法完全理解也没关系。\r\n\r\n当有疑问时：数组和字符串的函数参数应为 calldata，而返回类型的函数参数应为 memory。\r\n\r\n在函数参数使用 `calldata` 还有一些例外，但数组的返回类型应该始终是 memory，而不是 calldata，否则代码无法编译。为了不让你感到信息过载，关于 calldata 的例外，我们将稍后讨论。\r\n\r\n下面是如何在 Remix 中使用数字数组。\r\n\r\n![Untitled 1](https://img.learnblockchain.cn/2025/02/26/99926238_image.jpg)\r\n\r\n**数组是零索引的，像其他语言一样**\r\n\r\n这里没有惊喜。\r\n\r\n```solidity\r\ncontract ExampleContract {\r\n    function returnFirstElement(uint256[] calldata myArray)\r\n        public\r\n        pure\r\n        returns (uint256) {\r\n            uint256 first = myArray[0];\r\n            return first;\r\n    }\r\n}\r\n```\r\n\r\n注意返回类型是 uint256，因为我们返回的是一个数字，而不是数组。\r\n\r\n注意如果数组为空，交易将会回滚。\r\n\r\n**要获取数组的长度，使用 `.length`**\r\n\r\n这与 JavaScript 是一样的。\r\n\r\n```solidity\r\ncontract ExampleContract {\r\n    function returnFirstElement(uint256[] calldata myArray)\r\n        public\r\n        pure\r\n        returns (uint256) {\r\n            uint256 len = myArray.length;\r\n            return len;\r\n    }\r\n}\r\n```\r\n\r\n这也是你可以循环遍历数组的方式。\r\n\r\n```solidity\r\ncontract ExampleContract {\r\n    function productOfarray(uint256[] calldata myArray)\r\n        public\r\n        pure\r\n        returns (uint256) {\r\n            uint256 product = 1;\r\n            for (uint256 i = 0; i < myArray.length; i++) {\r\n                product *= myArray[i];\r\n            }\r\n            return product;\r\n    }\r\n}\r\n```\r\n\r\n**数组可以声明为固定长度**\r\n\r\n在之前的示例中，声明时方括号内没有内容。如果你想强制数组具有固定大小，可以在方括号内放入大小。\r\n\r\n```solidity\r\ncontract ExampleContract {\r\n    function productOfarray(uint256[5] calldata myArray)\r\n        public\r\n        pure\r\n        returns (uint256) {\r\n            uint256 last = myArray[4];\r\n            return last;\r\n    }\r\n}\r\n```\r\n\r\n如果函数传入的数组大小不是 5，则会回滚。\r\n\r\n**字符串**\r\n\r\n字符串的行为与数组非常相似。事实上，在底层它们就是数组（但有一些差异）。下面是一个返回你传入的字符串的函数。\r\n\r\n```solidity\r\ncontract ExampleContract {\r\n    function echo(string calldata input)\r\n        public\r\n        pure\r\n        returns (string memory) {\r\n            return input;\r\n    }\r\n}\r\n```\r\n\r\n这是 _hello world_ 最终版本。\r\n\r\n```solidity\r\ncontract ExampleContract {\r\n    function helloWorld()\r\n        public\r\n        pure\r\n        returns (string memory) {\r\n            return \"Hello, world!\";\r\n    }\r\n}\r\n```\r\n\r\n**字符串连接**\r\n\r\n有趣的是，Solidity 在2022年2月之前并不支持字符串连接，当时 Solidity 0.8.12 版本发布。如果你想在 Solidity 中进行字符串连接，请确保文件顶部的 pragma 至少为 0.8.12。\r\n\r\n```solidity\r\npragma solidity ^0.8.12;\r\ncontract ExampleContract {\r\n    function useArrays(string calldata user)\r\n        public\r\n        pure\r\n        returns(string memory) {\r\n            return string.concat(\"hello \", user);\r\n    }\r\n}\r\n```\r\n\r\n字符串连接的支持被如此晚加入是有原因的，因为智能合约通常处理数字，而不是字符串。\r\n\r\n**字符串无法索引**\r\n\r\n在 JavaScript 或 Python 等语言中，你可以像处理数组一样索引字符串并获取一个字符。Solidity 不能这样做。以下代码无法编译。\r\n\r\n```solidity\r\npragma solidity ^0.8.12;\r\ncontract BadContract {\r\n    function useArrays(string calldata input)\r\n        public\r\n        pure\r\n        returns(string memory) {\r\n            return input[0]; // 错误\r\n    }\r\n}\r\n```\r\n\r\n**字符串不支持长度**\r\n\r\nSolidity 不支持获取字符串的长度。这是因为 unicode 字符可能导致长度模糊不清，而 Solidity 将字符串表示为字节数组，而不是字符序列。\r\n\r\n```solidity\r\npragma solidity ^0.8.12;\r\ncontract StringContract {\r\n    function useArrays(string calldata input)\r\n        public\r\n        pure\r\n        returns(uint256) {\r\n            return input.length; // 不编译\r\n    }\r\n}\r\n```\r\n\r\n**我们遗漏的内容**\r\n\r\n- Solidity 中的数组支持像 pop() 这样的操作，但这会产生更高级的副作用，因此我们会在之后讲解。\r\n- 在函数内部声明数组和字符串，与在参数或返回值中声明它们的语法不同。\r\n\r\n**练习题**\r\n\r\n[FizzBuzz](https://github.com/RareSkills/Solidity-Exercises/tree/main/FizzBuzz)\r\n\r\n[SumArray](https://github.com/RareSkills/Solidity-Exercises/tree/main/SumArray)\r\n\r\n[FilterOddNumbers](https://github.com/RareSkills/Solidity-Exercises/tree/main/FilterOddNumbers)\r\n\r\n[IsSorted](https://github.com/RareSkills/Solidity-Exercises/tree/main/IsSorted)\r\n\r\n[Mean](https://github.com/RareSkills/Solidity-Exercises/tree/main/Mean)\r\n\r\n###   学习更多\r\n\r\n查看  [集训营](https://learnblockchain.cn/openspace/1)，以了解有关智能合约开发和代币标准的更多信息。\r\n\r\n \r\n\r\n>- 原文链接： [rareskills.io/learn-soli...](https://www.rareskills.io/learn-solidity/arrays)\r\n>- 登链社区 AI 助手，为大家转译优秀英文文章，如有翻译不通的地方，还请包涵～"},"author":{"user":"https://learnblockchain.cn/people/20722","address":null},"history":null,"timestamp":1740576387,"version":1}