Unity-剑英陪你零基础学c#系列(三)计算与类型
在这个中秋佳节,先祝大家节日快乐!不过咱们还是言归正传,今天来深入探索一下C#里那些奇特的计算方式和数据类型。
奇怪的计算
回想我刚踏入计算机编程领域时,一切在我眼中都显得那么陌生和奇特。就像下面这几个表达式:
i++;
i = i + 1;
i == i + 1;
乍一看,是不是让人一头雾水?这都是些什么呀!
要理解这些,我们得先了解一下编程语言的背景知识。机器语言基于图灵机模式,它主要是关于操作寄存器的,并非人类日常使用的语言。这里给大家附上一个图灵机的测试版视频地址:图灵机视频。
高级语言试图模拟人类语言,但上面这些表达式用自然语言去理解却困难重重。因为在编程语言中,很多符号和我们数学中熟悉的符号含义并不相同。比如这里的等号,它和数学中的等号毫无关系,而是替代自然语言的一种简化符号。实际上,编程语言中出现的所有符号,本质上都是在表达一句话。
下面我们来具体分析这几个表达式:
i++:它想表达的是让变量i的值增加1。i = i + 1:意思是让变量i的值改变为i + 1。对于初学者来说,这里可能会有些纠结。其实,这涉及到代码执行的顺序问题。根据“代码是棵树,执行有顺序”的原则,它相当于先拿一个临时变量j = i + 1,然后再让i = j。所以,这里并不是时空观有什么奇怪之处,而是这句话隐含了一个中间出现临时变量的机制。初学者在这里绕绕圈子很正常,多思考思考就会明白。i == i + 1:这是一个逻辑运算,完整的写法应该是bool b = i == i + 1;,它想表达的自然语言是:判断i是否与i + 1相等,并将结果(是或否)赋值给变量b。
在高级语言中,所有的语句都在表达一句话。这里的=符号,代表的是“让谁的值改变为xxx”,而不是数学中的等号。
对于加减乘除与数值类型,它们和数学意义是一致的,就不用特别说明了。接下来我们再看看逻辑运算。逻辑运算得到的结果只有“是”或“否”,在C#中,bool类型专门用来表示这种“是”或“否”的结果,而int类型则专门表示整数。
逻辑类型最常用的场合是配合if语句做选择逻辑。比如if(i > 2),它隐藏了一些细节,完整的写法应该是:
bool b = i > 2;
if(b)
{
// 代码块
}
C#将所有需要逻辑表达式的场合,统一为使用bool类型的参数。也就是说,在C#中所有使用逻辑运算的场合,都可以像上面那样展开写一个明确的bool变量。当你对这些逻辑运算非常熟悉,不会出错时,就可以不用这么展开写了。但如果你在写代码时被逻辑运算绕住了,不妨把它展开,这样更容易理解。
只要你始终牢记所有的运算符都是在表达一句话,就不会陷入这些陷阱中。实际上,不止是运算符,高级语言就像一棵大树,每一句代码都在表达一句人类的话,只是经过了很多简化,不太容易理解。
另外,C#中还有一类特殊的计算——位运算,它属于二进制的范畴。在对逻辑清晰和剩余内存有要求的当下,位运算的出镜率已经非常低了,所以我们这里就不详细介绍了。
奇怪的类型
在讨论类型时,很多人会立刻想到封装、继承、多态以及面向对象等概念。但我们现在还没到那个阶段,先不要去考虑面向对象的问题。面向对象讲的是像“猫继承自动物,狗继承自动物,波斯猫继承自猫,短毛猫继承自猫”这样的关系,但我们现在暂时不用关注这些。
我们来思考一个问题:为什么在C#里不能直接写i = 1,而要写int i = 1呢?为什么所有新的变量前面都要有类型,如int、float、string、bool等等?
其实,不写类型也是可以的,像basic语言就不用区分变量类型,这些事情交给运行时(runtime)去处理。这在程序圈里一直是两派争论的焦点。静态类型语言最初的设计是为了贴近机器语言,因为机器语言严格区分类型,int只能和int计算,float只能和float计算,机器语言主要只有整数和有小数点的数这两种类型。但随着发展,静态类型定义和动态类型识别已经成为了两种设计原则与方法论,和机器语言的关系已经不大了。
目前,静态类型定义语言的主要好处是语义明确,很多类型相关的错误在编译阶段就会暴露出来,不需要等到程序运行时才发现。而动态类型识别语言则强调简洁,写代码时更接近人类语言,因为人类说话时不会带着“类型”。比如人类不会说“让(整数)i的值改变为( 整数)2 加( 整数)3”,但机器语言会区分整数运算和浮点运算,就像CPU跑分的时候会分别测试整数运算能力和浮点运算能力。
总结一下,静态类型定义语言最初是因为编译器的“偷懒”,现在则是为了更明确地表达代码含义,让编译器更早地帮你发现问题。当然,代价就是你在写代码时需要明确指定变量的类型。
不过,C#也给了我们一个变通的方式来“偷懒”。可以使用var关键字,例如var i = 2 + 3;,它的意思是让变量i的值等于整数2加整数3,并让编译器帮我们确定i的类型。var只能用于编译器可以根据右边的表达式推断出类型的情况。虽然这种情况很常见,但有时候也会带来一些困扰,所以不建议在学习初期大量使用var,还是要多熟悉各种类型。
好了,中秋节就不多说了,下次我们再聊函数和封装的内容。
作者:李剑英 泰斗原文:点击查看