本文节选自《LPC语言基础教程:从零学习游戏开发》,版权归@mudren,欢迎转载,但必须注明来源(mud.ren)。
第三节 LPC语言变量和常量
数据有类型,也有常量和变量之分,下面我们具体介绍。
变量
变量(varible)可以理解成一块内存区域的名字。通过变量名,可以引用这块内存区域,获取里面存储的值。由于值可能发生变化,所以称为变量,否则就是常量了。
在LPC语言中变量的定义和初始化方式和C语言完全相同:
数据类型符 变量名1[,变量名2,...];
数据类型符 变量名1=初值1[,变量名2=初值2,...];
和C语言一样,LPC语言中的局部变量定义必须在可执行语句或代码块的开头。不像其它高级语言可以在哪儿使用在哪儿声明。
下面详细介绍变量相关知识,这些LPC语言和C语言差别不大,这里重点关注和C语言的不同点。
变量名
变量名在 C 语言里面属于标识符(identifier),命名有严格的规范。
- 只能由字母(包括大写和小写)、数字和下划线(
_)组成。 - 不能以数字开头。
- 长度不能超过63个字符。
下面是一些无效变量名的例子。
$zj
j**p
2cat
Hot-tab
tax rate
don't
上面示例中,每一行的变量名都是无效的。
变量名区分大小写,star、Star、STAR都是不同的变量。
并非所有的词都能用作变量名,有些词在 C 语言里面有特殊含义(比如int),另一些词是命令(比如continue),它们都称为关键字,不能用作变量名。另外,C 语言还保留了一些词,供未来使用,这些保留字也不能用作变量名。
在C语言中一共有32个关键字,LPC语言中的关键字和C语言有一些差别,LPC语言中的关键字具体如下(以fluffos为标准):
| LPC语言关键字 | 说明 | 备注 |
|---|---|---|
| __TREE__ | #ifdef DEBUG | |
| array | 声明数组变量或函数 | #ifdef ARRAY_RESERVED_WORD |
| asm | ||
| break | 跳出当前循环 | |
| buffer | 声明缓冲类型变量或函数 | |
| case | 开关语句分支 | |
| catch | ||
| class | 声明结构体变量或函数 | #ifdef STRUCT_CLASS |
| closure | #ifdef COMPAT_32 | |
| continue | 结束当前循环,开始下一轮循环 | |
| default | 开关语句中的“其他”分支 | |
| do | 循环语句的循环体(与 while 连用) | |
| efun | 调用外部函数 | |
| else | 条件语句否定分支(与 if 连用) | |
| float | 声明浮点型变量或函数 | |
| for | 一种循环语句 | |
| foreach | 一种循环语句 | |
| function | 声明函数指针类型变量 | |
| if | 条件语句 | |
| in | 用于 foreach 循环语句 | |
| inherit | 继承对象的方法 | |
| int | 声明整型变量或函数 | |
| mapping | 声明映射类型变量或函数 | |
| mixed | 声明混合类型变量或函数 | |
| new | ||
| nomask | 声明方法类型为不可被重写的(类似其它面向对象语言中的final) | |
| nosave | 声明变量类型为nosave | #ifdef SENSIBLE_MODIFIERS |
| object | 声明对象类型变量或函数 | |
| parse_command | ||
| private | 声明方法类型为私有 | |
| protected | 声明方法类型为被保护的 | |
| public | 声明方法类型为公开的(默认类型) | |
| ref | #ifdef REF_RESERVED_WORD | |
| return | 子程序返回语句(可以带参数,也可不带参数)循环条件 | |
| sscanf | ||
| static | 声明静态变量 | #ifndef SENSIBLE_MODIFIERS |
| string | 声明字符串变量或函数 | |
| struct | 声明结构体变量或函数 | #ifdef STRUCT_STRUCT |
| switch | 用于开关语句 | |
| time_expression | ||
| varargs | 声明函数参数为可变长度 | |
| void | 声明函数无返回值或无参数 | |
| while | 循环语句的循环条件 |
关键字是不可以做变量和函数名的,因为LPC和C的关键字的不同,定义变量和函数时要注意了。
另外,驱动预定义的常量不可以做变量名,如:__DIR__(表示当前目录)、__FILE__(表示当前文件)、__LINE__(表示当前行)、__VERSION__(当前驱动版本)等等。
变量的声明
C 语言的变量,必须先声明后使用。如果一个变量没有声明,就直接使用,会报错。
每个变量都有自己的类型(type)。声明变量时,必须把变量的类型告诉编译器。
int height;
上面代码声明了变量height,并且指定类型为int(整数)。
如果几个变量具有相同类型,可以在同一行声明。
int height, width;
// 等同于
int height;
int width;
注意,声明变量的语句必须以分号结尾。
一旦声明,变量的类型就不能在运行时修改。
变量的赋值
C 语言会在变量声明时,就为它分配内存空间,但是不会清除内存里面原来的值。这导致声明变量以后,变量会是一个随机的值。所以,变量一定要赋值以后才能使用。
赋值操作通过赋值运算符(=)完成。
int num;
num = 42;
上面示例中,第一行声明了一个整数变量num,第二行给这个变量赋值。
变量的值应该与类型一致,不应该赋予不是同一个类型的值,比如num的类型是整数,就不应该赋值为小数。虽然 C 语言会自动转换类型,但是应该避免赋值运算符两侧的类型不一致。
变量的声明和赋值,也可以写在一行。
int num = 42;
多个相同类型变量的赋值,可以写在同一行。
int x = 1, y = 2;
注意,赋值表达式有返回值,等于等号右边的值。
int x, y;
x = 1;
y = (x = 2 * x);
上面代码中,变量y的值就是赋值表达式(x = 2 * x)的返回值2。
由于赋值表达式有返回值,所以 C 语言可以写出多重赋值表达式。
int x, y, z, m, n;
x = y = z = m = n = 3;
上面的代码是合法代码,一次为多个变量赋值。赋值运算符是从右到左执行,所以先为n赋值,然后依次为m、z、y和x赋值。
C 语言有左值(left value)和右值(right value)的概念。左值是可以放在赋值运算符左边的值,一般是变量;右值是可以放在赋值运算符右边的值,一般是一个具体的值。这是为了强调有些值不能放在赋值运算符的左边,比如x = 1是合法的表达式,但是1 = x就会报错。
LPC语言中的不同点
在C语言中整型变量分有符号型、无符号型、基本整型、短整型、长整型,但LPC中只有基本整型变量。
在C语言中可以使用 const 关键字定义有名常量,但LPC语言中,没有这种用法,也没有 const 关键字。
另外,在C语言中有取地址指针运算符 & 获取变量的地址,在LPC中没有,LPC游戏开发不需要和底层互交,没有相关功能。
这里强调一点,LPC语言中任何变量类型如果只定义没有赋初值,默认初值都是0,而且对这种没有初始化的变量做类型判断时返回的都是 int 类型。
变量的作用域
作用域(scope)指的是变量生效的范围。C 语言的变量作用域主要有两种:文件作用域(file scope)和块作用域(block scope)。
文件作用域(file scope)指的是,在源码文件顶层声明的变量,从声明的位置到文件结束都有效。
int x = 1;
int main(void) {
printf("%i\n", x);
}
上面示例中,变量x是在文件顶层声明的,从声明位置开始的整个当前文件都是它的作用域,可以在这个范围的任何地方读取这个变量,比如函数main()内部就可以读取这个变量。这种变量被称为全局变量。
块作用域(block scope)指的是由大括号({})组成的代码块,它形成一个单独的作用域。凡是在块作用域里面声明的变量,只在当前代码块有效,代码块外部不可见。这种变量被称为局部变量。
int a = 12;
if (a == 12) {
int b = 99;
printf("%d %d\n", a, b); // 12 99
}
printf("%d\n", a); // 12
printf("%d\n", b); // 出错
上面例子中,变量b是在if代码块里面声明的,所以对于大括号外面的代码,这个变量是不存在的。
代码块可以嵌套,即代码块内部还有代码块,这时就形成了多层的块作用域。它的规则是:内层代码块可以使用外层声明的变量,但外层不可以使用内层声明的变量。如果内层的变量与外层同名,那么会在当前作用域覆盖外层变量。
{
int i = 10;
{
int i = 20;
printf("%d\n", i); // 20
}
printf("%d\n", i); // 10
}
上面示例中,内层和外层都有一个变量i,每个作用域都会优先使用当前作用域声明的i。在LPC语言中作用域内的局部变量不可以重复声明,这是和C语言不同的。
最常见的块作用域就是函数,函数内部声明的变量,对于函数外部是不可见的。for循环也是一个块作用域,循环变量只对循环体内部可见,外部是不可见的。
for (int i = 0; i < 10; i++)
printf("%d\n", i);
printf("%d\n", i); // 出错
上面示例中,for循环省略了大括号,但依然是一个块作用域,在外部读取循环变量i,编译器就会报错。
LPC语言中局部变量除了不能重复声明外,作用域和C语言完全一至。而全局变量的用法在LPC中比较特别,除了作用域的变化外还有存档相关,具体在后续章节会介绍。
常量
在C语言中整型常量有八进制、十进制、十六进制三种书写形式,而且可以赋值给整型变量。但是LPC中,无法用八进制赋值,具体在第二章第一节的示例 2.1.2.c 中有演示,本人怀疑是LPC的BUG,反正大家别用八进制赋值就对了。另外LPC中整型常量不像C语言中分基本整型、短整型和长整型,只用一种基本整型,取值范围为-263 ~ 263-1。
在C语言中实型(即浮点型)常量有二种书写形式,但在LPC语言中只有十进制小数一种书写形式,取值范围为 ±1.7E-308 和 ±1.7E308 之间。
在C语言中有字符类型变量和常量,但在LPC语言中,只有字符常量,没有字符变量。
在C语言中没有字符串变量,只有字符串常量,但在LPC语言中,既有字符串常量,也有字符串变量。
在LPC语言中的符号常量的用法和C语言一样,使用以下方式定义:
#define 符号常量 常量
在LPC中,驱动预定义了很多常量,具体会在后续第七章宏定义中讲解。