请注意,当复制数组时,其成员的“引用状态”将被保留 (https://php.net/manual/en/language.references.whatdo.php).
PHP 中的 数组 实际上是有序映射。映射是一种将值与键关联的类型。这种类型针对多种用途进行了优化;它可以被视为数组、列表(向量)、哈希表(映射的实现)、字典、集合、堆栈、队列,以及可能更多。由于 数组 值可以是其他 数组,因此树和多维 数组 也是可能的。
对这些数据结构的解释超出了本手册的范围,但每个结构至少提供了一个示例。有关更多信息,请参考大量关于此广泛主题的文献。
可以使用 array() 语言结构创建 数组。它接受任意数量以逗号分隔的 键 => 值
对作为参数。
array( key => value, key2 => value2, key3 => value3, ... )
最后一个数组元素后面的逗号是可选的,可以省略。这通常用于单行数组,即 array(1, 2)
比 array(1, 2, )
更受欢迎。另一方面,对于多行数组,通常使用尾随逗号,因为它允许在末尾更容易地添加新元素。
注意:
存在一个简短的数组语法,它用
[]
替换array()
。
示例 #1 简单数组
<?php
$array = array(
"foo" => "bar",
"bar" => "foo",
);
// 使用简短数组语法
$array = [
"foo" => "bar",
"bar" => "foo",
];
?>
此外,还会发生以下 键 转换
+
符号,将转换为 整数 类型。例如,键 "8"
实际上将在 8
下存储。另一方面,"08"
不会被转换,因为它不是有效的十进制整数。
8.7
实际上将在 8
下存储。
true
实际上将在 1
下存储,而键 false
将在 0
下存储。
null
实际上将在 ""
下存储。
非法偏移量类型
。
如果数组声明中的多个元素使用相同的键,则只使用最后一个元素,因为所有其他元素都被覆盖。
示例 #2 类型转换和覆盖示例
<?php
$array = array(
1 => "a",
"1" => "b",
1.5 => "c",
true => "d",
);
var_dump($array);
?>
上面的示例将输出
array(1) { [1]=> string(1) "d" }
由于上面示例中的所有键都被转换为 1
,因此值将在每个新元素上被覆盖,而最后一个分配的值 "d"
是唯一剩下的值。
PHP 数组可以同时包含 整数 和 字符串 键,因为 PHP 不区分索引数组和关联数组。
<?php
$array = array(
"foo" => "bar",
"bar" => "foo",
100 => -100,
-100 => 100,
);
var_dump($array);
?>
上面的示例将输出
array(4) { ["foo"]=> string(3) "bar" ["bar"]=> string(3) "foo" [100]=> int(-100) [-100]=> int(100) }
键 是可选的。如果未指定,PHP 将使用之前使用的最大 整数 键的增量。
示例 #4 没有键的索引数组
<?php
$array = array("foo", "bar", "hello", "world");
var_dump($array);
?>
上面的示例将输出
array(4) { [0]=> string(3) "foo" [1]=> string(3) "bar" [2]=> string(5) "hello" [3]=> string(5) "world" }
可以仅为某些元素指定键,而为其他元素省略键
示例 #5 不是所有元素都有键
<?php
$array = array(
"a",
"b",
6 => "c",
"d",
);
var_dump($array);
?>
上面的示例将输出
array(4) { [0]=> string(1) "a" [1]=> string(1) "b" [6]=> string(1) "c" [7]=> string(1) "d" }
如您所见,最后一个值 "d"
被分配了键 7
。这是因为之前的最大整数键是 6
。
示例 #6 复杂的类型转换和覆盖示例
此示例包含所有键类型转换和元素覆盖的变体。
<?php
$array = array(
1 => 'a',
'1' => 'b', // 值 "a" 将被 "b" 覆盖
1.5 => 'c', // 值 "b" 将被 "c" 覆盖
-1 => 'd',
'01' => 'e', // 由于这不是一个整数字符串,所以它不会覆盖键 1
'1.5' => 'f', // 由于这不是一个整数字符串,所以它不会覆盖键 1
true => 'g', // 值 "c" 将被 "g" 覆盖
false => 'h',
'' => 'i',
null => 'j', // 值 "i" 将被 "j" 覆盖
'k', // 值 "k" 被分配了键 2。这是因为在此之前最大的整数键是 1
2 => 'l', // 值 "k" 将被 "l" 覆盖
);
var_dump($array);
?>
上面的示例将输出
array(7) { [1]=> string(1) "g" [-1]=> string(1) "d" ["01"]=> string(1) "e" ["1.5"]=> string(1) "f" [0]=> string(1) "h" [""]=> string(1) "j" [2]=> string(1) "l" }
可以使用 array[key]
语法访问数组元素。
示例 #7 访问数组元素
<?php
$array = array(
"foo" => "bar",
42 => 24,
"multi" => array(
"dimensional" => array(
"array" => "foo"
)
)
);
var_dump($array["foo"]);
var_dump($array[42]);
var_dump($array["multi"]["dimensional"]["array"]);
?>
上面的示例将输出
string(3) "bar" int(24) string(3) "foo"
注意:
在 PHP 8.0.0 之前,方括号和花括号可以互换使用来访问数组元素(例如,
$array[42]
和$array{42}
在上面的示例中都会做相同的事情)。从 PHP 7.4.0 开始,花括号语法已被弃用,从 PHP 8.0.0 开始不再支持。
示例 #8 数组解引用
<?php
function getArray() {
return array(1, 2, 3);
}
$secondElement = getArray()[1];
?>
注意:
尝试访问尚未定义的数组键与访问任何其他未定义的变量相同:将发出 **
E_WARNING
** 级别错误消息(在 PHP 8.0.0 之前为 **E_NOTICE
** 级别),结果将为 **null
**。
注意:
对不是 string 的标量值进行数组解引用将产生 **
null
**。在 PHP 7.4.0 之前,这不会发出错误消息。从 PHP 7.4.0 开始,这会发出 **E_NOTICE
**;从 PHP 8.0.0 开始,这会发出 **E_WARNING
**。
可以通过在现有 array 中显式设置值来修改它。
这是通过将值分配给 array 来完成的,在方括号中指定键。键也可以省略,这将导致一对空括号 ([]
)。
$arr[key] = value; $arr[] = value; // key may be an int or string // value may be any value of any type
如果 $arr 还不存在或被设置为 **null
** 或 **false
**,它将被创建,所以这也是创建 array 的另一种方法。然而,这种做法不鼓励,因为如果 $arr 已经包含一些值(例如,来自请求变量的 string),那么这个值将保留在原位,而 []
实际上可能代表 字符串访问运算符。最好始终通过直接赋值来初始化变量。
注意: 从 PHP 7.1.0 开始,对字符串应用空索引运算符会抛出致命错误。以前,字符串会静默地转换为数组。
注意: 从 PHP 8.1.0 开始,从 **
false
** 值创建新数组已被弃用。从 **null
** 和未定义的值创建新数组仍然允许。
要更改某个值,请使用它的键将新值分配给该元素。要删除键值对,请在它上面调用 unset() 函数。
<?php
$arr = array(5 => 1, 12 => 2);
$arr[] = 56; // 这与 $arr[13] = 56 相同;
// 在脚本的这个点
$arr["x"] = 42; // 这将一个新元素添加到
// 数组,键为 "x"
unset($arr[5]); // 这将从数组中删除该元素
unset($arr); // 这将删除整个数组
?>
注意:
如上所述,如果没有指定键,将采用现有 int 索引的最大值,新键将是该最大值加 1(但至少为 0)。如果还没有 int 索引,则键将为
0
(零)。请注意,用于此的最高整数键 *不需要当前存在于 array 中*。它只需要在上次重新索引 array 之后,在 array 中存在过。以下示例说明了这一点
<?php
// 创建一个简单的数组。
$array = array(1, 2, 3, 4, 5);
print_r($array);
// 现在删除每个项目,但保留数组本身不变:
foreach ($array as $i => $value) {
unset($array[$i]);
}
print_r($array);
// 附加一个项目(注意,新键是 5,而不是 0)。
$array[] = 6;
print_r($array);
// 重新索引:
$array = array_values($array);
$array[] = 7;
print_r($array);
?>上面的示例将输出
Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 ) Array ( ) Array ( [5] => 6 ) Array ( [0] => 6 [1] => 7 )
可以使用 []
(从 PHP 7.1.0 开始)或 list() 语言结构对数组进行解构。这些结构可以用来将数组解构为不同的变量。
<?php
$source_array = ['foo', 'bar', 'baz'];
[$foo, $bar, $baz] = $source_array;
echo $foo; // 输出 "foo"
echo $bar; // 输出 "bar"
echo $baz; // 输出 "baz"
?>
数组解构可以在 foreach 中使用,在遍历多维数组时解构数组。
<?php
$source_array = [
[1, 'John'],
[2, 'Jane'],
];
foreach ($source_array as [$id, $name]) {
// 这里使用 $id 和 $name 进行逻辑处理
}
?>
如果未提供变量,数组元素将被忽略。数组解构始终从索引 0
开始。
<?php
$source_array = ['foo', 'bar', 'baz'];
// 将索引 2 处的元素赋值给变量 $baz
[, , $baz] = $source_array;
echo $baz; // 输出 "baz"
?>
从 PHP 7.1.0 开始,关联数组也可以被解构。这也可以更轻松地从数字索引数组中选择正确的元素,因为可以明确指定索引。
<?php
$source_array = ['foo' => 1, 'bar' => 2, 'baz' => 3];
// 将索引 'baz' 处的元素赋值给变量 $three
['baz' => $three] = $source_array;
echo $three; // 输出 3
$source_array = ['foo', 'bar', 'baz'];
// 将索引 2 处的元素赋值给变量 $baz
[2 => $baz] = $source_array;
echo $baz; // 输出 "baz"
?>
数组解构可以用来方便地交换两个变量。
<?php
$a = 1;
$b = 2;
[$b, $a] = [$a, $b];
echo $a; // 输出 2
echo $b; // 输出 1
?>
注意:
展开运算符 (
...
) 在赋值中不支持。
注意:
尝试访问尚未定义的数组键与访问任何其他未定义的变量相同:将发出 **
E_WARNING
** 级别错误消息(在 PHP 8.0.0 之前为 **E_NOTICE
** 级别),结果将为 **null
**。
有很多有用的函数用于处理数组。请参见 数组函数 部分。
注意:
该 unset() 函数允许从 数组 中删除键。请注意,数组 *不会* 被重新索引。如果需要真正的“删除和移动”行为,则可以 数组 使用 array_values() 函数重新索引。
<?php
$a = array(1 => 'one', 2 => 'two', 3 => 'three');
unset($a[2]);
/* 将生成一个数组,该数组将被定义为
$a = array(1 => 'one', 3 => 'three');
而 *不是*
$a = array(1 => 'one', 2 =>'three');
*/
$b = array_values($a);
// 现在 $b 是 array(0 => 'one', 1 =>'three')
?>
$foo[bar]
是错误的?始终在字符串文字数组索引周围使用引号。例如,$foo['bar']
是正确的,而 $foo[bar]
则不是。但为什么呢?在旧脚本中经常会遇到这种语法
<?php
$foo[bar] = 'enemy';
echo $foo[bar];
// 等等
?>
这是错误的,但它有效。原因是这段代码有一个未定义的常量 (bar
),而不是一个 字符串 ('bar'
- 注意引号)。它之所以有效,是因为 PHP 会自动将 *裸字符串*(一个未引用的 字符串,它不对应于任何已知符号)转换为一个包含裸 字符串 的 字符串。例如,如果不存在名为 **bar
** 的定义常量,那么 PHP 将替换为 字符串 'bar'
并使用它。
将未定义的常量作为裸字符串的回退会发出级别为 **E_NOTICE
** 的错误。从 PHP 7.2.0 开始,此行为已弃用,并会发出级别为 **E_WARNING
** 的错误。从 PHP 8.0.0 开始,此行为已移除,并会抛出 Error 异常。
注意: 这并不意味着要 *始终* 对键加引号。不要对作为 常量 或 变量 的键加引号,因为这会阻止 PHP 解释它们。
<?php
error_reporting(E_ALL);
ini_set('display_errors', true);
ini_set('html_errors', false);
// 简单数组:
$array = array(1, 2);
$count = count($array);
for ($i = 0; $i < $count; $i++) {
echo "\nChecking $i: \n";
echo "Bad: " . $array['$i'] . "\n";
echo "Good: " . $array[$i] . "\n";
echo "Bad: {$array['$i']}\n";
echo "Good: {$array[$i]}\n";
}
?>上面的示例将输出
Checking 0: Notice: Undefined index: $i in /path/to/script.html on line 9 Bad: Good: 1 Notice: Undefined index: $i in /path/to/script.html on line 11 Bad: Good: 1 Checking 1: Notice: Undefined index: $i in /path/to/script.html on line 9 Bad: Good: 2 Notice: Undefined index: $i in /path/to/script.html on line 11 Bad: Good: 2
更多示例来演示这种行为
<?php
// 显示所有错误
error_reporting(E_ALL);
$arr = array('fruit' => 'apple', 'veggie' => 'carrot');
// 正确
print $arr['fruit']; // apple
print $arr['veggie']; // carrot
// 错误。这能运行但也会抛出一个 PHP 错误,级别为 E_NOTICE,因为
// 一个名为 fruit 的未定义常量。
//
// Notice: Use of undefined constant fruit - assumed 'fruit' in...
print $arr[fruit]; // apple
// 这定义了一个常量来演示正在发生的事情。值 'veggie'
// 被赋予了一个名为 fruit 的常量。
define('fruit', 'veggie');
// 注意现在不同了
print $arr['fruit']; // apple
print $arr[fruit]; // carrot
// 以下操作没问题,因为是在字符串中。常量不会在字符串内查找,
// 所以这里不会出现 E_NOTICE
print "Hello $arr[fruit]"; // Hello apple
// 只有一个例外:字符串中包围数组的大括号允许常量
// 被解释
print "Hello {$arr[fruit]}"; // Hello carrot
print "Hello {$arr['fruit']}"; // Hello apple
// 这不会运行,并将导致解析错误,例如:
// Parse error: parse error, expecting T_STRING' or T_VARIABLE' or T_NUM_STRING'
// 当然,这也适用于在字符串中使用超级全局变量
print "Hello $arr['fruit']";
print "Hello $_GET['foo']";
// 连接是另一个选择
print "Hello " . $arr['fruit']; // Hello apple
?>
当 error_reporting 设置为显示 E_NOTICE
级别错误(例如,设置为 E_ALL
)时,此类用法将立即显现。默认情况下,error_reporting 设置为不显示通知。
如 语法 部分所述,方括号 ('[
' 和 ']
') 内必须是表达式。这意味着以下代码可以运行
<?php
echo $arr[somefunc($bar)];
?>
这是一个将函数返回值用作数组索引的示例。PHP 也知道常量
<?php
$error_descriptions[E_ERROR] = "A fatal error has occurred";
$error_descriptions[E_WARNING] = "PHP issued a warning";
$error_descriptions[E_NOTICE] = "This is just an informal notice";
?>
请注意,E_ERROR
也是一个有效的标识符,就像第一个示例中的 bar
一样。但最后一个示例实际上与编写以下内容相同
<?php
$error_descriptions[1] = "A fatal error has occurred";
$error_descriptions[2] = "PHP issued a warning";
$error_descriptions[8] = "This is just an informal notice";
?>
因为 E_ERROR
等于 1
等。
对于任何 int、float、string、bool 和 resource 类型,将值转换为 数组 会得到一个数组,该数组只有一个元素,索引为零,值是转换的标量。换句话说,(array)$scalarValue
与 array($scalarValue)
完全相同。
如果将 对象 转换为 数组,结果是一个 数组,其元素是 对象 的属性。键是成员变量名,有一些值得注意的例外:整数属性无法访问;私有变量在变量名前面有类名;受保护的变量在变量名前面有 '*'。这些前缀值在两侧都有 NUL
字节。未初始化的 类型化属性 会被静默丢弃。
<?php
class A {
private $B;
protected $C;
public $D;
function __construct()
{
$this->{1} = null;
}
}
var_export((array) new A());
?>
上面的示例将输出
array ( '' . "\0" . 'A' . "\0" . 'B' => NULL, '' . "\0" . '*' . "\0" . 'C' => NULL, 'D' => NULL, 1 => NULL, )
这些 NUL
会导致一些意想不到的行为
<?php
class A {
private $A; // 这将变为 '\0A\0A'
}
class B extends A {
private $A; // 这将变为 '\0B\0A'
public $AA; // 这将变为 'AA'
}
var_dump((array) new B());
?>
上面的示例将输出
array(3) { ["BA"]=> NULL ["AA"]=> NULL ["AA"]=> NULL }
上面的代码看起来有两个名为 'AA' 的键,但其中一个实际上名为 '\0A\0A'。
可以使用 array_diff() 函数和 数组运算符 来比较数组。
以 ...
为前缀的数组在数组定义期间将在原位展开。只有数组和实现了 Traversable 的对象才能展开。使用 ...
的数组解包从 PHP 7.4.0 开始可用。
可以多次展开,并在 ...
运算符之前或之后添加普通元素
示例 #9 简单的数组解包
<?php
// 使用短数组语法。
// 同时,也支持 array() 语法。
$arr1 = [1, 2, 3];
$arr2 = [...$arr1]; //[1, 2, 3]
$arr3 = [0, ...$arr1]; //[0, 1, 2, 3]
$arr4 = [...$arr1, ...$arr2, 111]; //[1, 2, 3, 1, 2, 3, 111]
$arr5 = [...$arr1, ...$arr1]; //[1, 2, 3, 1, 2, 3]
function getArr() {
return ['a', 'b'];
}
$arr6 = [...getArr(), 'c' => 'd']; //['a', 'b', 'c' => 'd']
?>
使用 ...
运算符解包数组遵循 array_merge() 函数的语义。 也就是说,后面的字符串键会覆盖前面的字符串键,整数键会被重新编号。
示例 #10 具有重复键的数组解包
<?php
// 字符串键
$arr1 = ["a" => 1];
$arr2 = ["a" => 2];
$arr3 = ["a" => 0, ...$arr1, ...$arr2];
var_dump($arr3); // ["a" => 2]
// 整数键
$arr4 = [1, 2, 3];
$arr5 = [4, 5, 6];
$arr6 = [...$arr4, ...$arr5];
var_dump($arr6); // [1, 2, 3, 4, 5, 6]
// 等同于 [0 => 1, 1 => 2, 2 => 3, 3 => 4, 4 => 5, 5 => 6]
// 其中原始的整数键没有被保留。
?>
注意:
既不是整数也不是字符串的键会抛出 TypeError。 这样的键只能由 Traversable 对象生成。
注意:
在 PHP 8.1 之前,不支持解包包含字符串键的数组。
<?php
$arr1 = [1, 2, 3];
$arr2 = ['a' => 4];
$arr3 = [...$arr1, ...$arr2];
// 致命错误:未捕获的错误:无法在 example.php:5 中解包包含字符串键的数组
$arr4 = [1, 2, 3];
$arr5 = [4, 5];
$arr6 = [...$arr4, ...$arr5]; // 可行。 [1, 2, 3, 4, 5]
?>
PHP 中的数组类型非常灵活。 以下是一些示例。
<?php
// 以下:
$a = array( 'color' => 'red',
'taste' => 'sweet',
'shape' => 'round',
'name' => 'apple',
4 // 键将为 0
);
$b = array('a', 'b', 'c');
// ... 与以下完全等效:
$a = array();
$a['color'] = 'red';
$a['taste'] = 'sweet';
$a['shape'] = 'round';
$a['name'] = 'apple';
$a[] = 4; // 键将为 0
$b = array();
$b[] = 'a';
$b[] = 'b';
$b[] = 'c';
// 在执行上述代码后,$a 将是数组
// array('color' => 'red', 'taste' => 'sweet', 'shape' => 'round',
// 'name' => 'apple', 0 => 4),$b 将是数组
// array(0 => 'a', 1 => 'b', 2 => 'c'),或者简写为 array('a', 'b', 'c')。
?>
示例 #11 使用 array()
<?php
// 数组作为(属性)映射
$map = array( 'version' => 4,
'OS' => 'Linux',
'lang' => 'english',
'short_tags' => true
);
// 严格的数字键
$array = array( 7,
8,
0,
156,
-10
);
// 与 array(0 => 7, 1 => 8, ...) 相同
$switching = array( 10, // 键 = 0
5 => 6,
3 => 7,
'a' => 4,
11, // 键 = 6(整数索引的最大值为 5)
'8' => 2, // 键 = 8(整数!)
'02' => 77, // 键 = '02'
0 => 12 // 值 10 将被 12 覆盖
);
// 空数组
$empty = array();
?>
示例 #12 集合
<?php
$colors = array('red', 'blue', 'green', 'yellow');
foreach ($colors as $color) {
echo "你喜欢 $color吗?\n";
}
?>
上面的示例将输出
Do you like red? Do you like blue? Do you like green? Do you like yellow?
可以通过引用传递的方式直接修改 数组 的值。
示例 #13 在循环中修改元素
<?php
foreach ($colors as &$color) {
$color = mb_strtoupper($color);
}
unset($color); /* 确保对 $color 的后续写入不会修改最后一个数组元素 */
print_r($colors);
?>
上面的示例将输出
Array ( [0] => RED [1] => BLUE [2] => GREEN [3] => YELLOW )
此示例创建了一个从 1 开始的数组。
示例 #14 从 1 开始的索引
<?php
$firstquarter = array(1 => 'January', 'February', 'March');
print_r($firstquarter);
?>
上面的示例将输出
Array ( [1] => 'January' [2] => 'February' [3] => 'March' )
示例 #15 填充数组
<?php
// 用目录中的所有项目填充一个数组
$handle = opendir('.');
while (false !== ($file = readdir($handle))) {
$files[] = $file;
}
closedir($handle);
?>
数组是有序的。可以使用各种排序函数更改顺序。有关更多信息,请参阅数组函数部分。 count()函数可用于计算数组中的项目数。
示例 #16 排序数组
<?php
sort($files);
print_r($files);
?>
因为数组的值可以是任何东西,所以它也可以是另一个数组。这使得可以创建递归和多维数组。
示例 #17 递归和多维数组
<?php
$fruits = array ( "fruits" => array ( "a" => "orange",
"b" => "banana",
"c" => "apple"
),
"numbers" => array ( 1,
2,
3,
4,
5,
6
),
"holes" => array ( "first",
5 => "second",
"third"
)
);
// 一些用于访问上述数组中的值的示例
echo $fruits["holes"][5]; // 打印 "second"
echo $fruits["fruits"]["a"]; // 打印 "orange"
unset($fruits["holes"][0]); // 移除 "first"
// 创建一个新的多维数组
$juices["apple"]["green"] = "good";
?>
<?php
$arr1 = array(2, 3);
$arr2 = $arr1;
$arr2[] = 4; // $arr2 已更改,
// $arr1 仍然是 array(2, 3)
$arr3 = &$arr1;
$arr3[] = 4; // 现在 $arr1 和 $arr3 相同
?>
请注意,当复制数组时,其成员的“引用状态”将被保留 (https://php.net/manual/en/language.references.whatdo.php).
我认为你的第一个主要示例是毫无必要地令人困惑,对于新手来说非常令人困惑
$array = array(
"foo" => "bar",
"bar" => "foo",
);
它应该被删除。
对于新手
数组索引可以是任何字符串值,甚至可以是数组中也作为值存在的的值。
array["foo"] 的值为 "bar"。
array["bar"] 的值为 "foo"
以下表达式都是真的
$array["foo"] == "bar"
$array["bar"] == "foo"
"如果你将一个 NULL 值转换为数组,你将得到一个空数组。"
事实证明,这是一个有用的属性。假设你有一个搜索函数,它在成功时返回一个值数组,如果没有找到任何值则返回 NULL。
<?php $values = search(...); ?>
现在你想将该数组与另一个数组合并。如果 $values 为 NULL,我们该怎么办?没问题
<?php $combined = array_merge((array)$values, $other); ?>
瞧。
注意,如果你在 $_POST 数组中使用字符串作为索引,那么句点将被转换为下划线
<html>
<body>
<?php
printf("POST: "); print_r($_POST); printf("<br/>");
?>
<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
<input type="hidden" name="Windows3.1" value="Sux">
<input type="submit" value="Click" />
</form>
</body>
</html>
单击按钮后,页面将显示以下内容
POST: Array ( [Windows3_1] => Sux )
注意,数组值桶是引用安全的,即使通过序列化也是如此。
<?php
$x='initial';
$test=array('A'=>&$x,'B'=>&$x);
$test=unserialize(serialize($test));
$test['A']='changed';
echo $test['B']; // 输出 "changed"
?>
这在某些情况下可能很有用,例如在复杂的结构中节省内存。