文章目录
介绍
V是一种静态类型的编译编程语言,用于构建可维护的软件。
它与Go类似,也受到Oberon,Rust,Swift的影响。
V是一种非常简单的语言。通过这个文档将花费你大约半个小时,到最后你将学习几乎整个语言。
尽管很简单,但它为开发人员提供了很多动力。你可以用其他语言做任何事情,你可以用V做。(官方文档入口:https://vlang.io/docs#option)
Hello World
//hello.v
fn main() {
println('hello world')
}
- 和 c语言一样,main 为程序入口函数
- fn 声明函数,函数的返回值类型在 名称() 的后面,此处不返回任何内容所以忽略;
- println 是为数不多的内置函数之一,作为标准输出;
- fn main()函数也可以被忽略,方便在编写小程序应用中,或者脚本,或者学习语言时带来方便;这样代表’Hello World’可以这么写
//hello.v
println('hello world')
注释
// 单行注释
/*
这是多行注释
/* 它可以嵌套 */
*/
函数 Functions
fn main() {
println(add(77, 33))
println(sub(100, 50))
}
fn add(x int, y int) int {
return x + y
}
fn sub(x, y int) int {
return x - y
}
- 再次声明,返回值的类型在 名称的后面,
- 和Go 和 C 一样,函数不能重载,这也简化了代码,提高了可维护性和可读性
- 函数的使用可以在声明之前:例如上面, add 和 sub 在main 之后声明,也可以被 main 调用。对于 V 语言,所有声明都是如此,并且不需要头文件或考虑文件和声明的顺序。
变量
name := 'Bob'
age := 20
large_number := i64(9999999999)
println(name)
println(age)
println(large_number)
- 变量的声明和初始化 使用 := ;这是V语言声明变量的唯一方式,这也意味着变量的初始化始终具有初始值。
- 变量的类型是右侧的值推断出来的。要强制使用其他类型,请使用强制类型转换;表达式 T(v) 将值 v 转换为 T类型
- 与大多数语言不同,V 只允许在函数中定义变量,不允许使用全局(模块级别)变量,没有全局状态。
mut age := 20
println(age)
age = 21
println(age)
- 【mut】改变变量的值,使用 = 符号。在V语言中,默认情况下变量是不可改变的,为了得到一个允许被修改的变量,你必须在声明时,用 mut来修饰。
- 您可以将上面例子的 mut 删除过后编译试试。
- 请注意 := 和 =之间的区别;:=用于声明和初始化,=用于赋值
- 请看如下3例错误示例
fn main() {
age = 21 //这段代码在V语言中是编译不通过的,因为age未声明变量。
}
fn main() {
age := 21 //此段代码也不会被编译通过,因为未使用的变量会导致编译错误
}
fn main() {
a := 10
if true {
a := 20 //与大多数语言不同,不允许使用已被声明的变量在局部变量。
//声明已在父作用域中使用的变量名,将导致编译错误。
}
}
基本类型
- bool 布尔类型
- string 字符串类型
- i8 i16 i32 i64 i128 (soon)
- u8 u16 u32 u64 u128 (soon)
- byte // 是 u8 的别名
- int // 是 i32 的别名,与C,Go不同,int 始终为 32字节
- rune // 是 i32 的别名,表示Unicode代码点
- f32 f64
- byteptr
- voidptr
字符串 Strings
name := 'Bob'
println('Hello, $name!') // `$` 用与字符串内引入变量
println(name.len) //字符串长度
bobby := name + 'by' // 通过 + 拼接字符串
println(bobby) // ==> "Bobby"
println(bobby.substr(1, 3)) // 字符串截取 ==> "ob"
// println(bobby[1:3]) // 这种写法很可能会替换 substr 方法
- V语言中,字符串是只读字节数组,字符串数据使用UTF-8编码。
- 单引号和双引号都可以用于表示字符串(TODO:暂时不支持双引号)。为了保持一致,vfmt 请将双引号转换为单引号,除非字符串包含单引号。
- 字符串是不可变的,这代表 substring 函数非常有影响:不执行复制,不需要额外的分配。
- V语言中,所有运算符必须在两侧都具有相同类型的值。如果有一个int 类型的变量 age:
println('age = ' + age) //此段代码将无法编译通过,需改为如下
println('age = ' + age.str()) //将 age 转换为 string ,或者使用
println('age = $age') //字符串引入变量的方式
数组 Arrays
nums := [1, 2, 3]
println(nums)
println(nums[1]) // ==> "2"
mut names := ['John']
names << 'Peter' //数组中添加元素
names << 'Sam'
// names << 10 //这里将编译不通过,因为 names 是一个字符串数组
println(names.len) // ==> "3"
println('Alex' in names) // ==> "false" 判断变量是否在数组中
// 我们也可以预先分配一定数量的元素
nr_ids := 50
mut ids := [0 ; nr_ids] // 这里将创建 包含50个0的数组
- 数组的类型由第一个元素决定
- [1,2,3] 是int数组 ([]int)。
- [‘a’,‘b’] 是一个字符串数组 ([]string)
- 所有元素必须相同类型,[1,‘a’] 将编译不通过
- 数组的追加, << 将一个值附加到数组末尾的运算符
- 数组长度,arr.len 字段。注意:len是一个只读字段,用户无法修改。
- 是否包含:val in arr ;如果arr包含val返回 true。
Map
mut m := map[string]int{} // 现在 map的键值只能为 string 类型,
m['one'] = 1 //设置值
println(m['one']) // ==> "1" //获取值
println(m['bad_key']) // ==> "0" //不包含返回 0
println('bad_key' in m) // 使用in 关键字来判断是否存在 key
numbers := {
'one': 1,
'two': 2,
}
IF 用法
if 语句非常简单,与大多数其他语言类似。
与其他类C语言不同,条件周围没有括号,并且始终需要大括号。
- if 可以用作表达式(类似3位运算符):
num := 777
s := if num % 2 == 0 {
'even'
}
else {
'odd'
}
println(s) // ==> "odd"
运算符 in
- in 用于检测 数组中是否包含 某元素
nums := [1, 2, 3]
println(1 in nums) // ==> true
- in 拓展用法,可以编写更清晰的布尔判断逻辑
//标准写法
if parser.token == .plus || parser.token == .minus || parser.token == .div || parser.token == .mult {
...
}
//优化后写法
if parser.token in [.plus, .minus, .div, .mult] {
...
}
// V 语言在这样的表达式中有做优化,上面两个语句会生成相同的机器码,不会创建任何数组
循环语句 FOR
V 语言只有一种循环方法:for
- 数组的循环: 使用 for value in arr 的方式循环取数组中的元素;如果需要索引,可以使用 for index,value in arr的方式代替
numbers := [1, 2, 3, 4, 5]
for num in numbers {
println(num)
}
names := ['Sam', 'Peter']
for i, name in names {
println('$i) $name') // Output: 0) Sam
} // 1) Peter
- 数组循环时,value是只读的,如果要在循环时修改数组,必须使用索引:
mut numbers := [1, 2, 3, 4, 5]
for i, num in numbers {
println(num)
numbers[i] = 0
}
- 类似while的循环方式实现:这种循环方式类似其他语言的 while 循环。一但条件不满足,循环停止,同样,条件周围不需要使用(),代码块总需要{}
mut sum := 0
mut i := 0
for i <= 100 {
sum += i
i++
}
println(sum) // ==> "5050"
- 循环的条件可以省去,这样循环会出现无限循环
mut num := 0
for {
num++
if num >= 10 {
break
}
}
println(num) // ==> "10"
- 传统的 for i 循环:同其他语言风格一样。他比 while形式更安全,因为后者很容易忘记更新计数器并陷入无限循环
- 这里的 i 无需 mut 修饰,因为在这里 默认会被定义为 mut 类型的
for i := 0; i < 10; i++ {
println(i)
}
Match (原 Switch)
//老版本V 使用
os := 'windows'
print('V is running on ')
switch os {
case 'darwin':
println('macOS.')
case 'linux':
println('Linux.')
default:
println(os)
}
//官方文档已经改为
os := 'windows'
print('V is running on ')
match os {
'darwin' => println('macOS.')
'linux' => println('Linux.')
else => println(os)
}
匹配语句是编写if – else语句序列的较短方式。找到匹配的分支时,将分配=>后面的表达式。当没有其他分支匹配时,将分配else =>分支。
注意:与C不同,每个代码块的末尾都不需要 break。
Structs 结构体
- 简单使用
struct Point {
x int
y int
}
p := Point{
x: 10
y: 20
}
println(p.x) // 访问结构体对象的某字段
- 结构体的内存是在堆上分配,需要获取结构体指针,使用 &前缀:
pointer := &Point{10, 10} // 字段少于3的结构体的简化初始化方式
println(pointer.x) // 指针访问结构体属性方式与默认的方式相同
- V 语言没有子类,但它支持嵌入式结构:
// TODO: 这个功能将与7月末实施
struct Button {
Widget
title string
}
button := new_button('Click me')
button.set_pos(x, y)
// 没有潜入式,我们需要这么做
button.widget.set_pos(x,y)
结构体-访问修饰符
结构字段默认是私有的和不可变的(使结构也是不可变的)。
他们可以被 pub(可被访问的)和mut(可被修改的) 修饰。总共有5种可能的选择:
struct Foo {
a int // 私有的 不可变的 (default)
mut:
b int // 私有的 可变的
c int // (你可以一次列出,相同访问修饰符的字段)
pub:
d int // 公开的,不可变的
pub mut:
e int // 公开的,但仅在父模块中可变
pub mut mut:
f int // 公开的,父模块内部和外部都可变
} // (不建议使用,这就是为什么它如此冗长的原因)
- 例子,string 在 builtin 模块中定义的类型:
struct string {
str byteptr
pub:
len int
}
// 很容易看出 string 内容是不可变的
- 错误示例:
fn main() {
str := 'hello'
len := str.len // OK
str.len++ // 编译出错
}
//具有字符串数据的字节指针根本无法在外部builtin访问。 len字段是公开的,但不是可变的。
结构体-方法
V 没有 class概念,但是我们可以在结构体上定义方法。
方法是具有特殊行参的函数。特殊行参 放在 fn关键字 和方法名之间的参数列表中。
- 简单示例(can_register 方法具有 User 类型的 特殊行参 u):
- 不和其他语言一样,使用 self 或 this ,而是使用短名称,当然最好一个字母长
struct User {
age int
}
fn (u User) can_register() bool {
return u.age > 16
}
user := User{age: 10}
println(user.can_register()) // ==> "false"
user2 := User{age: 20}
println(user2.can_register()) // ==> "true"
纯函数
V语言的函数默认是纯函数,也就是函数的输出结果只依赖输入的参数,并且没有其它的影响。
因为V语言没有全局变量,且所有的参数默认都是只读的,即使传入的引用也是默认只读的。
然后V语言并不纯的函数式语言。我们可以通过mut来修饰行参数,使得可以被修改:
struct User {
mut:
is_registered bool
}
fn (u mut User) register() {
u.is_registered = true
}
mut user := User{}
println(user.is_registered) // ==> "false"
user.register()
println(user.is_registered) // ==> "true"
- 如上示例:特殊行参(这里被修饰了第一个参数) 被mut 修饰,为可变的。因此 register() 可以更改用户对象。
- 当然也适用于函数的普通参数,示例如下:
fn multiply_by_2(arr mut []int) {
for i := 0; i < arr.len; i++ {
arr[i] *= 2
}
}
mut nums := [1, 2, 3]
multiply_by_2(mut nums)
println(nums) // ==> "[2, 4, 6]"
注意1:您必须在执行函数之前 声明一个 mut 的nums 变量。这清楚的表明被调用的函数将要修改该值!
- 最好使用返回值而不是修改参数。修改参数只应在应用程序的性能关键部分中完成,以减少分配和复制。
- 使用user.register()或user = register(user) 代替register(mut user)
fn register(u User) User {
return { u | is_registered: true }
}
user = register(user)
常量 Constants
常量声明为const。它们只能在模块级别上(函数body外)定义。
常量名称必须大写。这有助于将它们与变量区分开来
永远不能改变常量值。
- 简单使用
const (
PI = 3.14
World = '世界'
)
println(PI)
println(World)
- 复杂类型的常量定义(这相对大多数语言更灵活)
struct Color {
r int
g int
b int
}
fn (c Color) str() string { return '{$c.r, $c.g, $c.b}' } //重写 str方法
fn rgb(r, g, b int) Color { return Color{r: r, g: g, b: b} }
const (
Numbers = [1, 2, 3]
Red = Color{r: 255, g: 0, b: 0} //结构体常量
Blue = rgb(0, 0, 255)//结构体常量
)
println(Numbers)
println(Red)
println(Blue)
不支持使用全局变量,因此这种方式显得非常有必要
模块 Modules
V 语言是一个模块化的语言。鼓励创建可重用的模块,而且创建模块也非常简单。
- 新模块创建,请创建一个包含你模块名的目录 和 .v 代码文件:
cd ~/code/modules
mkdir mymodule
vim mymodule/mymodule.v
// mymodule.v
module mymodule
// 要用 pub修饰符修饰,外部才能访问该 模块函数
pub fn say_hi() {
println('hello from mymodule!')
}
-
模块的编译:
- 您可以在 你定义好的 模块目录下创建 更多的 .v模块文件
- 构建命令 v -lib ~/code/modules/mymodule ,然后就可以在代码中使用它
-
模块的使用:
module main
import mymodule
fn main() {
mymodule.say_hi()
}
请注意,每次调用外部函数时都必须指定模块。这看起来似乎很冗长,但它使代码更易读,更容易理解,因为它始终清楚的表达出从哪个模块调用哪个函数。特别是在大型代码库中。
模块名称应短,不超过10个字符。模块不允许循环依赖。
您可以在任何地方创建模块 ,所有的模块都将静态编译到单一的可执行程序中。
接口 Interfaces
- 简单使用:
类型通过实现接口同名的方法(注意返回值也相同类型)。和Go语言一样,V语言也是隐式接口,类型不需要显式实现接口,没有“implements”关键字。
struct Dog {}
struct Cat {}
fn (d Dog) speak() string {
return 'woof'
}
fn (c Cat) speak() string {
return 'meow'
}
interface Speaker {
speak() string
}
fn perform(s Speaker) {
println(s.speak())
}
dog := Dog{}
cat := Cat{}
perform(dog) // ==> "woof"
perform(cat) // ==> "meow"
枚举
enum Color {
red green blue
}
mut color := Color.red
// 下面的写法要注意了,V语言知道 color的类型为Color,所以这个地方不需要使用 Color.green
color = .green
println(color) // ==> "1" TODO: print "green"?
可选类型 和 错误处理
V语言针对函数返回值增加了一个可选的属性,这样可以用于处理失败的情况
- 可以用最小工作量来升级一个可选类型返回的函数:只需要在 返回值类型声明的前面添加一个 ?,这样就可以区别错误和真正的返回值。
- 当然如果你不需要返回错误,可以简单的使用 return None;(TODO:None尚未实现)
这是处理V中错误的主要手段。函数的返回值依然是值,但是错误处理要简洁很多。
struct User {
id int
name string
}
struct Repo {
users []User
}
fn new_repo() Repo {
return Repo {
users: [User{1, 'Andrew'}, User {2, 'Bob'}, User {10, 'Charles'}]
}
}
fn (r Repo) find_user_by_id(id int) ?User {
for user in r.users {
if user.id == id {
// V 自动将其包装为 Option 类型
return user
}
}
return error('User $id not found')
}
fn main() {
repo := new_repo()
user := repo.find_user_by_id(10) or { // Option类型必须使用 `or` 的代码块hold住;
return // `or` 代码块必须以, return ,break,或 continue 关键字结束
}
println(user.id) // ==> "10"
println(user.name) // ==> 'Charles'
}
- 当然,错误还可以继续传播:
resp := http.get(url)?
println(resp.body)
上面例子中,http.get return ?http.Response 的可选类型,如果错误发生,将传播到调用函数,这里是导致main函数抛出异常。
上面代码是下面代码的简写:
resp := http.get(url) or {
panic(err)
}
println(resp.body)
范型 Generics (预计7月上线)
为了方便阅读, 允许使用 ⟨⟩ 来代替 <>. vfmt 自动替换 ⟨⟩ 为<> .
struct Repo⟨T⟩ {
db DB
}
fn new_repo⟨T⟩(db DB) Repo⟨T⟩ {
return Repo⟨T⟩{db: db}
}
// 这是一个范型函数. V 语言可以使用任意类型的范型
fn (r Repo⟨T⟩) find_by_id(id int) ?T {
table_name := T.name // 在此示例中,获取类型的名称会得到表明
return r.db.query_one⟨T⟩('select * from $table_name where id = ?', id)
}
db := new_db()
users_repo := new_repo⟨User⟩(db)
posts_repo := new_repo⟨Post⟩(db)
user := users_repo.find_by_id(1)?
post := posts_repo.find_by_id(1)?
并发
并发模型与Go非常相似。要foo()同时运行,只需调用它go foo(),便会在新的系统线程执行该函数 .
很快 将实现 goroutines 和 scheduler 。
JSON 解析
JSON 在今天已经非常流行,这也是为什么要内置json支持的原因。
- json.decode 函数的第一个参数是要解码的类型,第二个参数是 json字符串。
- json 用于生成json 编码和解码的代码,不使用运行时反射,这也会带来更好的性能。
struct User {
name string
age int
foo Foo [skip] //使用 skip 来跳过某些字段
}
data := '{ "name": "Frodo", "age": 25 }'
user := json.decode(User, data) or {
eprintln('Failed to decode json')
return
}
println(user.name)
println(user.age)
单元测试
所有的单元测试都必须放在 *test.v 文件中,并且测试函数必须使用 test 开头方式命名。
- 若要运行 测试文件 hello_test.v 用来测试某整个模块,请执行命令 v test mymodule
// hello.v
fn hello() string {
return 'Hello world'
}
// hello_test.v
fn test_hello() {
assert hello() == 'Hello world'
}
内存管理
V语言没有自动内存回收(GC)和引用计数,V语言会在编译阶段完成必要的清理工作。例如:
fn draw_text(s string, x, y int) {
...
}
fn draw_scene() {
...
draw_text('hello $name1', 10, 10)
draw_text('hello $name2', 100, 10)
draw_text(strings.repeat('X', 10000), 10, 50)
...
}
字符串 string 生命周期不会超出 draw_text 函数,因此当函数执行完时,它们将会被清除。实际上,前面两次调用根本不会导致任何分配,因为这两个字符串很小,V语言会使用提前准备好的缓冲区构造字符串。
-
对于更复杂的情况,需要手动内存管理,我们将很快实现解决。
-
V 将在运行时检测并报告内存泄露,例如:要清除数组,请使用 free() 方法:
numbers := [0; 1000000]
...
numbers.free()
延迟 Defer
延迟代码块将推迟执行,直到所属的函数执行结束或return。
fn read_log() {
f := os.open('log.txt')
defer { f.close() }
...
if !ok {
// 将在此处调用defer语句,文件将被关闭
return
}
...
//将在此处调用defer语句,文件将被关闭
}
vfmt 代码格式化
您不需要担心V语言的代码格式化问题, vfmt 帮你解决:
v fmt file.v
建议设置编辑器,以便vfmt在每次保存时运行,始终允许vfmt.
在推送代码之前始终运行vfmt。
进阶内容
V语言中调用C 函数
#flag -lsqlite3
#include "sqlite3.h"
struct C.sqlite3
struct C.sqlite3_stmt
fn C.sqlite3_column_int(C.sqlite_stmt, int) int
fn main() {
path := 'sqlite3_users.db'
db := &C.sqlite3{}
C.sqlite3_open(path.cstr(), &db)
query := 'select count(*) from users'
stmt := &C.sqlite3_stmt{}
C.sqlite3_prepare_v2(db, query.cstr(), - 1, &stmt, 0)
C.sqlite3_step(stmt)
nr_users := C.sqlite3_column_int(res, 0)
C.sqlite3_finalize(res)
println(nr_users)
}
- C strings 可以转换为 “string(cstring)”的V字符串
预编译if语句
预编译 使用 $if 。现在它只能用于检测操作系统。
$if windows {
println('Windows')
}
$if linux {
println('Linux')
}
$if mac {
println('macOS')
}
代码反射
有内置的 json支持已经是很不错的选择,但是V还允许使用者为任意添加高效的序列化程序。
// TODO: 计划7月上线
fn decode(data string) T {
mut result := T{}
for field in T.fields {
if field.typ == 'string' {
result.$field = get_string(data, field.name)
} else if field.typ == 'int' {
result.$field = get_int(data, field.name)
}
}
return result
}
// 生成:
fn decode_User(data string) User {
mut result := User{}
result.name = get_string(data, 'name')
result.age = get_int(data, 'age')
return result
}
有限的运算符重载
运算符重载违背了V的简单性和可预测性的理念。但由于科学和图形应用程序属于V域,因此为了提高可读性,运算符重载非常重要:
a.add(b).add(c.mul(d))比 a + b + c * d 可读性差得多了。
struct Vec {
x int
y int
}
fn (a Vec) str() string {
return '{$a.x, $a.y}'
}
fn (a Vec) + (b Vec) Vec {
return Vec {
a.x + b.x,
a.y + b.y
}
}
fn (a Vec) - (b Vec) Vec {
return Vec {
a.x - b.x,
a.y - b.y
}
}
fn main() {
a := Vec{2, 3}
b := Vec{4, 5}
println(a + b) // ==> "{6, 8}"
println(a - b) // ==> "{-2, -2}"
}
- 为了提高安全性和可维护性,运算符重载 有几个局限性:
- 只能使用 +,=,* ,/ 运算符的重载
- 不允许运算符重载函数内调用其他函数
- 运算符重载函数无法修改其参数
- 运算符函数的两个参数必须具有相同类型,(同V中所有运算符一样,str+num => error)
内联汇编
TODO 尚未实现
fn main() {
a := 10
asm x64 {
mov eax, [a]
add eax, 10
mov [a], eax
}
}
将 C/C++翻译成V
TODO: 将C翻译为V将于7月上市。C ++到V将于今年晚些时候推出
V可以将您的C / C ++代码转换为可读的V代码。让我们先创建一个简单的程序test.cpp
#include
#include
#include
int main() {
std::vector s;
s.push_back("V is ");
s.push_back("awesome");
std::cout << s.size() << std::endl;
return 0;
}
允许命令 v translate test.cpp 将会得到 test.v:
fn main {
mut s := []
s << 'V is '
s << 'awesome'
println(s.len)
}
一个在线C / C ++到V的翻译即将推出.
- 什么时候应该翻译C代码,什么时候应该从V调用C代码?
- 如果您有经过良好编写且经过良好测试的C代码,那么您当然可以直接从V调用此C代码
- 将其翻译为V有以下几个优点:
- 如果您计划开发该代码库,那么现在您可以使用一种语言编写所有内容,这样可以更安全,更容易开发
- 交叉编译(mac 编译打包 win程序)变得更加容易。您根本不必担心它。
- 没有更多的构建标志和包含文件。
热门代码重加载
module main
import time
import os
[live]
fn print_message() {
println('Hello! Modify this message while the program is running.')
}
fn main() {
for {
print_message()
time.sleep_ms(500)
}
}
构建此示例:v -live message.v
要重新加载的函数必须 将 [live] 在定义之前添加。
现在,在程序运行时无法修改类型。
更多示例,包括图形应用程序: github.com/vlang/v/tree/master/examples/hot_code_reloading。
交叉编译 (mac 编译打包 win程序)
-
要交叉编译项目,只需简单执行命令:
- v -os windows .
- v -os linux .
(暂时无法对macOS进行交叉编译。)
-
如果您没有任何C依赖项,那就是您需要做的。即使在使用ui模块或图形应用程序编译GUI应用程序时也可以使用 gg。
-
您需要安装Clang,LLD链接器,并下载包含库和包含Windows和Linux文件的zip文件。V将为您提供链接。
V中的跨平台shell脚本 (6月下旬)
-
V可以用作Bash的替代方案来编写部署脚本,构建脚本等。
-
使用V的优势在于语言的简单性和可预测性以及跨平台支持。“V脚本”在类Unix系统和Windows上运行。
-
使用#v指令启动程序。它将使os 模块中的所有函数全局化(您可以使用ls() 来代替os.ls() )例如:
#v
rm('build/*')
// 效果同:
for file in ls('build/') {
rm(file)
}
mv('*.v', 'build/')
// 效果同:
for file in ls('.') {
if file.ends_with('.v') {
mv(file, 'build/')
}
}
现在您可以像普通的V程序一样编译它,并获得可以在任何地方部署和运行的可执行文件: v deploy.v && ./deploy
(当然也可以使用传统脚本运行方式来允许它: v run deploy.v)
附录1:关键字
V 拥有22个关键字
break
const
continue
defer
else
enum
fn
for
go
goto
if
import
in
interface
match
module
mut
or
pub
return
struct
type
附录2:运算符
-
运算符
运算符 含义 支持类型 + sum integers, floats, strings – difference integers, floats * product integers, floats / quotient integers, floats % remainder integers & bitwise AND integers | bitwise OR integers, floats ^ bitwise XOR integers << left shift integer << unsigned integer >> right shift integer >> unsigned integer -
运算符优先级
优先级 | 运算符 |
---|---|
5 | * / % << >> & |
4 | + – | ^ |
3 | == != < <= > >= |
2 | && |
1 | || |
- 赋值运算符
+= -= *= /= %= &= |= ^= >>= <<=