WEBKT

WebAssembly 控制流:与 C 和 JavaScript 的对比

31 0 0 0

为什么关注控制流?

Wasm 控制流指令总览

1. block - 块:代码的组织者

Wasm 中的 block

C 中的块

JavaScript 中的块

对比

2. loop - 循环:重复执行的利器

Wasm 中的 loop

C 中的循环

JavaScript 中的循环

对比

3. if / else - 条件语句:分支选择

Wasm 中的 if / else

C 中的 if / else

JavaScript 中的 if / else

对比

4. br - 转移:跳转到目标块

Wasm 中的 br

C 中的跳转

JavaScript 中的跳转

对比

5. br_if - 条件转移:根据条件跳转

Wasm 中的 br_if

C 中的条件跳转

JavaScript 中的条件跳转

对比

6. br_table - 表格转移:多分支跳转

Wasm 中的 br_table

C 中的表格跳转

JavaScript 中的表格跳转

对比

总结

你好,我是老码农。今天我们来聊聊 WebAssembly (Wasm) 中的控制流,以及它和 C、JavaScript 这些我们熟悉的语言的异同。

为什么关注控制流?

控制流是编程的基石。它决定了代码的执行顺序,让我们能够根据不同的条件执行不同的逻辑。理解 Wasm 的控制流对于编写高效、可移植的代码至关重要。毕竟,Wasm 的目标是成为一种通用的、可编译的目标格式,让我们可以用多种语言编写代码,并在浏览器或其他的运行时环境中运行。

Wasm 控制流指令总览

Wasm 的控制流指令主要包括以下几种:

  • block: 块,用于组织代码,可以有标签和返回值。
  • loop: 循环,类似于其他语言的 while 循环,但更灵活。
  • if / else: 条件语句,根据条件执行不同的代码块。
  • br: 转移,用于跳转到指定的块(block, loop, if)的末尾。
  • br_if: 条件转移,根据条件跳转。
  • br_table: 表格转移,根据索引跳转到不同的块。

接下来,我们会逐一对比这些指令与 C 和 JavaScript 中的对应控制流语句。

1. block - 块:代码的组织者

Wasm 中的 block

在 Wasm 中,block 用于创建代码块,它可以包含一系列指令,并可以有标签。标签用于跳转到块的开始或结束位置。一个 block 也可以有一个返回值。

示例:

(block $my_block
(i32.const 10)
(i32.const 20)
(i32.add)
(return)
)

在这个例子中,我们定义了一个名为 $my_block 的块。块内部执行了加法运算,并将结果压入栈中。return 语句用于从函数中返回,实际上,return 也可以看作是一种特殊的 br 指令,跳转到函数的末尾。

C 中的块

C 语言中的块通常用 {} 括起来,用于组织语句。C 中的块没有显式的标签,但可以使用 goto 语句跳转到块内的特定位置(通常是块内的标号)。

示例:

{
int a = 10;
int b = 20;
int sum = a + b;
printf("%d\n", sum);
}

JavaScript 中的块

JavaScript 中的块也用 {} 括起来,用于组织语句。JavaScript 中的块也没有标签,但可以使用 breakcontinue 语句控制循环的执行。从 ES2015 (ES6) 开始,JavaScript 支持 labelbreak label 的形式,实现对特定块的跳转。

示例:

{
let a = 10;
let b = 20;
let sum = a + b;
console.log(sum);
}
loop1:
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (i === 1 && j === 1) {
break loop1; // 跳出 loop1 循环
}
console.log(i, j);
}
}

对比

  • 标签: Wasm 的 block 显式支持标签,而 C 和 JavaScript 中的块通常没有标签(JavaScript 从 ES6 开始支持)。
  • 返回值: Wasm 的 block 可以有返回值,而 C 和 JavaScript 的块通常没有返回值。
  • 用途: Wasm 的 block 更灵活,可以用于实现复杂的控制流,而 C 和 JavaScript 的块主要用于组织代码。

2. loop - 循环:重复执行的利器

Wasm 中的 loop

Wasm 的 loop 指令用于创建循环。loop 类似于其他语言的 while 循环,但更灵活,因为它允许你在循环的任何位置使用 br 指令跳转到循环的开始或结束位置。

示例:

(loop $my_loop
(i32.const 1)
(i32.const 2)
(i32.add)
(drop) ; 丢弃结果
(br $my_loop) ; 跳转到循环开始处
)

在这个例子中,循环不断执行加法运算,并丢弃结果。br $my_loop 将控制流转移到循环的开始处。

C 中的循环

