Tiny-C 参考手册摘录
摘要
来自 tiny-c 参考手册的摘录,描述了 tiny-c 编程语言的起源、设计理念和特性,该语言在简单集成的环境中结合了 BASIC、LOGO 和 C 的元素。
暂无内容
查看缓存全文
缓存时间: 2026/07/07 05:09
# tiny-c 参考手册摘录
来源:https://permacomputer.solarpunk.au/?p=204
*该项目发现这份小手册很有教学意义。可以说这是一种受 BASIC 启发的 C 语言变体!*
https://archive.org/details/tiny-c_manual
## 前言
tiny-c 的灵感来源众多。首先是 BASIC [Kemeny & Kurtz 1967]。BASIC 已成为美国事实上的标准教学语言。它在高中、大学乃至一些生产工作中都很流行。尽管 BASIC 有缺陷,但它最大的优势之一就是易于学习。这主要是因为它提供了一个单一的计算环境。你可以输入新的程序行、修改旧的行、启动程序运行,所有这些都在一个命令环境中完成。你不需要记住当前所处的环境(例如编辑模式、编译模式、链接模式、系统模式、运行模式等)再下达命令。没有命令需要你在不同模式间切换。没有可重定位目标模块、链接编辑器以及“真正”计算机的那些琐碎之物。它非常简单,也非常够用。因此,重点放在了计算的核心要素上,而不是与计算机“搏斗”的要素上。
LOGO 语言 [Feurzeig 1975] 在许多方面与 tiny-c 相似。它提供了一种基于 BASIC 的结构化良好的语言,以及一个用于编程和执行的单一环境。LOGO 曾在公立学校对非常年幼的儿童进行实验性教学。实验表明,儿童能够理解简单的计算机概念,完成一套准备好的练习,然后进行自己的创造性工作。
C [Ritchie, Kernighan, & Lesk 1975] 是由贝尔电话实验室的 Dennis Ritchie 设计的一种计算机语言。tiny-c 借鉴了它的整体结构。C 在大学和工业界广泛使用。它被用于编写一个非常先进且强大的计算机操作系统,称为 UNIX [Ritchie & Thompson 1974]。然而,它是一种非常简单的语言。C 没有原生的输入/输出,例如 read 或 print 语句。输入/输出是通过函数完成的。因此 C 专注于计算设施,并允许外部开发或扩展输入/输出。tiny-c 采用了这个想法。
tiny-c 的命令环境是用 tiny-c 编写的。它不需要翻译成微处理器的机器语言。这有点类似于使用 C 作为编程语言来实现 UNIX 的想法。因此,尽管 tiny-c 旨在作为结构化编程的教学语言,但它是一种功能强大的语言。
tiny-c 用户手册试图同时面向四类读者。对于刚接触结构化编程的人,我们提供了一个简短的教程和程序讲解,以便他们掌握要点而不陷入细节。经验丰富的结构化编程用户会发现参考部分能让他们快速了解 tiny-c 的特性。对于那些想了解 tiny-c 解释器工作原理的人,我们描述了它的操作。最后,对于那些想在家用计算机上安装 tiny-c 的人,我们包含了一份完整的安装指南。
## 引言
什么是 tiny-c?tiny-c 是
- 一种语言,加上
- 一个标准库,加上
- 一个程序准备系统。
无需任何其他软件辅助,你就可以准备 tiny-c 程序、运行它们、编辑它们、将它们存储在磁带或软盘上,并在以后读取回来。
tiny-c 是一种结构化编程语言,具有 if-then-else、while 循环、函数、全局和局部变量、字符和整数数据类型、指针以及数组。
tiny-c 独立于操作系统。你可以轻松地将其连接到计算机的输入/输出例程。tiny-c 可以调用你自己的机器语言子程序,因此 tiny-c 编程语言可以适应你的系统,并且你的系统可以反映并扩展该语言。
## 一个 tiny-c 程序讲解
图 1-1 是一个完整的 tiny-c 程序,由两个函数组成。
### 图 1-1
```
/* 猜一个介于 1 到 100 之间的数字 */
/* T. A. Gibson, 11/29/76 */
guessnum [
int guess, number
number = random (1,100)
pl "guess a number between 1 and 100"
pl "type in your guess now"
while (guess != number) [
guess = gn
if (guess == number) pl "right !!"
if (guess > number) pl "too high"
if (guess < number) pl "too low"
pl"" ; pl""
] /* 游戏循环结束 */
] /* 程序结束 */
/* random - 生成一个随机数 */
int seed, last /* random 使用的全局变量 */
random int little, big [
int range
if (seed == 0) seed = last = 99
range = big - little + 1
last = last * seed
if (last < 0) last = -last
return little + (last/8) % range
]
```
### 图 1-1 结束
这个程序是如何工作的?让我们进行一个程序讲解:
从顶部开始,前两行是注释。注释以 `/*` 开头,到行尾结束。
“`guessnum`”是一个函数的名称,程序启动时会调用它。`guessnum` 后面是一个复合语句,长度为 12 行,最后一行是:
```
] /* 程序结束 */
```
复合语句是左右平衡括号之间的所有内容。
`guessnum` 中的第一个简单语句是:
```
int guess, number
```
这声明了两个整数变量,名为 `guess` 和 `number`。tiny-c 中的所有变量都必须声明。当执行时,`int` 语句将创建这些变量,并赋予它们初始值零。
`guessnum` 中的第二个简单语句是:
```
number = random (1,100)
```
这将 `number` 设置为执行 tiny-c 程序函数 `random` 的值,该函数的第一个参数等于 1,第二个参数等于 100。在我们的程序中,函数 random 返回一个介于 1 和 100 之间的随机数。
下一行中,`pl` 是一个 tiny-c 库函数,用于打印一行。它打印作为其参数的带引号字符串。
`while` 设置一个循环。`while` 的一般形式是:
```
while (expression) statement
```
在这个实例中,表达式部分是:
```
guess != number
```
其中 `!=` 表示不等于。该表达式被求值,如果为真,则执行语句,然后再次求值表达式。如果为假,则跳过语句。
最初,`guess` 为 0,而 `number` 不能小于 1,因此表达式最初为真。因此执行语句。
该语句是复合的,由六个简单语句组成。第一个语句是:
```
guess = gn
```
`gn` 代表“get number”,是另一个标准库函数。它读取用户在终端上输入的一个数字,并返回该值。因此,程序在此等待,直到用户输入一个数字并按下回车键,然后 `guess` 被赋予输入的数字。
接下来的三个简单语句是 `if` 语句。此处使用的 `if` 语句的一般形式是:
```
if (expression) statement
```
其中,如果表达式为真,则执行语句。
`while` 复合语句中的第五和第六个语句是 `pl""`。`pl""` 换行,但不打印任何内容。分号允许你在同一程序行上编写多个简单语句。因此:
```
pl"" ; pl""
```
打印两个空行。
现在,我们到达了 `while` 循环的末尾。由于 `while` 的表达式部分为真,`while` 语句再次执行。这始于另一次表达式求值,以查看其为真或假。
如果第一次猜测不等于 `number`,则再次执行复合语句。读取另一个猜测,给出相应的提示,并再打印两个空行;`while` 再次执行。
最终,用户猜对了数字,`guess` 等于 `number`。这将打印“right !!”和两个空行。然后再次测试 while 条件。表达式 `guess != number` 被求值并发现为假,因此整个 `while` 的复合语句被跳过,这使我们到达 `guessnum` 的末尾。游戏结束。程序停止,因为到达了 `guessnum` 的结尾(最后一个 `]`)。
在我们讲解 `random` 之前,请注意整数 `seed` 和 `last` 是在 `guessnum` 和 `random` 之外声明的。它们被称为全局变量。它们将在程序启动时被创建一次。初始值为零,并且 `guessnum` 和 `random` 都可以知道并使用它们。
另一方面,`guess`、`number` 和 `range` 是局部变量。`guess` 和 `number` 仅在 `guessnum` 内知道并使用,而 `range` 是 `random` 的局部变量。
`random` 的第一行给出了函数名。并且在 `[` 之前,它声明了两个整数参数,`little` 和 `big`。当函数被调用时,必须为每个参数提供一个值。`guessnum` 第六行中的调用将 `little` 设置为 1,将 `big` 设置为 100。
现在,我们进入函数 `random` 的函数体。`range` 被声明为整数,初始值为零。
第一次调用时,`seed` 为零。现在 `seed` 和 `last` 都被设置为 99。`range` 被计算为 100。`last` 被设置为 `last` 和 `seed` 的乘积,即 9801。这不小于 0,因此不执行 `if` 的语句部分。
接下来,我们遇到 `return` 语句。它做两件事。首先,它计算表达式。结果成为函数的值。其次,它将控制权返回给调用该函数的程序。
tiny-c 表达式类似于代数表达式。符号 `+` 表示加,`/` 表示除,`-` 表示减(或取负)。表示乘法时使用 `*`。一个不寻常的符号是 `%`,它表示用左边除以右边并取余数(不是商)。例如:
```
1225 % 100
```
是 25。
因此,return 语句计算表达式:
```
little + (last/8) % range = 1 + (9801/8) remainder 100 = 1 + 1225 remainder 100 = 1 + 25 = 26
```
值 26 作为函数 `random` 的值返回。它还在 `last` 中保留了 9801,在 `seed` 中保留了 99。由于这些是全局变量,它们的值在函数调用之间保留。这对于像 `range` 这样的局部变量不成立。它们的值仅在其定义的函数执行期间保留。当离开该函数时,它们的值会丢失。
第二次调用 `random` 时,`range` 被重新创建,并重新初始化为零。`seed` 不为零,因此 `seed` 和 `last` 不会设置为 99,而是分别保持为 99 和 9801。`range` 被重新计算为 100。然后:
```
last = last * seed = 9801 * 99 = 970299
```
这个数对于 tiny-c 来说太大了。任何计算机对可计算的数字大小都有限制。tiny-c 数字必须在范围:
```
-32768 <= number <= 32767
```
`last` 溢出此范围。它将被赋值为 -12741!(我们在第 2.11 节更完整地解释这一点。)这小于 0,因此下一个语句将 `last` 赋值为 12741。然后 return 语句计算:
```
1 + (12741/8) remainder 100 = 1 + 1592 remainder 100 = 93
```
这作为 `random` 的第二个值返回。
### 讲解回顾
讲解的目的是让你感受 tiny-c 编程。我们已经看到:
- 一个 tiny-c 程序是一组函数。
- 有些函数是标准库函数,如 `gn` 和 `pl`。
- 全局变量持续存在并保持其值。局部变量来去匆匆。
- 函数和变量名可以任意长。
- 一组用括号括起来的语句构成一个复合语句,可以像简单语句一样处理。
## 结构化编程——tiny-c 的核心
也许你听说过结构化编程被描述为“无 goto”编程。或者只是使用 `if-then-else` 和 `do-while` 控制语句的编程。这种说法过于简化了结构化编程的本质。结构化编程的精髓在于程序清晰度。你可以将程序编写成小的模块化部分,具有易于遵循的程序流程。你可以使用精心选择的、描述性的变量名。这带来了清晰、可理解的程序。程序清晰度就是结构化编程的全部意义所在。
我们这里讨论使程序清晰度成为可能的四个主要思想。它们是:模块化、可预测的程序流程、局部变量以及有意义变量名的简单想法。
软件中的模块化与硬件中的模块化同样重要。它使人类有可能处理复杂性。一个模块是用于构建更大模块的砖块或原子。从内部看,一个模块可能非常复杂,但从外部看,它是一个不可分割的整体。软件模块化是通过使用函数实现的。
如果你能指向任何语句并轻松回答“在什么条件下执行此语句?”,那么程序流程是可预测的。当程序有 20 或 30 页长并且仍然有错误时,这一点尤其重要。扫描整个程序并画箭头是不可行的。这不被视为回答该问题的简单方法。可预测的程序流程可以通过多种方式实现。在 tiny-c 中,它是通过复合语句完成的。
### 复合语句
函数还使得隐藏仅在局部上下文中使用的变量成为可能。变量 n 非常流行;它经常用于计数。你是否曾经因为在一个程序的两个不同地方用 n 做两件事而导致程序崩溃?解决方法是将其中一个改为 n1。更好的想法是局部和全局变量的概念。
至于为变量和函数起长而有意义的名字——只需看看示例程序就能看到改进。David Gries 建议将结构化编程称为“简单性理论”,并将其描述为“理解整个编程过程的一种方法”[Gries 1974]。作为令人愉快的额外收获,结构化编程比单一编程更愉快。因此,它当然应该成为个人计算的一部分。
为了开始将 tiny-c 视为一种结构化编程语言,让我们看看函数和可预测程序流程的基础——复合语句。当你编写一个程序时,你编写一个语句列表:
```
x = x-1
a = b+c
b = b*2-c
x = b-a
```
复合语句背后的想法是从一组语句(一些原子)中创建一个语句(一个分子)。在 tiny-c 中,这通过以下方式完成:
```
[x = x-1
a = b+c
b = b*2-c
x = b-a]
```
在任何可以编写简单语句的地方,你也可以编写复合语句。这听起来简单,但效果却很强大。例如,大多数编程语言都有一个类似于这样的 if 语句:
```
if (logical expression) statement
```
因此,你可以编写:
```
if (x>0) x = x-1
```
但是,如果将语句部分变成复合,你就有了这种能力:
```
if (x>0) [
b = b*2-c
a = b+c
x = x-1
]
```
这个多行 `if` 并不是某种特殊的 `if`。它仍然是:
```
if (logical expression) statement
```
但是语句部分是复合的。复合语句被视为一个不可分割的单元。它要么全部执行,要么全部不执行,取决于逻辑表达式的值。
复合语句也是循环的自然选择。各种编程语言在如何编写循环方面有很大差异,但它们都有一个共同点。一个循环有开始和结束。复合语句可以用来表达这一点。
循环语句是:
```
while (logical expression) statement
```
注意与 `if` 的相似性。只有关键字改变了。以下是 `while` 的工作原理。
相似文章
Show HN: Tiny – 一种带有内联Go原生函数的动态解释型语言
Tiny是一种用Go编写的新的动态解释型编程语言,具有字节码虚拟机、JIT编译和内联Go原生函数,以实现高性能。
迷你可执行文件再探
本文重新审视了在 Linux 上创建极小 ELF 可执行文件的技术,探讨如何通过滥用头部字段和重叠结构将大小缩减至 45 字节,同时保持与 ELF 规范的兼容性。
MicroUI – 一个用ANSI C编写的小巧、可移植的即时模式UI库
MicroUI是一个用ANSI C编写的最小化、可移植的即时模式UI库,在固定的内存占用下提供窗口、按钮、滑块和文本框等基本控件。
Tiny-Lua-Compiler: 可能是有史以来最小的 Lua 编译器
Tiny-Lua-Compiler 是一个用于教学的、自举的 Lua 5.1 编译器和虚拟机,完全用纯 Lua 编写。其设计目标是体积足够小以便于研究,同时又功能完备到足以处理真实的语言特性。
C3 0.8.2:小幅改进
C3 0.8.2 是 C3 编程语言的一个小幅更新,增加了可复用的库目标模板、泛型反射、位结构属性、新的 @param 选项,并修复了多个 bug。