V语言11写文档
类似go
.从文档串
中自动生成.函数/类型/常数
的文档必须放在其上1行.
//清理所有
fn clearall() {
}
注释,必须以自己名字
开头.多行用多个单行注释
.注释最好是现在时
.
模块简介,应是模块名
后的第1个注释.v doc net.http
来生成文档.
工具
v fmt 文件.v
格式化文件
.一次运行,非常便宜,v fmt -w(每次保存时运行)
约30毫秒.
拉代码时,v fmt -w file.v
.
用v -profile profile.txt run file.v
来检查性能.产生性能文件
供你分析.有四列:调用次数|总时间|平均时间|函数名
.
sort -n -k3 profile.txt|tail
这样来按第3列
排序.
用秒表显式测试:
import time
fn main() {
sw := time.new_stopwatch({})
println("你好")
println("花费: ${sw.elapsed().nanoseconds()}ns")
}
高级主题
运行时转存表达式
用dump(expr)
转存表达式
.
fn factorial(n u32) u32 {
if dump(n <= 1) {
return dump(1)
}
return dump(n * factorial(n - 1))//转存
}
fn main() {
println(factorial(5))
}
这样:v run factorial.v
.转存
会跟踪源位置,表达式,表达式的值
.
内存不安全代码
写低级但高效代码,V
要求标记出不安全
的代码.
序号 | 可能不安全动作 |
---|---|
1 | 指针运算 |
2 | 指针索引 |
3 | 从不匹配类型转换为指针 |
4 | 调用某些free,strlen和strncmp 等C函数 |
用unsafe
来包装它.
//分配未初化2字节并返回到它的引用
mut p := unsafe { malloc(2) }
p[0] = `h` //指针索引,仅在`不安全`块允许
unsafe {
p[0] = `h` // OK
p[1] = `i`
}
p++ //指针算术,仅在`不安全`块允许
unsafe {
p++ // OK
}
assert *p == `i`
内存安全的表达式
不要放在不安全
块中,这样用不安全
的原因,就很清晰.不安全块代码中不应有
安全代码.
你怀疑程序违反内存安全,可以检查不安全
块行为.
正在进行
中.
带引用字段构
带引用字段要求显式给引用字段
赋初值,除非构
已定义初值.
未来不支持零值引用/空针
.现在在注意置0
是不安全的,会导致恐慌
.
struct Node {
a &Node
b &Node = 0 //自动初化为0,使用时小心
}
//必须初化引用字段,除非如b一样,已声明
// Zero (0)没问题,但要小心,是空针.
foo := Node{
a: 0
}
bar := Node{
a: &foo
}
baz := Node{
a: 0
b: 0
}
qux := Node{
a: &foo
b: &bar
}
println(baz)
println(qux)
sizeof和__offsetof
.前者sizeof(Type)
按字节
给定大小,
后者__offsetof(Struct, field_name)
像这样给定字段偏移.
struct Foo {
a int
b int
}
assert sizeof(Foo) == 8
assert __offsetof(Foo, a) == 0
assert __offsetof(Foo, b) == 4
从V
调用C
:
#flag -lsqlite3
#include "sqlite3.h"
...C系接口...
//https://www.sqlite.org/quickstart.html,示例
fn my_callback(arg voidptr, howmany int, cvalues &&char, cnames &&char) int {
unsafe {
for i in 0 .. howmany {
print("| ${cstring_to_vstring(cnames[i])}: ${cstring_to_vstring(cvalues[i]):20} ")
}
}
println("|")
return 0
}
fn main() {
db := &C.sqlite3(0)
C.sqlite3_open(c"users.db", &db)
query := "select count(*) from users"
stmt := &C.sqlite3_stmt(0)
C.sqlite3_prepare_v2(db, &char(query.str), -1, &stmt, 0)
C.sqlite3_step(stmt)
nr_users := C.sqlite3_column_int(stmt, 0)
C.sqlite3_finalize(stmt)
println("数据库中有$nr_users用户.")
//
error_msg := &char(0)
query_all_users := "select * from users"
rc := C.sqlite3_exec(db, &char(query_all_users.str), my_callback, voidptr(7), &error_msg)
if rc != C.SQLITE_OK {
eprintln(unsafe { cstring_to_vstring(error_msg) })
C.sqlite3_free(error_msg)
}
C.sqlite3_close(db)
}
flag
,传递C
编译标志.在V
文件开头.
标志 | 意思 |
---|---|
-I | 包含路径 |
-l | 库名 |
-L | C库搜索路径 |
-D | 编译时变量 |
不同目标用不同标志:可用有:linux,darwin,freebsd,及windows
每个标志,必须单独一行:
#flag linux -lsdl2
#flag linux -Ivig
#flag linux -DCIMGUI_DEFINE_ENUMS_AND_STRUCTS=1
#flag linux -DIMGUI_DISABLE_OBSOLETE_FUNCTIONS=1
#flag linux -DIMGUI_IMPL_API=
控制台中,你可用-cflags
来传递自定义标志,用-cc
来改编译器
,如-cc gcc-9 -cflags -fsanitize=thread
.
可在终端
中定义VFLAGS
来存储-cc
与-cflags
标志,避免重复定义他们.
属性
在函数/构/枚
声明前,作用于该声明
.
//调用时,过时警告
[deprecated]
fn old_function() {
}
//自定义消息
[deprecated: "用新 new_function() 替代"]
fn legacy_function() {}
//会内联该函数
[inline]
fn inlined_function() {
}
//构在堆中分配只能用作引用
[heap]
struct Window {
}
//如果标志为假,则不编译
// 用`v -d flag`
[if debug]
fn foo() {
}
fn bar() {
foo() //未传递`-d debug`,则不调用
}
//函数返回前,垃集不释放内存
[keep_args_alive]
fn C.my_external_function(voidptr, int, voidptr) int
//必须在不安全块,
[unsafe]
fn risky_business() {
//检查代码及前条件
unsafe {
// 不检查,指针算术,访问联,调用`不安全`
// 最好最小化不安全块代码
}
//仍检查这里
}
//本函数,手动释放,程序员负责
[manualfree]
fn custom_allocations() {
}
//C互操作,C方式
[typedef]
struct C.Foo {
}
// 传递Win32 API回调
[windows_stdcall]
fn C.DefWindowProc(hwnd int, msg int, lparam int, wparam int)
//导入gg,ui图形库时,图窗占优势,不创建控制台
//有效禁止`打印行`语句,显式创建控制台,仅在`主`前有效.
[console]
fn main() {
}
允许goto
跳至标签.要求不安全
,要尽量少用.
交叉编译
v -os windows ./v -os linux .
目前不支持macOS
.
热加载
module main
import time
import os
[live]
fn print_message() {
println("运行本程序时修改")
}
fn main() {
for {
print_message()
time.sleep(500 * time.millisecond)
}
}
v -live message.v
.目前,运行程序时不能修改类型
.
转换C到V
#include "stdio.h"
int main() {
for (int i = 0; i < 10; i++) {
printf("你好,世界\n");
}
return 0;
}//转成下面
fn main() {
for i := 0; i < 10; i++ {
println("你好,世界")
}
}
C库中
生成包装器
:v wrapper c_code/libsodium/src/libsodium
.优秀C
代码,可转译至V
.
序号 | 好处 |
---|---|
1 | 用1种语言开发代码,更安全,更容易 |
2 | 交叉编译更简单 |
3 | 不需要各种标志 |
内联汇编
a := 100
b := 20
mut c := 0
asm amd64 {
mov eax, a
add eax, b
mov c, eax
; =r (c) as c // output
; r (a) as a // input
r (b) as b
}
println("a: $a") // 100
println("b: $b") // 20
println("c: $c") // 120
有限操作符重载:
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}
mut c := Vec{1, 2}
println(a + b) // "{6, 8}"
println(a - b) // "{-2, -2}"
c += a
println(c) // "{3, 5}"
}
用来改进可读性
,为了安全性/维护性
,限制:
序号 | 限制 |
---|---|
1 | 仅能重载+,-,*,/,%,<,>,==,!=,<=,>= |
2 | 编译器重载==/!= ,但你可覆盖 |
3 | 禁止符号 函数中调用其他函数 |
4 | 符号函数,不能修改参数 |
5 | 用</== 时,必须返回极 . |
6 | 定义了==和< 后,自动生成!=,>,<=及>= |
7 | 两边参数类型必须相同 |
8 | 定义操作符时,自动生成*=,+=,/= ,且必须返回相同类型 |
编译时反射
允许你对任何数据格式创建序化器.V
有编译时if/for
.
// TODO: 未完全实现
struct User {
name string
age int
}
// T应仅传递构名
fn decode<T>(data string) T {
mut result := T{}
// 编译时`for`循环
// T.fields给出字段元数据类型数组
$for field in T.fields {
$if field.typ is string {
// $(string_expr)产生标识符
result.$(field.name) = get_string(data, field.name)
} $else $if field.typ is int {
result.$(field.name) = get_int(data, field.name)
}
}
return result
}
// `decode<User>` 生成:
fn decode_User(data string) User {
mut result := User{}
result.name = get_string(data, "name")
result.age = get_int(data, "age")
return result
}
优化
用-prod
编译时,如果给定提示
,还可进一步
优化:
属性 | 意思 |
---|---|
[inline] | 内联 |
[direct_array_access] | 转为C数组操作来直接访问,省略检查边界,但不安全 . |
if _likely_(bool expression) { | 该分支很可能真 |
if _unlikely_(bool expression) { | 很可能假 |
编译时伪变量,编译时替换,类似宏
.
伪变量 | 意思 |
---|---|
@FN | 当前函数名 |
@METHOD | 用ReceiverType.MethodName 来替换,方法 |
@MOD | 模块名 |
@STRUCT | 构名 |
@FILE | 源码文件路径 |
@LINE | 行号,串表示 |
@COLUMN | 列号 |
@VEXE | V编译器路径 |
@VEXEROOT | V编译器路径根, |
@VHASH | V编译器的哈希 |
@VMOD_FILE | 最近v.mod 内容 |
@VMODROOT | 最近v.mod 根目录 |
调试/记录/跟踪
时有用:
eprintln("file: " + @FILE + " | line: " + @LINE + " | fn: " + @MOD + "." + @FN)
//或嵌入版本号
import v.vmod
vm := vmod.decode( @VMOD_FILE ) or { panic(err.msg) }
eprintln("$vm.name $vm.version\n $vm.description")
//后面为环境特殊变量
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现