琉离铟落的小窝琉离铟落的小窝
← 返回文章列表
2026-06-25
#Cangjie#入门教程

数组类型

仓颉的基本数据类型

2.7.1 什么是数组?

数组(Array)是一种有序的、元素类型相同的集合。说白了,就是把一堆同一类型的东西排成一排,编上号

💡TIP

你可以把数组想象成一个鸡蛋托——每个格子只能放一个鸡蛋,所有格子放的东西类型相同(都是鸡蛋),而且每个格子有固定位置(第 1 个、第 2 个……)。

Cangjie
let scores = [95, 87, 92, 78, 88]    // 5 个 Int64 分数
let names = ["张三", "李四", "王五"]  // 3 个 String 名字
ℹ️NOTE

与元组(Tuple)不同的是,数组的所有元素必须是同一种类型,而元组的元素可以不同类型。


2.7.2 创建数组

仓颉中用 Array<T> 来表示数组类型,T 是元素的类型,T 可以是任意类型。

方式一:用字面量创建(最常用)

用方括号 [] 把值括起来,逗号分隔:

Cangjie
let a: Array<Int64> = [1, 2, 3, 4]   // 明确标注类型
let b = [1, 2, 3, 3, 2, 1]           // 让编译器自动推断为 Array<Int64>
let c: Array<String> = []            // 创建一个空数组(必须标注类型)
💡TIP

当你不写类型时,编译器会根据里面的值自动推断。比如 [1, 2, 3] 会自动推断为 Array<Int64>

方式二:用构造函数创建

Cangjie
let a = Array<Int64>()               // 创建空数组
let b = Array<Int64>(3, repeat: 0)   // 创建长度为 3 的数组,全部初始化成 0
                                     // 结果: [0, 0, 0]
ℹ️NOTE

repeat: 0 表示"用 0 重复填充"。如果 repeat 的值是引用类型(比如对象),所有元素会指向同一个对象,而不是复制多份。

方式三:用初始化函数创建

Cangjie
let c = Array<Int64>(3, { i => i + 1 })  // 结果: [1, 2, 3]

这里的 { i => i + 1 } 是一个 lambda 表达式(也叫匿名函数),你可以把它理解成一个"小公式":

给一个数字 i(下标,从 0 开始),计算出对应的值。
下标 i 公式 i + 1 结果
0 0 + 1 1
1 1 + 1 2
2 2 + 1 3
💡TIP

Lambda 表达式是仓颉中非常常用的语法,后面会专门讲解。目前你只需要知道 { i => 表达式 } 的意思是"对每个下标 i,计算出对应的元素值"。


2.7.3 访问数组元素

获取数组长度

.size 属性获取数组中有多少个元素:

Cangjie
main() {
    let arr = [0, 1, 2]
    println("数组长度是 ${arr.size}")   // 输出:数组长度是 3
}

用下标访问单个元素

数组名[下标] 的方式获取指定位置的元素。下标从 0 开始,即第一个元素是 arr[0],最后一个元素是 arr[arr.size - 1]

Cangjie
main() {
    let arr = [0, 1, 2]
    let a = arr[0]       // a == 0(第一个元素)
    let b = arr[1]       // b == 1(第二个元素)
    let c = arr[2]       // c == 2(第三个元素)
}
⚠️WARNING

下标不能是负数,也不能大于等于数组长度。比如长度为 3 的数组,只能访问 arr[0]arr[1]arr[2]。访问 arr[3]arr[-1] 会导致错误。

Cangjie
let c = arr[-1]  // ❌ 错误:下标 -1 超出范围

用 Range 访问一段元素

💡TIP

将在下一章详细介绍

如果想一次性取出数组中连续的一段,可以用 .. 范围语法:

Cangjie
let arr1 = [0, 1, 2, 3, 4, 5, 6]
let arr2 = arr1[0..5]   // 从下标 0 到 4(不包含 5),结果是 [0, 1, 2, 3, 4]

范围语法的省略写法

写法 含义 示例 结果
start..end 从 start 到 end-1 [0,1,2,3,4][1..3] [1, 2]
..end 从 0 到 end-1 [0,1,2,3,4][..3] [0, 1, 2]
start.. 从 start 到结尾 [0,1,2,3,4][2..] [2, 3, 4]
Cangjie
let arr1 = [0, 1, 2, 3, 4, 5, 6]
let arr2 = arr1[..3]   // 取前 3 个: [0, 1, 2]
let arr3 = arr1[2..]   // 从第 3 个取到末尾: [2, 3, 4, 5, 6]
🚨CAUTION

arr[n..]arr[..n] 这类省略起始或结束的切片写法仅限数组等下标访问类型使用。其中 arr[n..] 等价于 arr[n..arr.size]arr[..n] 等价于 arr[0..n]

💡TIP

记住口诀——左闭右开start..end 包含 start,不包含 end。比如 [0..5] 取的是 0、1、2、3、4,没有 5。

用 for-in 遍历所有元素

ℹ️NOTE

此处暂先死记硬背,后续文章会讲解

Cangjie
main() {
    let arr = [0, 1, 2]
    for (i in arr) {
        println("元素是 ${i}")
    }
}

运行结果:

Bash
元素是 0
元素是 1
元素是 2
ℹ️NOTE

数组会按照元素插入的顺序(即你写进去的顺序)依次遍历,顺序是固定的。