C 语言提供了多种循环语句,包括 forwhiledo-while。这些循环语句提供了不同的控制方式,满足不同的循环需求。

示例:

// for 循环
for (int i = 0; i < 10; i++) {
printf("%d\n", i);
}
// while 循环
int i = 0;
while (i < 10) {
printf("%d\n", i);
i++;
}
// do-while 循环
int i = 0;
do {
printf("%d\n", i);
i++;
} while (i < 10);

JavaScript 中的循环

JavaScript 也提供了多种循环语句,包括 forwhiledo-while,以及 for...infor...of 循环。

示例:

// for 循环
for (let i = 0; i < 10; i++) {
console.log(i);
}
// while 循环
let i = 0;
while (i < 10) {
console.log(i);
i++;
}
// do-while 循环
let i = 0;
do {
console.log(i);
i++;
} while (i < 10);
// for...of 循环
const arr = [1, 2, 3, 4, 5];
for (const item of arr) {
console.log(item);
}

对比

  • 灵活性: Wasm 的 loop 更灵活,可以在循环的任何位置使用 br 指令进行控制流转移,而 C 和 JavaScript 的循环语句通常使用 breakcontinue 控制循环。
  • 标签: Wasm 的 loop 可以使用标签,而 C 和 JavaScript 的循环语句通常使用隐式的标签(通过 breakcontinue)。
  • 控制: Wasm 的 loop 更底层,需要手动控制循环的条件和终止,而 C 和 JavaScript 的循环语句提供了更高级的抽象。

3. if / else - 条件语句:分支选择

Wasm 中的 if / else

Wasm 的 if 语句用于根据条件执行不同的代码块。if 语句可以有 else 分支。

示例:

(if (i32.eq (i32.const 10) (i32.const 20))
(then
(i32.const 1)
)
(else
(i32.const 0)
)
)

在这个例子中,如果 10 等于 20,则执行 then 分支,否则执行 else 分支。

C 中的 if / else

C 语言的 if 语句用于根据条件执行不同的代码块。if 语句可以有 else 分支。

示例:

if (10 == 20) {
printf("true\n");
} else {
printf("false\n");
}

JavaScript 中的 if / else

JavaScript 的 if 语句用于根据条件执行不同的代码块。if 语句可以有 else 分支。

示例:

if (10 === 20) {
console.log("true");
} else {
console.log("false");
}

对比

  • 基本功能: Wasm、C 和 JavaScript 的 if / else 语句都提供了基本的功能:根据条件执行不同的代码块。
  • 语法: 它们的语法都比较相似,都是通过 if 关键字和条件表达式来实现的。
  • 复杂性: Wasm 的 if 语句相对简单,没有 C 和 JavaScript 中复杂的语法糖,例如 else if。如果需要多分支,可以嵌套 if 语句。

4. br - 转移:跳转到目标块

Wasm 中的 br

Wasm 的 br 指令用于无条件地跳转到指定的块。br 后面跟着一个标签,该标签标识了要跳转到的块。

示例:

(block $my_block
(i32.const 10)
(br $my_block)
(i32.const 20) ; 这行永远不会被执行
)

在这个例子中,br $my_block 将控制流转移到 $my_block 块的末尾。

C 中的跳转

C 语言中可以使用 goto 语句实现跳转。goto 语句需要一个标号,用于标识要跳转到的位置。

示例:

{
goto my_label;
printf("This line will not be executed.\n");
my_label:
printf("Hello, world!\n");
}

JavaScript 中的跳转

JavaScript 中没有直接的 goto 语句,但可以使用 breakcontinue 语句控制循环的执行,或者使用 throw 语句抛出异常来实现跳转。

示例:

// 使用 break 跳出循环
for (let i = 0; i < 10; i++) {
if (i === 5) {
break;
}
console.log(i);
}
// 使用 throw 抛出异常
try {
throw new Error("Something went wrong!");
} catch (e) {
console.error(e.message);
}

对比

  • 功能: Wasm 的 br 和 C 的 goto 语句都提供了无条件跳转的功能。JavaScript 没有直接的 goto,但可以通过其他方式实现跳转。
  • 标签: Wasm 的 br 使用标签,C 的 goto 也使用标号。
  • 灵活性: goto 在 C 中可以跳转到任意位置,而 Wasm 的 br 只能跳转到块的开始或结束位置,这提高了代码的结构性和可读性。

5. br_if - 条件转移:根据条件跳转

Wasm 中的 br_if

Wasm 的 br_if 指令用于根据条件跳转到指定的块。br_if 后面跟着一个标签和一个条件。如果条件为真,则跳转到指定的块;否则,继续执行下一条指令。

