unsafe 与指针
unsafe
危险操作必须放在 bu_anquan(不安全 / unsafe)块里。编译器不保证这些操作的安全,由写代码的人负责:
cpro
bu_anquan {
// 危险操作
}cpro
不安全 {
// 危险操作
}cpro
unsafe {
// 危险操作
}需要 bu_anquan 的操作:
- 调用 C 函数,见 调用 C 库
- 对局部变量、参数取地址
- 全部裸地址操作
- C 字符串与缓冲区转换(
c_chuan、c_zhuan_chuan、zhi_zhuan_shuzu等)
推荐用普通函数把 bu_anquan 包起来,业务代码保持干净。审计时搜索 bu_anquan 就能找到所有危险的地方:
cpro
wailian hs pow(x f64, y f64) f64
hs mypow(x f64, y f64) f64 {
bu_anquan { fanhui pow(x, y) }
}cpro
外联 函数 pow(x f64, y f64) f64
函数 mypow(x f64, y f64) f64 {
不安全 { 返回 pow(x, y) }
}cpro
extern fn pow(x f64, y f64) f64
fn mypow(x f64, y f64) f64 {
unsafe { return pow(x, y) }
}带类型地址
cpro 的指针叫带类型地址,类型写作 daizhi(T)(带址(T) / addr(T))。
| 拼音 | 中文 | 英文 | 作用 | 需要 unsafe |
|---|---|---|---|---|
quzhi(x) | 取址(x) | addr_of(x) | 取地址 | 取局部变量、参数的地址时需要 |
jiezhi(p) | 解址(p) | deref(p) | 读 | 不需要 |
xiezhi(p, v) | 写址(p, v) | store(p, v) | 写 | 不需要 |
cpro
bl x leixing z64 = 16 // 模块级变量
hs demo() kongbai {
bl p leixing daizhi(z64) = quzhi(x) // 模块级变量取地址,不需要 unsafe
dayin(jiezhi(p)) // 16
xiezhi(p, 42) // x 变成 42
}cpro
变量 x 类型 i64 = 16 // 模块级变量
函数 demo() void {
变量 p 类型 带址(i64) = 取址(x) // 模块级变量取地址,不需要 unsafe
打印(解址(p)) // 16
写址(p, 42) // x 变成 42
}cpro
var x type i64 = 16 // 模块级变量
fn demo() void {
var p type addr(i64) = addr_of(x) // 模块级变量取地址,不需要 unsafe
print(deref(p)) // 16
store(p, 42) // x 变成 42
}取局部变量或参数的地址必须在 bu_anquan 里,因为函数返回后这个地址就失效了:
cpro
hs foo() kongbai {
bl local leixing z64 = 1
bu_anquan {
bl p leixing daizhi(z64) = quzhi(local)
xiezhi(p, 2)
}
dayin(local) // 2
}cpro
函数 foo() void {
变量 local 类型 i64 = 1
不安全 {
变量 p 类型 带址(i64) = 取址(local)
写址(p, 2)
}
打印(local) // 2
}cpro
fn foo() void {
var local type i64 = 1
unsafe {
var p type addr(i64) = addr_of(local)
store(p, 2)
}
print(local) // 2
}规则:
- 只能对有名字的东西取地址:变量、结构体字段、数组元素;不能对临时值或函数返回值取地址
- 字段、元素的地址跟随所属变量:属于局部变量就需要
bu_anquan - 地址类型必须和变量类型一致,
z64变量的地址不能当daizhi(f32)用 - 地址不能为空,也不能做加减运算
- 没有多级地址,
daizhi(daizhi(T))会报错 - 两个地址可以用
==比较是否指向同一处 - 地址可以作参数和返回值,但返回局部变量的地址会悬垂,要自己保证不这样用
裸地址
裸地址就是一个 z64(i64)整数,用于和 C 互操作。所有裸地址操作都必须在 bu_anquan 里:
| 拼音 | 中文 | 英文 | 作用 |
|---|---|---|---|
luozhi(x) | 裸址(x) | raw_addr(x) | 取裸地址 |
luojie(p, T) | 裸解(p, T) | raw_deref(p, T) | 按类型读 |
luoxie(p, v, T) | 裸写(p, v, T) | raw_store(p, v, T) | 按类型写 |
luozhuanzhi(p, T) | 裸转址(p, T) | raw_to_addr(p, T) | 裸地址转带类型地址 |
zhizhuanluo(a) | 址转裸(a) | addr_to_raw(a) | 带类型地址转裸地址 |
cpro
bl x leixing z64 = 16
hs demo() kongbai {
bu_anquan {
bl p leixing z64 = luozhi(x)
dayin(luojie(p, z64)) // 16
luoxie(p, 42, z64)
bl a leixing daizhi(z64) = luozhuanzhi(p, z64)
bl r leixing z64 = zhizhuanluo(a)
}
}cpro
变量 x 类型 i64 = 16
函数 demo() void {
不安全 {
变量 p 类型 i64 = 裸址(x)
打印(裸解(p, i64)) // 16
裸写(p, 42, i64)
变量 a 类型 带址(i64) = 裸转址(p, i64)
变量 r 类型 i64 = 址转裸(a)
}
}cpro
var x type i64 = 16
fn demo() void {
unsafe {
var p type i64 = raw_addr(x)
print(raw_deref(p, i64)) // 16
raw_store(p, 42, i64)
var a type addr(i64) = raw_to_addr(p, i64)
var r type i64 = addr_to_raw(a)
}
}指针在 cpro 里主要用于调用 C 库,日常代码用 借用 就够了。
