基本类型

V语言是一门静态类型,编译型语言,以下是内置的基本类型:

布尔类型

bool,布尔类型,值只能是true或false.

1. module main

3. fn main() {
4. x := true
5. y := false
6. println(x) // true
7. println(y) // false
8. println(sizeof(bool)) // 1
9. }

布尔类型从定义的C代码看是byte(uint8_t)的类型别名.

true是常量1,false是常量0.

使用sizeof(bool)可以看到bool类型的长度是1个字节.

1. typedef uint8_t byte;
2. ...
3. #ifndef bool
4. typedef byte bool;
5. #define true 1
6. #define false 0
7. #endif

数值类型

数值类型分为整数和小数.

整数

类型 长度 取值范围 说明
i8 1字节 -128 到 127 有符号8位整数
i16 2字节 -32,768 到 32,767 有符号16位整数
int 4字节 -2,147,483,648 到 2,147,483,647 int总是32位整数,即i32
i64 8字节 -9223372036854775808到9223372036854775807 有符号64位整数
byte 1字节 0 到 255 无符号8位整数,即u8
u16 2字节 0 到 65,535 无符号16位整数
u32 4字节 0 到 4,294,967,295 无符号32位整数
u64 8字节 0 到 18446744073709551615 无符号64位整数

小数

类型 长度 取值范围 说明
f32 4字节 32位小数
f64 8字节 64位小数

数字可以通过前缀标识对应的进制,也可以增加下划线,进行视觉上的分隔,不影响本来的值.

1. mut c := 0xa_0 //十六进制数字a0
2. println(c) //输出160
3. c = 0b10_01 //二进制数字1001
4. println(c) //输出9
5. c = 1_000_000 //十进制数字1000000
6. println(c) //输出1000000

如果不希望由编译器自动类型推断,可以通过T(value)的格式,显式声明变量类型,T是类型,value是变量值.

1. x:=i64(3) //x是i64类型,而不是默认推断的int
2. y:=f32(3.0) //y是f32类型,而不是默认推断的f64

字节类型

1. module main

3. fn main() {
4. b := byte(98)
5. println(b.str()) // 98
6. println(b.ascii_str()) // b
7. }

字符串类型

string字符串类型,字符串是一个只读字节数组,默认不可变,UTF-8编码.

单引号和双引号都可以,习惯上都还是使用单引号,V编译器中的代码都是统一使用的单引号.

按作者的说法是可以少按一个shift键,更自然简单一些,不过打破了之前C的使用习惯,让人一开始有些不习惯.

当使用双引号的时候,编译器会自动把双引号自动替换为单引号.

1. s1:='abc'
2. s2:="abc"

字符串长度:

1. s:='abc'
2. println(s.len) //输出3

字符串连接:

1. s1:='abc'
2. s2:='def'
3. s3:=s1+s2

字符串追加:

1. mut s:='hello ' //必须是可变才可以追加
2. s+='world'
3. println(s) //输出hello world

字符串插值:

1. name:='Bob'
2. println('hello $name') //方式1
3. println('hello ${name}') //方式2,效果一样,更常用于复杂的表达式,把表达式放在{}里面

判断字符串是否包含子字符串:

1. fn main() {
2. s:='abcd'
3. println(s.contains('c')) //true
4. println(s.contains('bc')) //true
5. println(s.contains('bb')) //false
6. }

遍历字符串:

1. str := 'abcdef'
2. //遍历value
3. for s in str {
4. println(s.str())
5. }
6. //遍历index和value
7. for i, s in str {
8. println('index:$i,value:$s.str()')
9. }

字符串切片/区间:

采用的是左闭右开.

1. s:='hello_world'
2. println(s[..3]) //输出hel
3. println(s[2..]) //输出llo_world
4. println(s[2..5]) //输出llo

字符串从定义的v代码看,也是一个struct.

1. pub struct string {
2. pub:                      //pub表示这两个字符串的属性都是:公共且只读的
3. str &byte //一个byte类型的指针,指向字符串的首字节地址
4. len int  //字符串的长度
5. }

字符串单引号和双引号混合使用

主要的逻辑是:

  • 以单引号为主要使用的符号,双引号也可以使用.
  • 如果有字符串嵌套使用,内外必须不能同时是单引号或者同时是双引号,不然无法正确配对.
  • 如果有加上反斜杠进行转义,则都可以.
1. module main

3. fn main() {
4. s0:='name=tom'         //ok
5. s1:="name=tom"         //ok
6. s2:="name='tom'"     //ok
7. s3:='name="tom"'     //ok
8. //s4:='name='tom''    //error
9. //s5:="name="tom""    //error
10. s6:='name=\'tom\''  //ok
11. s7:="name=\'tom\'"    //ok
12. s8:="name=\"tom\""    //ok
13. s9:='name=\"tom\"'    //ok
14. println(s0)
15. println(s1)
16. println(s2)
17. println(s3)
18. // println(s4)
19. // println(s5)
20. println(s6)
21. println(s7)
22. println(s8)
23. println(s9)
24. }

