不安全代码
内存安全/不安全函数
默认情况下:
所有的V函数调用都是内存安全的,可信的。
所有的C函数调用都是内存不安全的,不可信的。
如果要把某个V函数定义为内存不安全的,要给函数加上unsafe注解。
如果要把C函数定义为内存安全的,可信的,要给函数加上trusted注解。
不管是V函数还是C函数,所有内存不安全的函数都要在unsafe代码块中调用,否则编译器会报错。
[unsafe] //注解V函数为不安全的函数,因为里面有手动的内存控制或指针运算等内存不安全操作
pub fn (a array) free() {
//if a.is_slice {
//return
//}
C.free(a.data)
}
// v/builtin/cfns.c.v
[trusted] //注解C函数为安全的,信任的函数
fn C.calloc(int, int) &u8
fn C.malloc(int) &u8
fn C.realloc(a &u8, b int) &u8
fn C.free(ptr voidptr)
[trusted]
fn C.exit(code int)
unsafe函数必须在unsafe代码块中调用。
fn my_fn() {
unsafe {
//在这里才能调用不安全函数
}
//在unsafe代码块之外调用,编译器会警告
}
在不安全代码块中进行指针运算和多级指针,编译器啥都不管,自己掌控,就跟C一样。
module main
fn main() {
unsafe {
v := 4
mut p := &v
// unsafe代码块中可以进行指针运算,编译器不负责检查
p++
p += 2
p = p - 1
println(p)
p = p + 1
println(p)
r := p++
println(r)
//多级指针
n := 100
pn := &n
ppn := &pn
mut pppn := &ppn
println(pppn)
}
}
不安全表达式
module main
fn main() {
mut q := &u8(10)
println(q)
unsafe {
q -= 2
q = q + 1
}
println(q)
//不安全表达式,返回指针运算后的结果
s := unsafe { q - 1 }
println(s)
unsafe {
q++
q++
q--
}
println(q)
}
空值/空指针
nil表示空值或者空指针,等价于voidptr(0),nil的使用一定要在不安全代码中定义。
下面两种定义空指针的结果一致,推荐使用第一种,第二种会逐步被替换掉
p1:= unsafe { nil }
p1:= voidptr(0)
正常情况下,由V创建的变量,因为声明和初始化一定是同时进行的,
所以变量一定会有初始值,V指针一定不会有空值nil。
但是通过调用C代码返回的指针,有可能是空指针,所以在使用前可以用isnil(ptr)内置函数来判断一下,判断由C代码生成的指针,是否是空指针。
fn main() {
a := 1
println(isnil(&a)) // 返回false,变量只能通过:=来初始化,一定会有初始值
f := C.popen('ls', 'r') //调用C代码返回的指针,有可能是空指针
if isnil(&f) {
// ...
println('f is nil')
} else {
println('f is not nil')
}
}
也可以在代码中使用==或!=来判断指针是否是空值:
module main
struct Point {
pub:
x int
y int
}
fn main() {
p1 := unsafe { nil } //初始化为空值nil,一定要在unsafe代码块中,或unsafe表达式
p2 := &Point{1, 2}
if p1 == unsafe { nil } { //判断是否为空值nil
println('p1 is nil')
}
if p2 != unsafe { nil } { //判断是否不为空值nil
println('p2 is not nil')
}
}
最后更新于
这有帮助吗?