2.3.1 一些简单的类型
在本书所提及的平台里,所有的变量都有特定的“类型”。计算机根据变量的类型判断变量里存放的是什么种类的东西。数值、字母(通常称为“字符”)和true/false(真/假,通常称为布尔值)各需要不同大小的空间来存放和移动。
以下是一些最常见的类型,你会在本书涉及的任何一种编程语言中见到它们。
整型(int)
整型用于存放整数,没有小数点,例如2或20 392。下面我们来解决本章的第一个问题:本书用到了3种编程语言,它们包含的东西都差不多,但有时作用却有些不同。在Processing语言中,整数的范围是-2 147 483 648~2 147 483 647。而Arduino语言和openFrameworks(oF)所使用的C++中,情况则有些许不同。要明白为什么会有这个不同,就需要先快速理解一下有符号变量和无符号变量,这点我们在下一小节讨论。这一小节一开始理解起来可能会有些困难,你可能想跳过它,尤其是如果你比较感兴趣的是Processing的话。
有符号(signed)和无符号(unsigned)
如前文所述,需要声明变量的类型,以通知计算机分配多少空间存放该变量。也就是说,一个整型数据是4字节(即32比特)长度的信息,它能容纳4字节的信息量。那么对于负整数又是怎样的状况呢?这时你就要把符号信息也存下来,遇到是负整数,就把原来整数的值按某种方式取反一下。我们以oF要用到的C++为例,其他语言以此类推。在C++里,整型的范围为-2 147 483 647~2 147 483 647。如果你用到的整型里没有负数,你就可以用无符号整型,它的取值范围为0~4 294 967 295。这就是有符号变量和无符号变量之间的区别。无符号变量不能存放负数,而有符号变量可以存放负数。其中的原理要用二进制来解释。
整型是32比特的二进制数。不像我们平常用0到9来记数,二进制用的是1和0。也就是说,你在存储器的那个位置写了个1,值就是1;当你要存放数值2的时候,你就要往存储器里写10,意思是权值为2的数位上是1,权值为1的数位上是0。如果是数值3,那就是11,意思是权值为2的数位上是1,权值为1的数位上也是1, 2+1即为3。接下来,4就是100,意思是权值为4的数位上是1,其他数位为0。同理,16就是1000,17是1001,18是1010,以此类推。这是所谓的二补数,因为每一位的权值是前一位的平方。这里只是对二进制的简单介绍,别着急,在后面的章节里我们会对此详细讨论,并且你还会开始使用比特和二进制。
我们可以从图2-1看到有符号变量和无符号变量是怎样存放和表示的。C++里整型的长度是32比特,所以用32格来表示一个整型数,1格对应1比特。
无符号数的首比特(最高位)跟其他比特一样用来计数,而有符号数的首比特表示的是数值的正或者负,所以无符号变量可表示的最大值比有符号变量大。
Arduino和C++可使用无符号变量,但定义变量时默认其有符号,定义无符号变量的时候要特别声明,即在变量类型前面添加“unsigned”:
unsigned int = 5;
Processing不使用无符号变量,所有数值变量都有符号。
在oF所使用的C++里,有符号整型的范围为-2 147 483 647 ~2 147 483 647,无符号整型的范围为0~4 294 967 295。而在Arduino中,整型的范围为-32 768~32 767。复杂吗?其实不然。大多数情况下,不用考究整型里到底存放了些什么数据。如果要用一个很大的值,可以使用值域更大的数据类型,例如长整型(long)或双精度型(double)。这些稍后我们会讨论到。使用Processing的时候,你一点不用担心数值的大小问题,只需要想想要不要用小数。如果用小数,就用浮点型(float);如果用不到小数,就用整型。
图2-1:有符号变量和无符号变量中的比特
浮点型(float)
浮点型用来表示小数。浮点数比整型数值域更大,可近似地表示连续数值。有符号浮点数的取值范围为-2 147 483 647~2 147 483 647。C++和Arduino里的浮点数是无符号的。
字符型(char)
这种类型用来表示字符,即单个字母或印刷符号,例如A、d和$。下面是两个字符型变量的声明:
char firstLetter = 'a';
char secondLetter = 'b';
那么浮点型、整型和字符型之间有何不同呢?字符型变量有时也可以像数那样相加,但不是你想象中的那种方式。Processing和Arduino里字符型变量不可以相加,而在C++里可以,但不是你想象的那样:
char thirdLetter = 'a' + 'b'; // 相加结果不会是'ab'或者'c'
正如前文所述,Processing和Arduino不允许这样的运算,它们会直接向你报错。而C++允许字符型变量相加,其运算的原理稍后讨论。
布尔型(bool或boolean)
布尔型变量有两个取值:true(真)和false(假)。C++和Arduino里的true值为1,false为0。直接使用true和false很清楚,但如果你喜欢,也可以用1和0。布尔型非常方便用来存储简单的结果:例如你收到了服务器发出的消息还是没有收到,用户按了F键还是没有按。
Arduino和Processing里的代码可以这样写:
boolean b = true;
C++里这样写:
bool b = true;
注意,布尔型在Processing和Arduino里用“boolean”表示,在C++里用“bool”表示。
true和false都是保留字,也就是说,编译器永远只会把它们理解为布尔型变量的值,不会再有其他意义。例如,这样写是不对的:
boolean true = false;
编译器会报错。虽然这样的错误难以避免,但你最好尽量减少这样的错误,而把精力集中在有助于你创作的更有意思的事情上。
字符串(string)
字符串就是一串字符。这样的说法只是打个比方,但确实有助于理解。稍后我们会对此作更仔细的讨论。字符串有若干有用的方法(method),例如查询当中的每个字符,比较字符串之间的异同,查找字符串里的某部分(例如从“toaster”中查找“toast”),提取字符串里的某部分(例如从“toaster”中提取“toast”)。字符串和字符的差别在于,字符串总是包括在双引号里(例如 “Abc”)并可含有多个字符,而字符是包括在单引号里(例如‘A’)并且只含一个字符。
Processing里字符串可以这样表示:
String f = "foo";
String b = "bar";
String fb = f+b; // 相加的结果是 "foobar"
C++里字符串可以这样表示:
string f = "foo";
string b = "bar";
string foobar = f+" "+b;// 结果是"foo bar" ,留意双引号内的空格
我们已经看到了Processing里用String,C++里用String,那么Arduino呢?Arduino不使用字符串,因为它根本不需要。到下一节我们再讨论Arduino的这个问题,反正不是什么大问题。
字符串由双引号括住:
string super = "super";
若字符串本身含有双引号,则要在该双引号之前加反斜杠:
string quoted = "He said "It's super"."
这个反斜杠就是之前提到的 “转义字符”,能把紧接其后的一个字符按某种规定换成其他的意义。使用转义字符的例子还有:t表示tab键,n表示新起一行,\表示反斜杠(若只用一根反斜杠,则编译器会认为是转义字符)。如果你想打出that抯 great!,就应该这样写:
string quoted = "\\that's great!";
否则,缺少了双反斜杠,你得到的将是
" hat's great!"
编译器认为\t是tab。
字节型(byte)
字节型是以字节为单位计算的数据类型,也就是说,8位的信息存为1个数值。Processing的字节型变量取值从-128到127(有符号数),Arduino的是从0到255(无符号数)。当要发送字符串或其他类型无法表示的信息时,就要用到字节型,例如文件中的内容、硬件收发的数据、图像的像素数据或1毫秒的声音数据。
以下是字节型变量在Processing和Arduino里的例子:
byte firstByte = 55;
byte newByte = 10+firstByte;
现在可以随意摆布字节型变量newByte了。如果把它用作字符,就会得到A;如果用作一个整数,就是65。字节型变量使用起来很灵活,经常用于存放有待读写或发送的数据,又或者是那些没有合适数据类型存放的数据。
C++没有字节型。在Processing和Arduino里用字节型存放的数据,在C++里可以用字符型。这是为什么呢?你应该还记得ASCII码用-128~127(或者无符号字符型的0~225)的数字表示字母或字符。C++承继了C语言的这个特点,字符型变量不仅用于存储字符,还可以是任何东西。比起Processing和Arduino, C++使用字符型变量要频繁得多。
长整型(long)
长整型变量用于存储非浮点型的长度较长的整数,跟整型的不同在于取值范围。在C++和Arduino中,长整型取值从-2 147 483 648到2 147 483 647。而在Processing中,长整型的最大值就更大了,为18 446 744 073 709 551 615。