rune类型

rune是u32的类型别名,用4个字节来表示一个unicode字符/码点,跟string不是同一个类型.

rune使用反引号来表示.

1. module main

3. fn main() {
4. s1 := 'a' //单引号,string类型
5. s2 := 'a' //双引号,string类型
6. s3 := `a` //反引号,rune类型
7. println(typeof(s1).name)
8. println(typeof(s2).name)
9. println(typeof(s3).name)
10. println(int(s3)) // 97
11. //
12. // c2 := `aa` //编译不通过,报错,只能是单字符
13. c3 := `中`
14. println(typeof(c3).name) // rune类型
15. println(sizeof(c3)) // 4个字节,unicode4.0
16. println(int(c3)) // 20013
17. println(c3)
18. }

常用字符串内置函数,可以参考后面的标准库章节,也可以直接参考vlib/builtin/string.v源代码.

指针类型

voidptr指针类型,用来存储变量的内存地址.

指针本身占用的内存大小就是C语言里面的size_t类型.

通常在32位系统上的长度是32位,64位系统上是64位.

1. module main

3. fn main() {
4. //测试机器是64位操作系统
5. println(sizeof(voidptr)) //输出8个字节
6. }

变量前加&表示取内存地址,返回指针类型.

指针类型前加*表示取内存地址对应变量的值.

1. fn main() {
2. a := 'abc'
3. println(&a) // 取变量地址,返回地址
4. b := &a
5. println(*b) // 取指针对应的值,返回abc
6. }

正常情况下,由V创建的变量,因为声明和初始化一定是同时进行的,

所以变量一定会有初始值,V指针一定不会有空值,即0值.

内置函数中的isnil(ptr),是用来判断由C代码生成的指针,是否是空指针.

1. fn main() {
2. a := 1
3. println(isnil(&a)) // 返回false,变量只能通过:=来初始化,一定会有初始值
4. // 但是通过调用C代码返回的指针,有可能是空指针,所以在使用前可以用isnil函数来判断一下
5. f := C.popen('ls', 'r')
6. if isnil(&f) {
7. // ...
8. println('f is nil')
9. } else {
10. println('f is not nil')
11. }
12. }

在V语言中,指针只是表示引用,不能进行指针运算.

只有在unsafe代码块中,V编译器不进行任何检查,允许指针像C那样可以进行指针运算,指针偏移,多级指针.

详细内容可以参考:不安全代码.

类型占用内存大小sizeof()

使用内置函数sizeof(T)来返回类型占用内存大小.

1. println(sizeof(int)) //4
2. println(sizeof(byte)) //1
3. println(sizeof(bool)) //1

变量类型typeof().name

使用内置函数typeof(var).name来返回变量的类型.

1. module main

3. struct Point {
4. x int
5. }

7. type MyFn = fn (int) int

9. type MyFn2 = fn ()

11. fn myfn(i int) int {
12. return i
13. }

15. fn myfn2() {
16. }

18. fn main() {
19. a := 123
20. s := 'abc'
21. aint := []int{}
22. astring := []string{}
23. astruct_static := [2]Point{}
24. astruct_dynamic := [Point{}, Point{}]

26. //使用typeof().name获取变量的类型
27. println(typeof(a).name) // int
28. println(typeof(s).name) // string
29. println(typeof(aint).name) // array_int
30. println(typeof(astring).name) // array_string
31. println(typeof(astruct_static).name) // [2]Point
32. println(typeof(astruct_dynamic).name) // array_Point

34. //函数类型
35. println(typeof(myfn).name) // fn (int) int
36. println(typeof(myfn2).name) // fn ()
37. }

类型推断及类型转换

1. module main

3. fn main() {
4. i := 8 // 默认类型推断为int
5. b := byte(8) // 明确指定类型为byte
6. ii := int(b) // 强制转换为int
7. f := 3.2 // 默认推断类型为f64
8. ff := f32(3.2) // 明确指定类型为f32
9. f3 := f64(f) // 强制转换为f64
10. s := 'abc' // 默认推断为string
11. c := `c` // 默认推断为byte,也就是单字符类型

13. // 布尔类型可以转换为byte/int或其他整数类型
14. yes := true
15. no := false
16. yes_byte := byte(yes) // 输出1
17. no_byte := byte(no) // 输出0
18. yes_int := int(yes)  // 输出1
19. no_int := int(no) // 输出0
20. // 将字节数组转成字符串
21. mut byte_arr := []byte{} // 字节数组
22. byte_arr << `a`
23. byte_arr << `b`
24. println(byte_arr) // 输出[a,b]
25. str := byte_arr.str() // 将字节数组转成字符串
26. println(str) // 输出[a,b]
27. }