V语言是一门静态类型,编译型语言,以下是内置的基本类型:
布尔类型
bool,布尔类型,值只能是true或false。
复制 module main
fn main () {
x := true
y := false
println (x) // true
println (y) // false
println ( sizeof ( bool )) // 1个字节
}
布尔类型从定义的C代码看是u8的类型别名。
true是常量1,false是常量0。
使用sizeof(bool)可以看到bool类型的默认长度是1个字节。
复制 typedef uint8_t u8;
...
#ifndef bool
#ifdef CUSTOM_DEFINE_4bytebool
typedef int bool ;
#else
typedef u8 bool ;
#endif
#define true 1
#define false 0
#endif
数值类型
数值类型分为整数和小数。
整数
类型 长度 取值范围 说明 -2,147,483,648 到 2,147,483,647
-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
等价于C的ptrdiff_t类型,长度取决于运行的计算机架构,64 位是i64,32 位是i32
0 到 18,446,744,073,709,551,615
等价于C的size_t类型,长度取决于运行的计算机架构,64位是u64,32 位是u32
小数
数字可以通过前缀标识对应的进制,也可以增加下划线,进行视觉上的分隔,不影响本来的值。
复制 mut c := 0xa_0 //十六进制数字a0
println (c) //输出160
c = 0b10_01 //二进制数字1001
println (c) //输出9
c = 1_000_000 //十进制数字1000000
println (c) //输出1000000
小数只能使用类似1.0的风格,不允许使用1.的风格,小数点后面的0不允许省略。
复制 f1 := 1.0
println (f 1 )
//f2 := 1. //不允许使用1.的风格
//println(f2)
如果不希望由编译器自动类型推断,可以通过T(value)的格式,显式声明变量类型,T是类型,value是变量值。
复制 x := 3 //默认自动推断为int
y := 1.2 //默认自动推断为f64
println ( typeof (x).name) //输出int
println ( typeof (y).name) //输出f64
xx := i64 ( 3 ) // xx是i64类型,而不是默认推断的int
yy := f32 ( 3.0 ) // yy是f32类型,而不是默认推断的f64
println ( typeof (xx).name) //输出i64
println ( typeof (yy).name) //输出f32
字节类型
字节类型u8,就是单字节byte。
早期的V版本,确实有byte这个类型,是u8的类型别名,后来被声明取消了,因为作者希望坚持one way的原则,目前byte还可以照常使用,但是在不远的将来,会被取消。
复制 module main
fn main () {
b := u8 ( 98 )
println (b. str ()) // 98
println (b. ascii_str ()) // b
}
字符串类型
string字符串类型,默认不可变,UTF-8编码。
单引号和双引号都可以,习惯上都还是使用单引号,V编译器中的代码都是统一使用的单引号。
按作者的说法是可以少按一个shift键,更自然简单一些,不过打破了之前C的使用习惯,让人一开始有些不习惯。
当使用双引号的时候,编译器会自动把双引号自动替换为单引号。
字符串长度:
复制 s := 'abc'
println (s.len) //输出3
字符串连接:
复制 s1 := 'abc'
s2 := 'def'
s3 := s 1 + s 2
字符串追加:
复制 mut s := 'hello ' //必须是可变才可以追加
s += 'world'
println (s) //输出hello world
字符串插值:
复制 name := 'Bob'
println ( 'hello ${name}' )
println ( "hello ${name}" )
判断字符串是否包含子字符串:
复制 fn main () {
s := 'abcd'
println (s. contains ( 'c' )) //true
println (s. contains ( 'bc' )) //true
println (s. contains ( 'bb' )) //false
}
遍历字符串:
复制 str := 'abcdef'
//遍历value
for s in str {
println (s. str ())
}
//遍历index和value
for i, s in str {
println ( 'index:$i,value:$s.str()' )
}
字符串切片/区间
采用的是左闭右开。
复制 module main
fn main () {
s := 'hello_world'
println (s[.. 3 ]) //输出hel
println (s[ 2 ..]) //输出llo_world
println (s[ 2 .. 5 ]) //输出llo
//区间支持负数的索引值,不过要在字符串名后特别加一个#,否则会报错
println (s#[ - 5 .. - 2 ]) //输出wor
//字符串区间越界的or代码块处理
println (s[..s.len])
println (s[..s.len + 1 ] or { 'oh,no' })
println ( range_check (s) or { 'out of range' })
}
fn range_check (s string ) ! string {
return s[.. 20 ] !
}
字符串从定义的V源代码代码看,也是通过结构体struct来实现的。
vlib/builtin/string.v:
复制 pub struct string {
pub : //pub表示这两个字符串的属性都是:公共且只读的
str &u8 //一个u8类型的指针,指向字符串的首字节地址
len int //字符串的长度
}
字符串单引号和双引号混合使用
主要的逻辑是:
如果有字符串嵌套使用,内外必须不能同时是单引号或者同时是双引号,不然无法正确配对。
复制 module main
fn main () {
s0 := 'name=tom' //ok
s1 := "name=tom" //ok
s2 := "name='tom'" //ok
s3 := 'name="tom"' //ok
//s4:='name='tom'' //error
//s5:="name="tom"" //error
s6 := 'name=\'tom\'' //ok
s7 := "name=\'tom\'" //ok
s8 := "name=\"tom\"" //ok
s9 := 'name=\"tom\"' //ok
println (s 0 )
println (s 1 )
println (s 2 )
println (s 3 )
// println(s4)
// println(s5)
println (s 6 )
println (s 7 )
println (s 8 )
println (s 9 )
}
原始字符串
在单引号或双引号前加上小写r,就表示是raw字符串,在原始字符串中不支持插值和转译。
复制 module main
fn main () {
name := 'tom'
str := 'name is: ${name} \n' //字符串插值和转译
raw_str := r 'name is: {name} \n' //原始字符串,在单引号或双引号之前加上r前缀
raw_str2 := r "name is: {name} \n" //原始字符串,在单引号或双引号之前加上r前缀
println (str)
println (raw_str)
println (raw_str 2 )
}
输出:
复制 name is: tom
name is: {name} \n
name is: {name} \n
rune类型
rune是u32的类型别名,用4个字节来表示一个unicode字符/码点,跟string不是同一个类型。
rune使用反引号来表示。
复制 module main
fn main () {
s1 := 'a' //单引号,string类型
s2 := "a" //双引号,string类型
s3 := `a` //反引号,rune类型
println ( typeof (s 1 ).name)
println ( typeof (s 2 ).name)
println ( typeof (s 3 ).name)
println ( int (s 3 )) // 97
//
// c2 := `aa` //编译不通过,报错,只能是单字符
c3 := `中`
println ( typeof (c 3 ).name) // rune类型
println ( sizeof (c 3 )) // 4个字节,unicode4.0
println ( int (c 3 )) // 20013
println (c 3 )
}
常用字符串内置函数,可以参考后面的标准库 章节,也可以直接参考vlib/builtin/string.v源代码。
指针类型
voidptr,通用类型指针,用来存储变量的内存地址,可以保存任何类型的地址。
指针本身占用的内存大小就是C语言里面的size_t类型,通常在32位系统上的长度是32位,64位系统上是64位。
复制 module main
fn main () {
//测试机器是64位操作系统
println ( sizeof ( voidptr )) //输出8个字节
}
变量前加&表示取内存地址,返回指针类型。
指针类型前加*表示取内存地址对应变量的值。
复制 fn main () {
a := 'abc'
println ( & a) // 取变量地址,返回地址
b := & a
println ( * b) // 取指针对应的值,返回abc
}
nil,表示空值或者空指针,等价于voidptr(0)。
正常情况下,由V创建的变量,因为声明和初始化一定是同时进行的,所以变量一定会有初始值。V指针一定不会有空值nil,即voidptr(0)值。
但是通过调用C代码返回的指针,有可能是空指针,所以在使用前可以用isnil(ptr)内置函数判断一下,判断由C代码生成的指针是否是空指针。
复制 fn main () {
a := 1
println ( isnil ( & a)) // 返回false,变量只能通过:=来初始化,一定会有初始值
// 但是通过调用C代码返回的指针,有可能是空指针,所以在使用前可以用isnil函数判断一下
f := C. popen ( 'ls' .str , 'r' .str)
if isnil ( & f) {
println ( 'f is nil' )
} else {
println ( 'f is not nil' )
}
}
在V语言中,指针只是表示引用,不能进行指针运算。
只有在unsafe代码块中,V编译器不进行任何检查,允许指针像C那样可以进行指针运算,指针偏移,多级指针。
详细内容可以参考:不安全代码 。
除了voidptr通用类型指针,V还有2种指针类型,一般情况比较少用:字节类型指针byteptr,字符类型指针charptr。
复制 module main
fn main () {
u := u8 ( 10 )
b := & u //&u类型
c := `a`
b_ptr := byteptr (b) // byteptr类型,等价于&u
c_ptr := charptr ( & c) // charptr类型,一般用于跟C集成使用,等价于*char
println ( typeof (b).name)
println ( typeof (b_ptr).name)
println ( typeof (c_ptr).name)
}
类型推断及类型转换
复制 module main
fn main () {
i := 8 // 默认类型推断为int
b := u8 ( 8 ) // 明确指定类型为u8
ii := int (b) // 强制转换为int
f := 3.2 // 默认推断类型为f64
ff := f32 ( 3.2 ) // 明确指定类型为f32
f3 := f64 (f) // 强制转换为f64
s := 'abc' // 默认推断为string
c := `c` // 默认推断为rune
// 布尔类型可以转换为u8/int或其他整数类型
yes := true
no := false
yes_u8 := u8 (yes) // 输出1
no_u8 := u8 (no) // 输出0
yes_int := int (yes) // 输出1
no_int := int (no) // 输出0
// 将字节数组转成字符串
mut u8_arr := [] u8 {} // 字节数组
u 8_ arr << `a`
u 8_ arr << `b`
println (u 8_ arr) // 输出[97,98]
str := u 8_ arr. bytestr () // 将字节数组转成字符串
println (str) // 输出ab
}
获取变量和类型占用内存大小
使用内置函数sizeof来获取变量和类型占用内存大小:
复制 module main
fn main () {
x := 1
u := u8 ( 1 )
b := true
//普通函数
println ( sizeof (x)) // 4
println ( sizeof (u)) // 1
println ( sizeof (b)) // 1
//泛型函数
println (get_size[ int ]()) // 4
println (get_size[ u8 ]()) // 1
println (get_size[ bool ]()) // 1
}
fn get_size [T]() u32 {
return sizeof (T) //泛型函数
}
获取变量和类型的类型信息
使用内置函数typeof来返回变量的类型和类型的idx和name
复制 module main
struct Point {
x int
}
type MyFn = fn ( int ) int
type MyFn2 = fn ()
fn myfn (i int ) int {
return i
}
fn myfn2 () {
}
fn main () {
a := 123
s := 'abc'
aint := [] int {}
astring := [] string {}
astruct_static := [ 2 ]Point{}
astruct_dynamic := [Point{} , Point{}]
//使用typeof().idx获取变量类型的id
//使用typeof().name获取变量的类型
println ( typeof (a).idx) // 7
println ( typeof (a).name) // int
println ( typeof (s).name) // string
println ( typeof (aint).name) // array_int
println ( typeof (astring).name) // array_string
println ( typeof (astruct_static).name) // [2]Point
println ( typeof (astruct_dynamic).name) // array_Point
//函数类型
println ( typeof (myfn).name) // fn (int) int
println ( typeof (myfn 2 ).name) // fn ()
}
获取类型的idx和name:
复制 module main
struct MyStruct {}
struct MyGenericStruct2 [T , U] {}
type Abc = int | string
type AnAlias = int
enum EFoo {
a
b
c
}
fn main () {
println ( typeof [ int ]().idx) //返回类型的id,int类型在编译器内部的id是7
println ( typeof [ int ]().name) //返回类型的名字: int
println ( typeof [ ? string ]().name) //返回类型的名字: ?string
println ( typeof [ ! string ]().name) //返回类型的名字: !string
println ( typeof [[] string ]().name) //返回数组类型的名字: []string
println ( typeof [ map [ string ] int ]().name) //返回字典类型的名字: map[string]int
println ( typeof [ fn (s string , x u32 ) ( int , f32 )]().name) //返回函数类型的名字: fn (string, u32) (int, f32)
println ( typeof [MyGenericStruct]().name) //返回结构体类型的名字: MyStruct
println ( typeof [MyGenericStruct 2 [ string , int ]]().name) //返回泛型结构体类型的名字: MyGenericStruct2[string, int]
println ( typeof [Abc]().name) //返回联合体类型的名字: Abc
println ( typeof [EFoo]().name) //返回枚举类型的名字: EFoo
println ( typeof [AnAlias]().name) //返回类型别名名字: AnAlias
}
判断变量和类型是否为引用类型
普通函数,判断变量是否为引用类型
isreftype(var)
泛型函数,判断类型是否为引用类型
isreftype[T]()
复制 module main
struct Point {
x int
y int
}
fn main () {
i := 8
println ( isreftype (i)) //基本类型除了string,都不是引用类型
s := 'abc'
println (( isreftype (s))) // string是引用类型,因为字符串是使用结构体来实现的
mut m := map [ string ] string {} // map是引用类型,因为字典是使用结构体来实现的
m[ 'name' ] = 'tom'
println ( isreftype (m))
a := [ 1 , 2 , 3 ]
println ( isreftype (a)) // array是引用类型,因为数组是使用结构体来实现的
p := Point{
x: 1
y: 2
}
pp := & Point{
x: 1
y: 2
}
println ( isreftype (p)) // p不是引用类型
println ( isreftype (pp)) // pp是引用类型
}
判断类型是否为引用类型:
复制 module main
fn main () {
println ( isreftype ( int ))
println (isreftype[ string ]())
println (get_is_ref_type[array]())
}
fn get_is_ref_type [T]() bool {
return isreftype[T]()
}