基本类型
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. }