示例:

(block $my_block
(i32.const 10)
(i32.const 20)
(i32.eq)
(br_if $my_block)
(i32.const 30)
(drop)
)

在这个例子中,如果 10 等于 20(条件为假),则继续执行 i32.const 30,否则跳转到 $my_block 块的末尾。

C 中的条件跳转

C 语言中可以使用 if 语句和 goto 语句结合实现条件跳转。

示例:

if (10 == 20) {
goto my_label;
}
printf("This line will be executed.\n");
my_label:
printf("Hello, world!\n");

JavaScript 中的条件跳转

JavaScript 中可以使用 if 语句和 break/continue 语句结合实现条件跳转。

示例:

for (let i = 0; i < 10; i++) {
if (i === 5) {
break; // 或 continue
}
console.log(i);
}

对比

  • 功能: Wasm 的 br_if 和 C 的 if 结合 goto 语句、以及 JavaScript 的 if 结合 break/continue 都提供了条件跳转的功能。
  • 简洁性: Wasm 的 br_if 更加简洁,将条件判断和跳转合并在一个指令中。
  • 可读性: Wasm 的 br_if 提高了代码的可读性,因为它清晰地表达了条件跳转的意图。

6. br_table - 表格转移:多分支跳转

Wasm 中的 br_table

Wasm 的 br_table 指令用于根据索引跳转到不同的块。br_table 后面跟着一个标签列表和一个索引。根据索引的值,跳转到标签列表中对应的块。如果索引超出范围,则跳转到默认块。

示例:

(block $default
(i32.const -1)
)
(block $block1 (i32.const 1))
(block $block2 (i32.const 2))
(block $block3 (i32.const 3))
(i32.const 2)
(br_table $block1 $block2 $block3 $default)

在这个例子中,br_table 指令根据栈顶的索引值 (2) 跳转到 $block3 块,该块的返回值是 3。

C 中的表格跳转

C 语言可以使用 switch 语句实现多分支跳转。

示例:

int index = 2;
switch (index) {
case 0:
printf("case 0\n");
break;
case 1:
printf("case 1\n");
break;
case 2:
printf("case 2\n");
break;
default:
printf("default\n");
}

JavaScript 中的表格跳转

JavaScript 中可以使用 switch 语句实现多分支跳转。

示例:

let index = 2;
switch (index) {
case 0:
console.log("case 0");
break;
case 1:
console.log("case 1");
break;
case 2:
console.log("case 2");
break;
default:
console.log("default");
}

对比

  • 功能: Wasm 的 br_table 和 C/JavaScript 的 switch 语句都提供了多分支跳转的功能。
  • 性能: br_table 通常比一系列 if/else 语句或 switch 语句更高效,因为它可以通过查表来实现跳转。
  • 灵活性: Wasm 的 br_table 更加灵活,可以跳转到任意块,而不仅仅是代码块的开始位置。

总结

我们已经对比了 Wasm 的控制流指令与 C 和 JavaScript 中的对应语句。总结如下:

  • block: Wasm 的 block 提供了更灵活的标签和返回值支持,用于组织代码。
  • loop: Wasm 的 loop 更底层,可以灵活控制循环的跳转,而 C 和 JavaScript 的循环提供了更高级的抽象。
  • if / else: Wasm、C 和 JavaScript 的 if / else 语句在功能上基本一致,但 Wasm 的 if 语句更简洁。
  • br: Wasm 的 br 提供了无条件跳转的功能,C 的 goto 也有类似的功能,但 Wasm 的 br 提高了代码的结构性和可读性。
  • br_if: Wasm 的 br_if 提供了条件跳转的功能,更加简洁。
  • br_table: Wasm 的 br_table 提供了多分支跳转的功能,通常比 C/JavaScript 的 switch 语句更高效。

总的来说,Wasm 的控制流指令设计得既强大又灵活,既能满足底层控制的需求,又能提高代码的结构性和可读性。对于熟悉 C 和 JavaScript 的开发者来说,理解 Wasm 的控制流指令并不困难,但需要注意 Wasm 的一些特性,例如显式标签和栈操作。熟练掌握这些控制流指令,将有助于你编写高效、可移植的 Wasm 代码,并在 Web 平台上释放出更强大的潜能。

希望这篇文章能帮助你更好地理解 Wasm 的控制流。如果你有任何问题,欢迎留言讨论。下次再见!

老码农 WebAssemblyWasm控制流CJavaScript

评论点评

打赏赞助
sponsor

感谢您的支持让我们更好的前行

分享

QRcode

https://www.webkt.com/article/8062