2.7.4 修改数组元素

数组创建后长度不能改变(不能增删元素),但可以修改已有元素的值

Cangjie
main() {
    let arr = [0, 1, 2, 3, 4, 5]
    arr[0] = 3                        // 把第一个元素从 0 改成 3
    println("第一个元素是 ${arr[0]}")  // 输出:第一个元素是 3
}
IMPORTANT

虽然这里 arr 是用 let 声明的(不可变变量),但数组内部存储的元素可以被修改。这是因为数组内部只是"引用"着这些数据,修改元素并不改变数组本身的引用。


2.7.5 数组的"共享引用"特性

数组在赋值时不会复制数据,而是让两个变量指向同一份数据

Cangjie
let arr1 = [0, 1, 2]
let arr2 = arr1       // arr2 和 arr1 指向同一份数据

arr2[0] = 3           // 修改 arr2 的第一个元素

// 结果:arr1 和 arr2 都被影响了!
// arr1 变成了 [3, 1, 2]
// arr2 变成了 [3, 1, 2]
💡TIP

可以把数组想象成一个共享的笔记本——小明(arr1)和小红(arr2)共用一个笔记本,谁在上面改了内容,另一个人看到的也会变。

这个特性在很多编程场景下很有用,但也要小心——如果你不希望共享数据,需要显式地复制一份。


2.7.6 值类型数组 VArray

除了上面介绍的 Array(引用类型),仓颉还提供了一个值类型数组 VArray

和普通 Array 有什么区别?

对比 Array VArray
类型 引用类型 值类型
赋值行为 共享同一份数据 复制一份新数据
长度定义 运行时确定 编译时固定
内存分配 堆上分配 栈上分配(更快)

VArray 语法

VArray<T, $N> 中的 $N 表示数组长度,编译时必须确定

Cangjie
var a: VArray<Int64, $3> = [1, 2, 3]   // 长度为 3 的 VArray

构造函数

Cangjie
// 用初始化函数:下标 i 从 0 到 4,值 = i
let b = VArray<Int64, $5>({i => i})         // [0, 1, 2, 3, 4]

// 用 repeat 填充
let c = VArray<Int64, $5>(repeat: 0)        // [0, 0, 0, 0, 0]

访问和修改

和普通 Array 一样使用下标:

Cangjie
var a: VArray<Int64, $3> = [1, 2, 3]
let i = a[1]       // i == 2
a[2] = 4           // a 变为 [1, 2, 4]
let s = a.size     // s == 3
ℹ️NOTE

VArray 是值类型,赋值时会复制数据,不会像 Array 那样共享引用:

Cangjie
var a: VArray<Int64, $3> = [1, 2, 3]
var b = a
b[0] = 99
// a 仍然是 [1, 2, 3],不受影响
// b 是 [99, 2, 3]
⚠️WARNING

由于运行时后端限制,VArray 的元素类型不能包含引用类型、枚举类型、Lambda 表达式等。这在入门阶段几乎不会遇到,了解一下有这个限制就行。


2.7.7 动手试一试 ✏️

  1. 创建一个包含你一周每天学习时长的数组(比如 [2, 1.5, 0, 3, 2, 4, 1]),然后计算一周的总学习时长。
Cangjie
main() {
    let study_hours = [2.0, 1.5, 0.0, 3.0, 2.0, 4.0, 1.0]
    var total = 0.0
    for (h in study_hours) {
        total = total + h
    }
    println("一周总学习时长:${total} 小时")
}
  1. 创建一个数组 [10, 20, 30, 40, 50],尝试用不同的下标范围语法取出 [20, 30, 40] 这一段。
💡TIP

提示:[20, 30, 40] 在原数组中的下标范围是从 1 到 3(不包含 4),所以是 arr[1..4]。试试用 arr[1..]arr[..4] 看看结果有什么不同?


2.7.8 小结

概念 要点
什么是数组 相同类型元素的有序集合
类型写法 Array<T>T 是元素类型
创建方式 字面量 [1, 2, 3]、构造函数 Array<Int64>(3, repeat: 0)、初始化函数
访问元素 arr[i],下标从 0 开始
范围访问 arr[start..end],左闭右开
数组长度 arr.size
修改元素 arr[i] = 新值,但不能增删
共享引用 数组赋值不复制数据,两个变量指向同一份数据
值类型数组 VArray<T, $N>,长度编译时固定,赋值时复制数据

2.7.9 常见问题

Q1:`arr[arr.size]` 会怎样?

会报错!因为下标从 0 开始,最后一个元素的下标是 arr.size - 1。访问 arr[arr.size] 就是越界了。

Q2:能创建一个包含不同类型元素的数组吗?

不能!数组要求所有元素类型相同。如果你需要不同类型的数据,可以使用元组(Tuple)。

Q3:`Array` 和 `VArray` 我应该用哪个?

  • 日常开发:用 Array 就足够了,灵活方便
  • 性能敏感场景:如果数组长度固定且很小(比如几百以内),可以考虑 VArray
  • 入门阶段:先用 Array,等对值类型和引用类型有更深理解后再了解 VArray

Q4:数组下标为什么不从 1 开始?

这是一个历史悠久的编程惯例。从 0 开始有很多好处,比如 arr[0] 就表示第一个元素,偏移量为 0。你熟悉了就会觉得自然。