11.2 复杂数据类型的构造和解读
11.2.1 数据类型的构造方法
本小节以前一小节代码中出现的指向函数指针的例子来讨论复杂数据类型的构造方法。为此,首先对一些基础知识进行简要回顾。
1.数组的定义方法
定义一个数组,应该从数组的名字这个标识符开始。假设数组的名字为“array”,首先写出这个名字。
既然是数组,那么这个标识符后面一定跟“[]”这个特定的类型说明符。
接着,需要说明这个数组有几个元素(1),假定是“6”,那么就需要在“[]”内添上“6”。
最后还需要向编译器说明这个数组的各个元素的类型。如果是简单的类型(那种凡是在前面写上类型名字就可以定义变量的类型),把类型名写在前面,比如各个元素都是“double”类型,那么在前面写“double”,再添上变量定义的结束标志“;”,这个数组名的标识符的含义就说明完了。
从形式看,这种定义仿佛是在说,“array”与“6”进行“[]”运算就会得到一个“double”类型的值。C语言的所有变量定义都可以如此去“理解”。
如果这个数组的元素是指针,比如说“int ”类型(这也是一个简单类型),那么按照定义“int ”类型变量的方法说明“array[6]”就可以了,亦即:
如果这个数组的各个元素的类型不是简单的类型,比如说是数组,那么按照说明数组的方法继续说明“array[6]”是数组。
由于是数组,所以后面必然跟“[]”,即:
多数情况下,数组必须说明有几个元素,这里假定是7个,那么需要在“[]”内写上“7”。
这个数组同样需要说明其元素是什么类型,这里假定是“float ”,那么只要像定义“float ”类型普通变量一样,把“array[6][7]”放在“float *”后面,再加上变量定义的结束标志“;”就可以了。最后得到:
也就是说,只要把“array[6][7]”按照定义普通变量的方法来描述就可以了。
再来看前一小节中定义的指向函数类型指针所构成的数组。首先为数组取名:
由于是数组,
该数组有两个元素,
由于数组元素的类型是指向函数的指针,定义这种类型指针变量的方法是“int (*p)(int , int );”,所以只要按照这个方式说明“ys[2]”就可以了。最后得到:
好像没什么太难的吧?核心在于按照定义数组的方式进行,首先书写数组名这个标识符,再在后面加“[]”,再说明有几个元素,最后说明“数组名[元素个数]”是什么类型就可以了。
秘诀在于不要按照从前到后的方式写,而是按照数组类型的构造规律的次序写,最后说明数组元素的类型。顺便说一句,那种从前到后写代码的方式从来都是很蠢的代码编写方式,包括按顺序写字符串字面量,先写“{”再写“{}”之内的内容最后写“}”……定义变量时也是如此。
此外要注意,函数类型不属于数据对象的范畴,数组的元素不可能是函数(但指向函数的指针是可以的)。
2.函数的原型或声明
函数原型只不过是对函数名这个标识符的数据类型的描述,这听起来可能让人有些惊讶,然而这是事实,只不过是鲜有所闻的一个事实罢了。
那么函数定义是什么?函数定义的本质是对“()”这个运算符的运算规则所做的定义。每一个“()”运算的规则都不一样,具体的函数名进行“()”运算时都有自己的运算规则。这个具体的运算规则的说明就是函数定义完成的任务。这就是函数定义的本质。
由于函数定义的头部比函数原型仅仅多了函数形参的名字,所以这里只讨论函数原型。
写函数原型的要点在于正确地写出该函数各个形参的数据类型以及函数返回值的数据类型,难点在于函数返回值的类型的写法。所以,在下面讨论中不涉及函数形参的类型,形参数据类型部分用“~”记号表示。
写函数原型首先同样应该先写出函数名,例如:
因为是“fun”是函数名,所以在后面要加“()”来说明:
又假设函数形参的类型已经正确写出:
这样剩下的问题就只有函数返回值的数据类型的描述了。假设函数返回的是一个简单数据类型,那么在函数前面写上函数返回值的类型就可以了。例如,如果这个函数返回的是“char *”类型的数据,那么函数的原型便是:
就仿佛用“char ”定义这种类型的变量去说明“fun(~)”。如果返回值的类型是复杂的类型,也按此进行,亦即按照定义变量那样去说明“fun(~)”。比如返回值为一个“int ()[32]”类型的数据,由于定义这种变量的方法是“int(*p)[32]”,所以简单地把“fun(~)”替换其中的“p”就可以了。函数原型为:
这个函数原型说明的意义就是“fun”是一个函数,函数的形参类型为“~”,函数返回值的类型是指针,这个指针指向一个数组,数组有32个元素,每个元素都是“int”类型。
此外需要注意的是,函数不可以返回数组(但可以返回指针),函数也不可以返回函数(但可以返回指向函数的指针)。这虽然是前面讲述过的常识,但在此值得再次重申。
3.指针的定义
首先,让我们从最简单的指针类型开始。假如要定义一个“int *”类型的指针变量“p”,众所周知,应该是:
但实际上这只是一种简写的方式。比较学究气但却很一本正经的方式应该是:
可能标没有见过这种定义方式(2),然而它是合法的。这样符合C语言秘而不宣的哲学(稍后我会揭露这种哲学)。
由于在变量定义“int (p);”中“()”没有什么特别显著的意义(除了告诉你“p”是一个指针),所以早期的程序员(3)喜欢把它写做“int p”,这非常符合C语言追求简洁——甚至是追求“至简”的风格。然而这对省略的“()”却让后来的人感到C语言指针非常难懂,至今思之,仍令人不胜唏嘘。
“int (p);”的隐秘意义在于,“p”这个标识符进行“”运算得到的是一个“int”。事实上C语言所有的变量的定义都是建立在这样的世界观的基础上的。
“int (i);”(4)说的是“i”就是一个“int”。
“long (al[5]);”说的是“al”与“5”做“[]”运算的结果是“long”类型。
“float ((f[2]) [3])”说的是“f”做两次“[]”运算得到的是一个“float”。
而“void (f (int));”说的是“f”做“()”运算(当然还要带上那个“int”类型的参数)得到的是“void”类型的值。
这就是C语言朴素而隐秘的哲学:在定义一个数据标识符含义的时候,直接披露了这个标识符可以进行何种运算以及进行运算之后得到的是什么。而加上“()”能让我们更清楚地看出这种含义。
大概因为这种哲学太朴素,“土得直掉渣”,所以C语言从来没公开地宣布过它的哲学。
现在,披着“()”这件“马甲”,定义指针就没那么难了。比如,定义一个指向一个由4个int类型元素组成的数组的指针,可以首先写出变量名:
由于它是一个指针,所以可以进行“*”运算,此外再给它披上“马甲”:
又由于它指向的是数组,也就是说“( *p )”是数组,所以后面的过程就和数组定义方法一致了。由于数组总是跟着“[]”的:
下面需要回答的是数组有几个元素,
最后回答数组元素是什么类型,
定义完毕。
此法百发百中,屡试不爽,然不见诸经典,可谓独家秘籍。为了证实其有效性,请容许我再举一个极其复杂的例子进行测试。
试定义一指针,该指针指向一个有两个类型分别为“float ()[2][3]”和“int ( )[4]”类型参数的、返回值为“double ( * ) [32]”类型指针的函数。
首先“(*p)”是常规动作,不再细述。
其次,“(*p)”显然是函数(因为该指针指向函数),故:
函数有两个参数,分别为“float ()[2][3]”和“int ( )[4]”类型,因而:
尚差函数返回值类型有待描述。由于返回值是指针(别忘记“马甲”):
这个指针指向数组,故而“((p)( float ()[2][3] , int ( )[4] ) )”是数组,下面按照数组的构造方法如法“炮制”:
估计如此复杂的变量定义,即使是C语言专家,也能给唬得一楞一楞的。
结论:在说一个东西是指针的时候,在它前面加上“”,此外还要给它穿上“()”这个“马甲”。不要让它“赤裸裸”地面对它经过“”运算得到的那个东西的类型。
那么,为什么在定义数组和写函数原型时,“数组名”和“函数名”这两种标识符不必穿“马甲”?其实穿也可以,只是不必要,因为“[]”、“()”运算的优先级最高(“*”要低一级),所以不需要穿“马甲”。
4.向洋葱同志学习
洋葱的形态很有趣。它有一个核心,从里到外,一层一层地长得很有条理,长得很从容不迫。
复杂的构造数据类型的定义也是如此。它的核心是被定义的变量标识符。在说明其类型的时候,也是从里到外一层一层地说明。向洋葱同志学习,就是要学习它从里到外、一层一层地的很有条理、从容不迫的生长方式。
为了将这个过程进行的有条理、从容不迫,建议初学者每说明一层就加上一对像洋葱皮那样的“()”。尽管有时这不是必要的,但为了有条理,多加几对“()”是值得的,稍微麻烦一点与构造错了相比,自是天壤之别。待以后熟练时,有些不必要的“洋葱皮”就可以不加了。
此外要熟练掌握数组、指针类型和函数原型的构造方法。注意,次序问题很重要。
对于指针,次序如下。
指针变量名→(指针变量名)→(指向的数据的类型(指针变量名))
然后把这样得到的东西视为“所指向的数据的类型”性质的东西继续说明。
对于函数原型,次序如下。
函数名→(函数名())→(函数名(各个形参的类型描述))→(返回值的类型 函数名(各个形参的类型描述))
这样得到的东西视为“函数返回值类型”性质的东西继续说明。
对于数组,次序如下。
数组名→(数组名[])→(数组名[元素个数])→(元素类型 数组名[元素个数])
得到的结果可以作为元素类型的东西继续描述。
每次的最后都可能转为对另一种类型的说明,直到最后会成为一种对简单数据类型的说明。C语言就是这样一环接一环、一层包一层地构造数据类型的。
此外,有些道理很浅显,本不足以再提,但对初学者来说,难免犯糊涂,所以还是再强调一下:函数不可以返回函数,但可以返回指向函数的指针;函数不可以返回数组,但可以返回指向数组的指针或指向数组元素的指针(请注意体会两者含义的差别);数组元素的类型不可能是函数类型,但可以是指向函数的指针类型(但你必须保证这些元素类型的一致性)。
最后,强烈建议初学者把从本章开头一直到这里的内容再读一遍。否则本书不保证你能彻底地领会、掌握C语言数据类型的构造的知识和技能,这是C语言精华中的精华。
11.2.2 复杂数据类型的解读
1.对变量定义或函数原型的解读
比构造复杂数据类型更难的似乎是对复杂数据类型的解读,至少我这样认为。
难点有以下两点。
1.一开始很难弄清到底在定义或说明什么。
2.有时候很难弄清到底在定义谁,因为有些程序员在写函数原型的时候加上形参的标识符,这样在一个变量定义中就会有多个标识符。
先从简单的例子开始,以下几个例子出自《The C Progranmming Language(第二版)》。
这个定义中只有一个非关键字的标识符“f”,这就排除了第二个难点。由于紧邻“f”的类型说明符只有前面的“”和后面的“()”,所以很显然“f”是函数(没穿“马甲”)。不知道大师因为什么缘故省略了对这个函数参数的说明,所以可以直接剖析函数的返回值是什么。由于“int (f())”中“f()”前面有一个“”类型说明符,所以返回值是指针,最后需要回答的是这个指针指向什么样类型的数据,显然从“int( (f()) )”中可以看出,这个作为返回值的指针所指向的是“int”类型的数据。
需要说明的是,大师的这个例子中没有写函数参数的类型,可能是因为觉得这并非是这里的主要问题吧。尽管如此,初学者必须明白,不写函数参数的数据类型并非一种严谨的代码风格,其后果是使编译器忽视某些类型检查,这样一旦代码有错误,那么这个错误可能会被编译器放过,直到代码运行时才可能暴露出来。所以后面的几个例子都做了些小小的改动。下面再来看另一个例子。
显然,如果注意到了“pf”外面的“()”就能判断出“pf”是一个指针,这个指针指向函数(因为“(pf)”右面的“()”),这种函数没有参数(“void”),返回值是“int”类型。
尽管“argv”没有与用“()”括起来(括起来也可以,这个变量定义其实等价于“char ((argv));”),但是“argv”后面没有“[]”或“()”,所以“argv”只能是一个指针,这个指针指向的数据也是指针,而且是“char ”类型的指针。
这个“daytab”显然是指针,问题是它所指向的数据的类型,由于去掉“(*daytab)”后定义中剩余的部分是“int [13]”,这显然是一个数组类型,所以“daytab”是一个指向由13个“int”数据构成的一维数组的指针。
显然这个“ daytab”不是指针(没有“马甲”)。因为后面跟“[]”,所以是个数组。“[]”内的数字表示这个数组有13个元素。“daytab[13]”前面的“”表示每个元素都是指针。“daytab[13]”前面的“int”表示这些指针都指向“int”类型的数据。
这个“comp”显然是函数(因为comp右面的“()”的优先级高于comp左面的“”),这个函数没有参数(“()”内的“void”),返回值是指针,且是指向“void”的指针,亦即“void ”类型的指针。
由于“comp”外有“()”,所以这个“comp”是一个指针;“(comp)”后面为“()”,所以是指向函数的指针;“()”内的“void”表明所指向的函数没有参数;而且这个函数也没有返回值。
1 原书中为char ( ( x () ) [] ) ( )。很遗憾,大师的书在这里有个错误,“[]”,内无论如何不能什么都没有。这也许是大师的一个疏忽吧。
“x”后面为“(void)”,显然“x”是函数且没有参数,返回值为指针,该指针指向数组,数组有5个元素,这5个元素也是指针,指向的是一个没有参数且返回值为“char”类型数据的函数。
这个“x”是数组,该数组有3个元素,元素是指针类型,这种指针指向没有参数的函数,其返回值为指针,指向有5个char类型元素的数组。
有些程序员喜欢在描述函数原型的时候写上形参的名字(尽管这基本上没必要),这很讨厌。因为这会导致在复杂类型变量定义时出现多个变量标识符,让人眼花缭乱。比如:
首先要弄清楚的是,这个变量定义是在定义“c”还是“p”。在一个变量定义中,由于出现多个变量标识符一般只在有函数或指向函数的指针数据类型的情况下出现,所以通常变量定义中最左边的未被定义的变量标识符就是被定义、描述的对象。因此前面的变量定义显然是针对“c”的。换一个角度,假设是定义“p”的,会发现这个变量定义是解释不通的。
此外还有一些方法可以帮助我们确定哪些标识符是形参、哪些是被定义的变量(或函数、数组)。那就是如果在某对“()”内,某个标识符被完全地说明了其类型,这个标识符必然不是被定义的变量。
这样,一旦找到了被定义的变量事情就容易了,可以从这个变量出发,逐层地对它解读。由于“c”后面紧跟“[]”,所以显然是一个数组,数组有10个元素,元素的类型是指针,指针指向函数(后面紧跟“()”),函数的参数是“char **”类型,返回值是指针,指向“char”类型的指针。
下面是另外一个例子。
这其实是一个函数原型,是描述说明函数名标识符的类型的,其中出现了3个非关键字的标识符,可以让人看得头晕目眩几近崩溃。不过,按照前面说的从左到右找被说明标识符的原则,很容易发现被说明的是“signal”这个标识符,再从这个标识符出发看下去,一切就迎刃而解了。
首先可以断定“signal”是函数的名字(后面紧接“()”),函数有两个参数,第一个是“int”类型,第二个显然是个指针,该指针指向参数为“int”类型返回值为“void”类型的函数,说到哪里了?哦,说完了“signal”指向的函数的参数,继续,“signal”指向的函数的返回值是一个指针,这个指针也指向函数,这种函数有一个“int”类型的参数返回值为“void”类型。
然而,让人感到惊奇的是,前面几乎用了300多字才用自然语言说清楚的事情,C语言只用了一行44个字符(不算空格)就很精确、很清楚地说明白了!这难道不是C语言的一种魅力吗?
最后,分析一个最复杂的例子。
不难看出,这是关于“f”的定义或说明,由于“f”后面紧跟“[]”,所以“f”是数组,有7个元素,每个元素也都是数组,有8个元素,这8个元素也是数组,有9个元素,每个元素都是指针(5);这种指针指向的是函数,该种函数有两个参数,第一个是指向“struct abc”数据类型的指针,第二个也是一个指针,这种指针指向的是由5×6个元素所构成数组,数组的每一个元素都是指向由7×8个“unsigned”类型数据构成的二维数组的指针;该种函数的返回值是一个指针,指向一个由5个“union u”类型数据所构成的一维数组。
练习
解释下面的变量定义或函数原型。
代码中,除了定义复杂数据类型的变量或写出复杂的函数声明,还可能涉及对复杂数据类型名称本身的描述。这在进行类型转换运算、描述函数形参数据类型和对某些变量(外部变量、函数外部定义的变量将在后面章节介绍)的多次说明时是避免不了的。
对于指针,只要把指针变量定义中的指针变量标识符去掉,所剩下来的就是这个指针的数据类型的名字。比如:
中,“func”的类型的名字是“int ()(int p, int (f)(int))”或“int()(int , int ()(int))”。对于数组,情况有些复杂。比如:
这里“func”显然是一个数组的名字,全面地描述它的类型的名标应该是“int( [5])(int p)”。然而那个“[]”里面的“5”经常可以省略。所以它的类型有时写为“int ( [])(int p)”。后一种是最通用的写法。由于数组名的值(右值)是一个指针,所以在使用数组名右值的场合,数组名的类型也可以写成“int ( ())(int p)”,即把“[]”写成“()”,但这种写法一定是在使用数组名右值的场合下才可以,比如在说明函数参数类型的时候,因为这时如果实参是数组名,使用的是数组名的右值。对于另外一些场合(比如作为sizeof的运算对象),数组名的类型是“int ( [5])(int p)”。
对于函数名来说,由于可以参与的运算只有赋值、类型转换和函数调用,这些情况下使用的都是函数名的右值,因而函数名在运算时的类型永远是指针类型。在描述其类型时,应该把函数名换成“(*)”。比如:
“func”类型的名称是“(((())(int ))[5])(int )”。
11.2.3 添乱的const等类型限定符
对于许多数据类型,由于说明其含义时还可以加上“const”、“volatile”等关键字进行进一步的修饰,所以可以变得更加复杂。比如:
实际上也可以写作:
它们表达的含义是相同的。“const”关键字的位置有时比较灵活,似乎可以当“状语”,也可以当“宾语补足语”。然而在有些情况下却不是这样。如果要进一步修饰指针,那么这个关键字只能出现在“*”的右面。下面3个数据类型中:
前两个是等价的,表示的都是一个指向“const int”数据的指针的类型,而后一个表示一个“const”的指针,这个指针指向“int”数据类型。
所以一般来说,“const”或“volatile”这样的类型限定符所限定的是其左面的类型说明符,如果其左面没有任何类型,那么限定的是右面的类型。
很多人认为类型限定符(qualifiers)是限定变量定义或说明中被说明的标识符的,其实不然。类型限定符是对数据类型本身的进一步限制。它表明这样的数据类型在使用时有额外的限制或特点。比如“const”表示的含义是这种类型不可以被“显式”地改变(例如通过赋值、++、——等运算改变)。
11.2.4 5 5 5 5 5=19
本小节的标题是一道小学三年级的数学课外题,题目要求在各数字之间添上+、-、×、÷使得算式成立。
在本章开始的题目中,介绍了如何穷举“动作”并求出算式的值。然而在那个题目中要求的是+、-运算,不存在优先级的问题。本小节的题目则有一个显著的难点,那就是在求算式值的时候会遇到优先级的问题。那么,如何解决本题目中的优先级问题呢?也就是说,应该如何设计关于优先级的算法呢?
设计算法的前提是,能够用笔在纸上用有限的步骤解决这个问题。所以寻找算法也可以通过考察在纸上的运算过程得到。假设添上的运算分别是+、-、×、÷,在纸上求算式5+5-5×5÷5的运算过程大抵如下。
可以看到,第一个5被照抄。这是因为其后紧跟的是“+”运算。紧接着“+”被照抄。
第二个5也被照抄,因为其后紧跟“-”运算。紧接着“-”被照抄。
第三个5没有被照抄,这是因为其后紧跟“×”运算。这个运算被执行得到了25。这个25可以被看成是被放在了第四个5的位置,随后审视这个位置后紧跟的是何种运算。
由于后面紧跟的是“÷”运算,这个运算也被执行,结果可看成放在了第五个5的位置。这时,由于已经到了算式的结尾,因此把放在第五个位置的运算结果照抄,形成了一个只有运算的新的运算式“5+5-5”。这个算式的计算不难借鉴本章开头的方法计算。这样,运算的优先级问题接可以得到解决。
这只是一个大致的设想,距离问题的解决还有不少差距。比如,从左到右地扫描过去,比较容易实现的方法是通过指针的加法实现。但指针要求所指向的数据构成一个数组。然而本题目中的算式不但有整数还有运算符,如何才能构成数组呢?
本书前面提到过,离开了数据类型,算法只是空中楼阁式的幻想。这个论断在这里再次得到了证实。设计算法必须与数据结构或数据类型的设计同时进行。
C语言的union类型可以把不同类型的数组在形式上统一起来,所以算式的各个项,无论是整数还是运算,都可以用union类型的“外观”使得它们在形式上统一起来。这个union类型可以按照如下方式定义。
算式可以抽象为下面形式的数组。
其中“5-1”为运算符的个数(因为两个整数中填一个运算符号)。
由于对算式的第一次扫描得到另一个算式,所以需要两个这样类型的数据,一个表示可能含有乘除运算的原始算式,另一个表示没有乘除运算的新算式。
设计好数据类型或数据结构之后,再在纸上走一遍。过程如下。
第一步,预备两个算式的存储空间,并在第一个中写入原始算式:
5+5-5×5÷5(原始算式)
□□□□□□□□□(新算式)
第二步,把指针指向原始算式的开头(用下面加着重号表示):
5+5-5×5÷5(原始算式)
□□□□□□□□□(新算式)
由于这不是算式的结尾,所以查看指针所指的后面一项:
5+5-5×5÷5(原始算式)
□□□□□□□□□(新算式)
由于后面一项是“+”,所以在新算式中照抄前两项:
5+5-5×5÷5(原始算式)
5+□□□□□□□(新算式)
原始算式中指向数据的指针移动两个元素的位置:
5+5-5×5÷5(原始算式)
5+□□□□□□□(新算式)
由于这不是算式的结尾,所以查看指针所指的后面一项:
5+5-5×5÷5(原始算式)
5+□□□□□□□(新算式)
由于后面一项是“-”,所以在新算式中照抄两项:
5+5-5×5÷5(原始算式)
5+5-□□□□□(新算式)
原始算式中指向数据的指针移动两个元素的位置:
5+5-5×5÷5(原始算式)
5+5-□□□□□(新算式)
由于这不是算式的结尾,所以查看指针所指的后面一项:
5+5-5×5÷5(原始算式)
5+5-□□□□□(新算式)
由于后面一项是“×”,所以进行计算,结果存放在“×”下一个存储单元中:
5+5-5×25÷5(原始算式)
5+5-□□□□□(新算式)
原始算式中指向数据的指针移动两个元素的位置:
5+5-5×25÷5(原始算式)
5+5-□□□□□(新算式)
由于这不是算式的结尾,所以查看指针所指的后面一项:
5+5-5×25÷5(原始算式)
5+5-□□□□□(新算式)
由于后面一项是“÷”,所以进行计算,结果存放在“÷”下一个存储单元中:
5+5-5×25÷5(原始算式)
5+5-□□□□□(新算式)
由于已经到了算式的结尾,所以把该项照抄至新算式中。至此,新算式求出:
5+5-5×25÷5(原始算式)
5+5-5□□□□(新算式)
从上面的推演过程中还可以发现,在描述算法的时候,始终需要算式结尾的位置。此外在计算所得到的新算式的值的时候,也必须知道其总的项数。为此,把算式定义为:
计算新算式的N-S图如图11-1所示。
图11-1 求新算式过程的N-S图
下面是程序代码。
程序代码11-2
需要说明的是,这段代码是对我们平时计算四则算式的方法的一种自然平实的描述,强调的是复杂数据类型的构造,以及应用这些复杂的数据类型对算法的自然描述,这是这段代码的意义所在。然而无论在程序结构、数据组织还是算法等方面,本代码都有许多可改进的空间。
比如,在main()中用循环的嵌套进行穷举。在需要穷举的“运算”较多的情况下,即使这种穷举的方法有效,太多层的循环嵌套也往往是令人生畏甚至难以忍受,出于效率和美感方面的顾虑,这样的代码往往令专业的程序员不忍心写出手。
在数据组织方面可以看到,所计算出的新的算式有时并不需要那么大的空间,因此可能造成内存空间的浪费。而且本程序代码描述的算式的数据结构并没有一般性,只能描述具有5个整数的算式。试想,如果在程序运行时才能知道算式中运算数据的数目,那么又应该如何解决这个问题呢?
在算法方面,本代码只是描写了日常四则运算的方法,熟悉、简单、易懂是其优点,然而这种算法更适合在纸面上计算,而没有充分考虑到计算机内存的特点。
解决这些问题的方法将在本章其他部分叙述。