复制与借用
默认是复制
在 cpro 里,赋值和传参默认都是复制:
cpro
bl a = "hello"
bl b = a // b 是 a 的独立副本
b = "world"
dayin(a) // hello,a 不受影响cpro
变量 a = "hello"
变量 b = a // b 是 a 的独立副本
b = "world"
打印(a) // hello,a 不受影响cpro
var a = "hello"
var b = a // b 是 a 的独立副本
b = "world"
print(a) // hello,a 不受影响字符串、数组、结构体都一样,复制出来的值和原值互不影响。变量离开作用域时,它占用的内存会自动释放,不需要手动管理,也没有垃圾回收。
复制是安全的,但字符串、数组这类值复制一次要分配内存、拷贝数据。不想复制时有三种做法:
| 想干什么 | 怎么写 |
|---|---|
| 让函数读一下、改一下,用完还回来 | 借用 &(本页) |
| 把值彻底交出去,自己不再用 | yidong(移动 / move) |
| 值不用了,提前释放内存 | xiaohui(销毁 / destroy) |
借用
在形参类型前加 &,函数就借用调用方的变量:不复制,直接读写原来那一份。调用时实参前也要写 &:
cpro
hs show(s &zifuchuan) kongbai {
dayin(s) // 直接当 zifuchuan 用
}
rukou() {
bl a = "hello"
show(&a) // 把 a 借出去,不复制
dayin(a) // 用完还回来,a 照常可用
}cpro
函数 show(s &string) void {
打印(s) // 直接当 string 用
}
入口() {
变量 a = "hello"
show(&a) // 把 a 借出去,不复制
打印(a) // 用完还回来,a 照常可用
}cpro
fn show(s &string) void {
print(s) // 直接当 string 用
}
rukou() {
var a = "hello"
show(&a) // 把 a 借出去,不复制
print(a) // 用完还回来,a 照常可用
}两边都要写 &:声明写了、调用没写会报错,反过来也一样。
借用能读也能改
借用参数用起来和普通值一样,不需要额外的语法。修改它,改的就是调用方的变量:
cpro
jiegouti Point {
x leixing z64,
y leixing z64,
}
hs move_right(p &Point) kongbai {
p.x = p.x + 100
}
rukou() {
bl pt leixing Point = { x: 1, y: 2 }
move_right(&pt)
dayin(pt.x) // 101,pt 被改了
}cpro
结构体 Point {
x 类型 i64,
y 类型 i64,
}
函数 move_right(p &Point) void {
p.x = p.x + 100
}
入口() {
变量 pt 类型 Point = { x: 1, y: 2 }
move_right(&pt)
打印(pt.x) // 101,pt 被改了
}cpro
struct Point {
x type i64,
y type i64,
}
fn move_right(p &Point) void {
p.x = p.x + 100
}
rukou() {
var pt type Point = { x: 1, y: 2 }
move_right(&pt)
print(pt.x) // 101,pt 被改了
}数组同理,在借用参数上 zhuijia 会加到调用方的数组里:
cpro
hs grow(xs &[z64]) kongbai {
xs.zhuijia(7)
}
rukou() {
bl a leixing [z64] = [1]
grow(&a)
dayin(a.changdu()) // 2
}cpro
函数 grow(xs &[i64]) void {
xs.push(7)
}
入口() {
变量 a 类型 [i64] = [1]
grow(&a)
打印(a.length()) // 2
}cpro
fn grow(xs &[i64]) void {
xs.push(7)
}
rukou() {
var a type [i64] = [1]
grow(&a)
print(a.length()) // 2
}能借什么
只能借有名字的东西:变量、结构体字段、数组元素。
cpro
f(&a) // ✓ 变量
f(&a.name) // ✓ 字段
f(&arr[i]) // ✓ 数组元素
f(&(a + b)) // ✗ 不能借临时值cpro
f(&a) // ✓ 变量
f(&a.name) // ✓ 字段
f(&arr[i]) // ✓ 数组元素
f(&(a + b)) // ✗ 不能借临时值cpro
f(&a) // ✓ 变量
f(&a.name) // ✓ 字段
f(&arr[i]) // ✓ 数组元素
f(&(a + b)) // ✗ 不能借临时值规则
& 只能用在函数参数上,不能作变量类型、返回类型、结构体字段或数组元素:
cpro
hs f(x &z32) kongbai { } // ✓
bl x leixing &z32 // ✗
hs g() &z32 { } // ✗cpro
函数 f(x &i32) void { } // ✓
变量 x 类型 &i32 // ✗
函数 g() &i32 { } // ✗cpro
fn f(x &i32) void { } // ✓
var x type &i32 // ✗
fn g() &i32 { } // ✗这条限制保证借用不会比原变量活得更久,所以不会出现"原变量已经释放、借用还在用"的情况。
不能借只读变量,因为借用可以修改它:
cpro
bl({ readonly: zheng }) a = 10
f(&a) // ✗cpro
变量({ readonly: 真 }) a = 10
f(&a) // ✗cpro
var({ readonly: true }) a = 10
f(&a) // ✗不能借"借用"。借用参数要交给另一个函数时,只能让那个函数按值接收(会复制一份):
cpro
hs f(x &z32) kongbai {
g(&x) // ✗ x 本身已经是借用
h(x) // ✓ h 按值接收,复制一份
}cpro
函数 f(x &i32) void {
g(&x) // ✗ x 本身已经是借用
h(x) // ✓ h 按值接收,复制一份
}cpro
fn f(x &i32) void {
g(&x) // ✗ x 本身已经是借用
h(x) // ✓ h 按值接收,复制一份
}编译器不会替你决定
要不复制就自己写 &。即使函数里没有修改参数,编译器也不会自动改成借用——参数是复制还是借用,只看你写了什么,读代码就能确定。
传临时值本来就不复制,例如 show(greet()) 直接把 greet() 的结果交给 show,只有传变量时才会复制。
