Vala 语言入门教程 / 03 - 基本语法
第 3 章:基本语法
本章涵盖 Vala 语言的基本构建块:变量、类型、函数和命名空间。
3.1 程序入口:main 函数
每个 Vala 程序必须有一个 main 函数作为入口点:
// 最简单的 Vala 程序
void main () {
print ("Hello, World!\n");
}
3.1.1 带命令行参数的 main
// 使用字符串数组参数
void main (string[] args) {
print ("程序名: %s\n", args[0]);
print ("参数数量: %d\n", args.length - 1);
for (int i = 1; i < args.length; i++) {
print ("参数 %d: %s\n", i, args[i]);
}
}
valac args_demo.vala -o args_demo
./args_demo foo bar baz
# 输出:
# 程序名: ./args_demo
# 参数数量: 3
# 参数 1: foo
# 参数 2: bar
# 参数 3: baz
3.1.2 带返回值的 main
int main (string[] args) {
if (args.length < 2) {
printerr ("用法: %s <名字>\n", args[0]);
return 1; // 非零返回值表示错误
}
print ("你好, %s!\n", args[1]);
return 0; // 成功
}
💡
print()输出到 stdout,printerr()输出到 stderr。
3.2 变量
3.2.1 变量声明
Vala 使用类型推断(var)或显式类型来声明变量:
void main () {
// 显式类型声明
int age = 30;
string name = "张三";
double salary = 15000.50;
bool active = true;
// 类型推断(var 关键字)
var city = "北京"; // 推断为 string
var count = 42; // 推断为 int
var pi = 3.14159; // 推断为 double
var done = false; // 推断为 bool
// 打印
print ("姓名: %s, 年龄: %d\n", name, age);
print ("城市: %s, 工资: %.2f\n", city, salary);
print ("活跃: %s\n", active.to_string ());
}
3.2.2 变量命名规则
// ✅ 合法的变量名
int count = 0;
int user_age = 25;
int itemCount = 10;
int _internal = 42;
string 名字 = "中文变量名也可以"; // Vala 支持 Unicode 标识符
// ❌ 非法的变量名
// int 2count = 0; // 不能以数字开头
// int my-var = 0; // 不能包含连字符
// int class = 0; // 不能使用关键字
3.2.3 常量
// 模块级常量
const int MAX_SIZE = 100;
const string APP_NAME = "MyApp";
const double PI = 3.14159265358979;
void main () {
// const 常量必须在编译期确定值
print ("最大大小: %d\n", MAX_SIZE);
print ("应用名: %s\n", APP_NAME);
print ("圆周率: %.10f\n", PI);
// 局部常量
const string GREETING = "你好";
print ("%s\n", GREETING);
}
3.2.4 可空类型(Nullable Types)
void main () {
// 非空字符串(默认)
string name = "Vala";
// 可空字符串(加 ? 后缀)
string? nickname = null;
// 检查 null
if (nickname != null) {
print ("昵称: %s\n", nickname);
} else {
print ("没有昵称\n");
}
// 可空类型用于可能失败的操作
int? result = parse_int ("42");
if (result != null) {
print ("解析结果: %d\n", result);
}
int? bad = parse_int ("abc");
if (bad == null) {
print ("解析失败\n");
}
}
int? parse_int (string str) {
// 尝试将字符串转为整数
int val = int.parse (str);
if (val == 0 && str != "0") {
return null;
}
return val;
}
⚠️ 重要:Vala 中,
string默认是可空的(因为底层是 C 指针),但int等值类型默认是非空的。使用int?表示可空的整数。
3.3 基本数据类型
3.3.1 类型总览
| Vala 类型 | C 类型 | GLib 类型 | 大小 | 范围 |
|---|---|---|---|---|
bool | _Bool | gboolean | 1B | true / false |
int8 | int8_t | gint8 | 1B | -128 ~ 127 |
uint8 / uchar | uint8_t | guint8 | 1B | 0 ~ 255 |
int16 | int16_t | gint16 | 2B | -32768 ~ 32767 |
uint16 | uint16_t | guint16 | 2B | 0 ~ 65535 |
int / int32 | int | gint | 4B | -2³¹ ~ 2³¹-1 |
uint / uint32 | unsigned int | guint | 4B | 0 ~ 2³²-1 |
int64 | int64_t | gint64 | 8B | -2⁶³ ~ 2⁶³-1 |
uint64 | uint64_t | guint64 | 8B | 0 ~ 2⁶⁴-1 |
float | float | gfloat | 4B | ±3.4E38 |
double | double | gdouble | 8B | ±1.7E308 |
char | char | gchar | 1B | ASCII 字符 |
unichar | gunichar | gunichar | 4B | Unicode 码点 |
string | char* | gchar* | 指针 | UTF-8 字符串 |
3.3.2 整数类型
void main () {
// 基本整数
int a = 42;
int64 big = 9223372036854775807; // int64 最大值
uint positive = 100;
// 不同进制
int decimal = 255; // 十进制
int hex = 0xFF; // 十六进制
int octal = 0o377; // 八进制
int binary = 0b11111111; // 二进制
print ("十进制: %d\n", decimal);
print ("十六进制: 0x%x\n", hex);
print ("八进制: 0o%o\n", octal);
print ("二进制: 0b11111111\n");
// 数字分隔符(Vala 0.52+)
int million = 1_000_000;
int phone = 138_0000_0000;
print ("百万: %d\n", million);
// 类型转换
int x = 42;
double y = (double) x; // 显式转换
int z = (int) 3.14; // 截断
print ("y=%.1f, z=%d\n", y, z); // y=42.0, z=3
}
3.3.3 浮点类型
void main () {
float f = 3.14f; // float 需要 f 后缀
double d = 3.14159; // double 是默认浮点类型
double scientific = 1.5e10;
print ("float: %f\n", f);
print ("double: %.5f\n", d);
print ("科学计数法: %e\n", scientific);
// 特殊值
double inf = double.INFINITY;
double nan = double.NAN;
print ("无穷大: %f\n", inf);
print ("NaN: %f\n", nan);
print ("是否 NaN: %s\n", nan.is_nan ().to_string ());
}
3.3.4 布尔类型
void main () {
bool flag = true;
bool empty = false;
// 布尔运算
bool a = true && false; // false
bool b = true || false; // true
bool c = !true; // false
// 在 Vala 中,只有 true 和 false 是合法的布尔值
// 不像 C,0 和 1 不能隐式转换为 bool
print ("a=%s, b=%s, c=%s\n",
a.to_string (),
b.to_string (),
c.to_string ());
// 三元运算符
string status = flag ? "启用" : "禁用";
print ("状态: %s\n", status);
}
3.3.5 字符和字符串
void main () {
// char —— 单个 ASCII 字符
char letter = 'A';
char digit = '0';
print ("字符: %c, %c\n", letter, digit);
// string —— UTF-8 字符串
string greeting = "你好,世界!";
string path = "/home/user";
// 字符串长度(UTF-8 字符数,不是字节数)
print ("\"%s\" 的长度: %d\n", greeting, greeting.length);
// unichar —— Unicode 码点
unichar heart = '♥';
print ("心形: %c (U+%04X)\n", heart, heart);
// 字符串拼接
string first = "张";
string last = "三";
string full = first + last; // 用 + 拼接
print ("全名: %s\n", full);
// 原始字符串(raw string)
string raw = """这是一段
跨越多行的
原始字符串""";
print ("%s\n", raw);
// 字符串模板(Vala 0.52+ 不支持,使用 printf 风格)
string name = "Vala";
int ver = 0;
// 使用 string.join 或 printf 风格
string info = "语言: %s,版本: %d".printf (name, ver);
print ("%s\n", info);
}
3.3.6 字符串常用方法
void main () {
string s = "Hello, Vala World!";
// 查找
print ("包含 'Vala': %s\n", s.contains ("Vala").to_string ());
print ("以 'Hello' 开头: %s\n", s.has_prefix ("Hello").to_string ());
print ("以 '!' 结尾: %s\n", s.has_suffix ("!").to_string ());
print ("'Vala' 的位置: %d\n", s.index_of ("Vala"));
// 转换
print ("大写: %s\n", s.up ());
print ("小写: %s\n", s.down ());
print ("去空格: %s\n", " hello ".strip ());
// 截取
print ("前5个字符: %s\n", s.substring (0, 5));
print ("从第7个开始: %s\n", s.substring (7));
// 替换
print ("替换: %s\n", s.replace ("Vala", "Rust"));
// 分割
string csv = "a,b,c,d";
string[] parts = csv.split (",");
foreach (string part in parts) {
print (" 部分: %s\n", part);
}
// 连接
string[] words = {"Hello", "Vala", "World"};
string joined = string.joinv (" ", words);
print ("连接: %s\n", joined);
// 格式化
string formatted = "%s 有 %d 个字符".printf (s, s.length);
print ("%s\n", formatted);
}
3.4 格式化输出
Vala 使用 GLib 的 printf 风格格式化:
| 格式符 | 说明 | 示例 |
|---|---|---|
%d | 整数 | "%d".printf(42) → "42" |
%s | 字符串 | "%s".printf("hi") → "hi" |
%f | 浮点数 | "%f".printf(3.14) → "3.140000" |
%.2f | 保留2位小数 | "%.2f".printf(3.14) → "3.14" |
%x | 十六进制(小写) | "%x".printf(255) → "ff" |
%X | 十六进制(大写) | "%X".printf(255) → "FF" |
%o | 八进制 | "%o".printf(8) → "10" |
%c | 字符 | "%c".printf('A') → "A" |
%p | 指针地址 | "%p".printf(ptr) |
%ld | long int | "%ld".printf(big) |
%% | 字面量 % | "100%%" → "100%" |
void main () {
string name = "Vala";
int year = 2006;
double ver = 0.58;
// print 直接输出
print ("%s 诞生于 %d 年,当前版本 %.1f\n", name, year, ver);
// 格式化为字符串
string info = "%s v%.1f (%d)".printf (name, ver, year);
print ("%s\n", info);
// 对齐和填充
print ("|%-10s|%10d|%10.2f|\n", name, year, ver);
// 输出:
// |Vala | 2006| 0.58|
}
3.5 运算符
3.5.1 算术运算符
void main () {
int a = 10, b = 3;
print ("加: %d + %d = %d\n", a, b, a + b); // 13
print ("减: %d - %d = %d\n", a, b, a - b); // 7
print ("乘: %d * %d = %d\n", a, b, a * b); // 30
print ("除: %d / %d = %d\n", a, b, a / b); // 3(整数除法)
print ("模: %d %% %d = %d\n", a, b, a % b); // 1
// 注意:整数除法会截断
print ("10 / 3 = %d\n", 10 / 3); // 3
print ("10.0 / 3 = %.2f\n", 10.0 / 3); // 3.33
// 自增自减
int c = 5;
c++; // c = 6
c--; // c = 5
++c; // c = 6
--c; // c = 5
print ("c = %d\n", c);
// 复合赋值
c += 10; // c = 15
c -= 3; // c = 12
c *= 2; // c = 24
c /= 4; // c = 6
c %= 5; // c = 1
print ("c = %d\n", c);
}
3.5.2 比较运算符
void main () {
int x = 5, y = 10;
print ("%d == %d: %s\n", x, y, (x == y).to_string ()); // false
print ("%d != %d: %s\n", x, y, (x != y).to_string ()); // true
print ("%d < %d: %s\n", x, y, (x < y).to_string ()); // true
print ("%d > %d: %s\n", x, y, (x > y).to_string ()); // false
print ("%d <= %d: %s\n", x, y, (x <= y).to_string ()); // true
print ("%d >= %d: %s\n", x, y, (x >= y).to_string ()); // false
}
3.5.3 位运算符
void main () {
uint a = 0b1100; // 12
uint b = 0b1010; // 10
print ("AND: 0x%X\n", a & b); // 0x8 (0b1000)
print ("OR: 0x%X\n", a | b); // 0xE (0b1110)
print ("XOR: 0x%X\n", a ^ b); // 0x6 (0b0110)
print ("NOT: 0x%X\n", ~a); // 0xFFFFFFF3
print ("左移: 0x%X\n", a << 1); // 0x18 (0b11000)
print ("右移: 0x%X\n", a >> 1); // 0x6 (0b0110)
}
3.6 控制流
3.6.1 条件语句
void main () {
int score = 85;
// if-else if-else
if (score >= 90) {
print ("优秀\n");
} else if (score >= 80) {
print ("良好\n");
} else if (score >= 60) {
print ("及格\n");
} else {
print ("不及格\n");
}
// switch 语句
string grade;
switch (score / 10) {
case 10:
case 9:
grade = "A";
break;
case 8:
grade = "B";
break;
case 7:
grade = "C";
break;
case 6:
grade = "D";
break;
default:
grade = "F";
break;
}
print ("等级: %s\n", grade);
}
3.6.2 循环语句
void main () {
// for 循环
print ("--- for 循环 ---\n");
for (int i = 0; i < 5; i++) {
print (" i = %d\n", i);
}
// while 循环
print ("--- while 循环 ---\n");
int count = 0;
while (count < 3) {
print (" count = %d\n", count);
count++;
}
// do-while 循环
print ("--- do-while 循环 ---\n");
int n = 0;
do {
print (" n = %d\n", n);
n++;
} while (n < 3);
// break 和 continue
print ("--- break/continue ---\n");
for (int i = 0; i < 10; i++) {
if (i == 3) continue; // 跳过 3
if (i == 7) break; // 到 7 停止
print (" i = %d\n", i);
}
// 循环标签(用于嵌套循环的 break/continue)
print ("--- 循环标签 ---\n");
outer:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 1 && j == 1) {
break outer; // 跳出外层循环
}
print (" (%d, %d)\n", i, j);
}
}
}
3.6.3 foreach 循环
void main () {
// 遍历数组
string[] fruits = {"苹果", "香蕉", "橙子", "葡萄"};
print ("--- 水果列表 ---\n");
foreach (string fruit in fruits) {
print (" %s\n", fruit);
}
// 遍历带索引
print ("--- 带索引 ---\n");
for (int i = 0; i < fruits.length; i++) {
print (" %d: %s\n", i, fruits[i]);
}
// 遍历 GLib.List
var list = new GLib.List<int> ();
list.append (10);
list.append (20);
list.append (30);
print ("--- GLib.List ---\n");
foreach (int item in list) {
print (" %d\n", item);
}
// 遍历 GLib.Array
var array = new GLib.Array<string> ();
array.append_val ("hello");
array.append_val ("world");
print ("--- GLib.Array ---\n");
foreach (string item in array.data) {
print (" %s\n", item);
}
}
3.7 数组
3.7.1 基本数组
void main () {
// 固定长度数组
int[5] fixed = {1, 2, 3, 4, 5};
// 动态数组
int[] dynamic = {10, 20, 30};
// 空数组
string[] names = new string[0];
// 添加元素
names += "Alice";
names += "Bob";
names += "Charlie";
print ("数组长度: %d\n", names.length);
foreach (string name in names) {
print (" %s\n", name);
}
// 数组切片
int[] nums = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int[] slice = nums[2:5]; // [2, 3, 4]
print ("切片: ");
foreach (int s in slice) {
print ("%d ", s);
}
print ("\n");
// 多维数组
int[,] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
print ("矩阵[1][2] = %d\n", matrix[1, 2]); // 6
}
3.7.2 数组方法
void main () {
// 使用 GLib 的数组工具
int[] data = {5, 3, 8, 1, 9, 2, 7};
// 排序
// data.sort ((a, b) => a - b); // 升序
// 使用 qsort_with_data
GLib.qsort_with_data<int> (data, sizeof (int), (a, b) => {
return a - b;
});
print ("排序后: ");
foreach (int d in data) {
print ("%d ", d);
}
print ("\n");
// 移动语义
int[] a = {1, 2, 3};
int[] b = a; // 移动语义!a 现在为空
print ("a 长度: %d\n", a.length); // 0
print ("b 长度: %d\n", b.length); // 3
// 如果要复制,使用 dup
int[] c = {4, 5, 6};
int[] d = c; // 注意:这是移动,不是复制
// 要复制需要手动操作
}
⚠️ 重要:Vala 中数组赋值是移动语义,不是复制!赋值后原变量将变为空。
3.8 函数
3.8.1 函数定义
// 基本函数
void greet (string name) {
print ("你好, %s!\n", name);
}
// 带返回值
int add (int a, int b) {
return a + b;
}
// 带默认参数
void print_info (string name, int age = 0, string city = "未知") {
print ("%s, %d岁, %s\n", name, age, city);
}
// 可变参数
void log (string format, ...) {
var args = va_list ();
// 注意:Vala 的可变参数处理与 C 类似
print (format + "\n");
}
void main () {
greet ("世界");
int sum = add (3, 5);
print ("3 + 5 = %d\n", sum);
print_info ("张三", 30, "北京");
print_info ("李四", 25); // 使用默认城市
print_info ("王五"); // 使用默认年龄和城市
}
3.8.2 命名参数
void create_user (string name, int age, string email) {
print ("创建用户: %s, %d岁, %s\n", name, age, email);
}
void main () {
// 使用命名参数(顺序可以不同)
create_user (name: "张三", age: 30, email: "[email protected]");
create_user (email: "[email protected]", name: "李四", age: 25);
// 混合使用位置参数和命名参数
create_user ("王五", age: 28, email: "[email protected]");
}
3.8.3 返回多个值(out 参数)
// 使用 out 参数返回多个值
void divide (int a, int b, out int quotient, out int remainder) {
quotient = a / b;
remainder = a % b;
}
// 使用 out 参数进行可能失败的操作
bool try_parse (string str, out int result) {
if (str.length == 0) {
result = 0;
return false;
}
// 简单的数字解析
result = 0;
for (int i = 0; i < str.length; i++) {
char c = str[i];
if (c < '0' || c > '9') {
result = 0;
return false;
}
result = result * 10 + (c - '0');
}
return true;
}
void main () {
// 使用 out 参数
int q, r;
divide (17, 5, out q, out r);
print ("17 / 5 = %d 余 %d\n", q, r);
// 使用可能失败的操作
int val;
if (try_parse ("123", out val)) {
print ("解析成功: %d\n", val);
} else {
print ("解析失败\n");
}
if (try_parse ("abc", out val)) {
print ("解析成功: %d\n", val);
} else {
print ("解析失败\n");
}
}
3.8.4 Lambda 表达式
void main () {
// 简单的 lambda
var greet = (name) => {
print ("你好, %s!\n", name);
};
greet ("Vala");
// 作为回调
string[] names = {"Charlie", "Alice", "Bob"};
// 使用 GLib 的排序函数
// 注意:Vala 的数组排序使用 qsort_with_data
var list = new GLib.List<string> ();
foreach (string name in names) {
list.append (name);
}
list.sort ((a, b) => {
return strcmp (a, b);
});
print ("排序后: ");
foreach (string name in list) {
print ("%s ", name);
}
print ("\n");
// 带类型的 lambda
var add = (int a, int b) => {
return a + b;
};
print ("2 + 3 = %d\n", add (2, 3));
}
3.8.5 委托(Delegate)
// 定义委托类型
delegate int MathFunc (int a, int b);
// 使用委托的函数
int apply (int a, int b, MathFunc func) {
return func (a, b);
}
int multiply (int a, int b) { return a * b; }
int subtract (int a, int b) { return a - b; }
void main () {
// 使用函数引用
print ("3 * 4 = %d\n", apply (3, 4, multiply));
print ("10 - 3 = %d\n", apply (10, 3, subtract));
// 使用 lambda
print ("2 + 5 = %d\n", apply (2, 5, (a, b) => a + b));
}
3.9 命名空间
3.9.1 基本命名空间
// 定义命名空间
namespace MathUtils {
public int factorial (int n) {
if (n <= 1) return 1;
return n * factorial (n - 1);
}
public double circle_area (double radius) {
return 3.14159 * radius * radius;
}
// 嵌套命名空间
namespace Geometry {
public struct Point {
public double x;
public double y;
}
public double distance (Point a, Point b) {
double dx = a.x - b.x;
double dy = a.y - b.y;
return Math.sqrt (dx * dx + dy * dy);
}
}
}
void main () {
// 使用命名空间中的函数
print ("5! = %d\n", MathUtils.factorial (5));
print ("圆面积(r=3): %.2f\n", MathUtils.circle_area (3.0));
// 使用嵌套命名空间
MathUtils.Geometry.Point p1 = {0, 0};
MathUtils.Geometry.Point p2 = {3, 4};
print ("两点距离: %.2f\n", MathUtils.Geometry.distance (p1, p2));
}
3.9.2 using 指令
using MathUtils;
using MathUtils.Geometry;
void main () {
// 使用 using 后可以省略命名空间前缀
print ("5! = %d\n", factorial (5));
print ("圆面积(r=3): %.2f\n", circle_area (3.0));
Point p1 = {0, 0};
Point p2 = {3, 4};
print ("两点距离: %.2f\n", distance (p1, p2));
}
💡 命名空间不生成任何 C 代码,它仅在编译期用于符号解析。Vala 的命名空间纯粹是编译器层面的概念。
3.10 枚举
// 基本枚举
enum Color {
RED,
GREEN,
BLUE;
// 枚举方法
public string to_string () {
switch (this) {
case RED: return "红色";
case GREEN: return "绿色";
case BLUE: return "蓝色";
default: return "未知";
}
}
}
// 带值的枚举
enum HttpStatus {
OK = 200,
NOT_FOUND = 404,
INTERNAL_ERROR = 500;
}
// 标志枚举(位域)
[Flags]
enum Permissions {
NONE = 0,
READ = 1,
WRITE = 2,
EXECUTE = 4;
}
void main () {
Color c = Color.RED;
print ("颜色: %s\n", c.to_string ());
HttpStatus status = HttpStatus.NOT_FOUND;
print ("HTTP 状态码: %d\n", status);
// 标志枚举的使用
Permissions perms = Permissions.READ | Permissions.WRITE;
print ("权限: %s\n", perms.to_string ());
print ("有读权限: %s\n", (perms & Permissions.READ).to_string ());
print ("有执行权限: %s\n", (perms & Permissions.EXECUTE).to_string ());
}
3.11 结构体
// 基本结构体
struct Point {
public double x;
public double y;
// 结构体方法
public double distance_to (Point other) {
double dx = x - other.x;
double dy = y - other.y;
return Math.sqrt (dx * dx + dy * dy);
}
}
// 带构造器的结构体
struct Size {
public int width;
public int height;
// 结构体没有构造器语法,使用静态工厂方法
public static Size create (int w, int h) {
Size s = Size ();
s.width = w;
s.height = h;
return s;
}
public int area () {
return width * height;
}
}
void main () {
Point p1 = {10.0, 20.0};
Point p2 = {13.0, 24.0};
print ("距离: %.2f\n", p1.distance_to (p2));
Size s = Size.create (100, 200);
print ("尺寸: %dx%d, 面积: %d\n", s.width, s.height, s.area ());
}
3.12 错误处理
// 定义错误域
errordomain FileError {
NOT_FOUND,
PERMISSION_DENIED,
IO_ERROR
}
// 抛出错误的函数
string read_file (string path) throws FileError {
if (path == "") {
throw new FileError.NOT_FOUND ("文件路径不能为空");
}
try {
// 使用 GLib 的文件操作
string content;
size_t length;
GLib.FileUtils.get_contents (path, out content, out length);
return content;
} catch (GLib.Error e) {
throw new FileError.IO_ERROR ("读取文件失败: " + e.message);
}
}
void main () {
try {
string content = read_file ("/etc/hostname");
print ("文件内容: %s\n", content);
} catch (FileError e) {
printerr ("错误: %s\n", e.message);
}
// 处理不存在的文件
try {
string content = read_file ("/nonexistent");
print ("文件内容: %s\n", content);
} catch (FileError e) {
printerr ("错误: %s\n", e.message);
}
}
3.13 注意事项
⚠️ 初学者常见陷阱
- 数组是移动语义:
int[] b = a;会使a变为空 - 字符串不可变:Vala 字符串是不可变的,修改会创建新字符串
- 值类型 vs 引用类型:
struct是值类型,class是引用类型 - null 安全:引用类型默认可以为 null,注意空指针检查
- 分号必须:每条语句后必须有分号(不像 Go)
- 类型推断:
var推断类型,但变量类型一旦确定不能改变
3.14 扩展阅读
| 资源 | 链接 |
|---|---|
| Vala 语言规范 | https://wiki.gnome.org/Projects/Vala/LanguageSpecification |
| Vala 类型系统 | https://wiki.gnome.org/Projects/Vala/TypeSystem |
| Vala 教程 | https://wiki.gnome.org/Projects/Vala/Tutorial |
| Valadoc API 文档 | https://valadoc.org/ |
| GLib 文档 | https://docs.gtk.org/glib/ |
3.15 总结
| 要点 | 说明 |
|---|---|
| 入口函数 | void main() 或 int main(string[] args) |
| 类型推断 | 使用 var 关键字 |
| 可空类型 | 值类型加 ? 后缀 |
| 数组赋值 | 移动语义,非复制 |
| 错误处理 | throws + try/catch + errordomain |
| 命名空间 | 编译期概念,不生成 C 代码 |
| 格式化 | printf 风格,使用 %d、%s 等 |
下一章我们将学习 Vala 的面向对象编程。→ 第 4 章:面向对象编程