C++程序设计从零开始之表达式


  本篇是此系列的开头,在学英语时,第一 工夫学的是字母,其是英语的 根底 。同样,在C++中,所有的代码都是通过标识符(Identifier)、 抒发式(Expression)和语句(Statement)及一些必要的符号(如大括号等)构成,在此先 注明何谓标识符 。

  标识符

  标识符是一个字母序列,由大小写英文字母、下划线及数字构成,用于标识 。标识便是标出并 鉴别,也便是名字 。其 可以作为后面将提到的变量或者函数或者类等的名字,也便是说用来标识某个特定的变量或者函数或者类等C++中的元素 。

   比方:abc便是一个合法的标识符,即abc 可以作为变量、函数等元素的名字,但并不代表abc便是某个变量或函数的名字,而所谓的合法便是任何一个标识符都必须不能以数字开头,不得不包含大小写英文字母、下划线及数字,不能有其它符号,如,!^等,而且不能与C++ 要害字 雷同 。也便是我们在给一个变量或函数起名字的时候,必须将起的名字看作是一个标识符,并进而必须满足上面提出的要求 。如12ab_C就不是一个合法的标识符, 因此我们不能给某个变量或函数起12ab_C这样的名字;ab_12C便是合法的标识符, 因此 可以被用作变量或函数的名字 。

  前面提到 要害字,在后续的语句及一些申明 润饰符的介绍中将发现,C++提供了一些特别的标识符作为语句的名字,用以标识某一特定语句,如if、while等;或者提供一些 润饰符用以 润饰变量、函数等元素以实现语义或给编译器及衔接器提供一些特定信息以进行优化、查错等操作,如extern、static等 。 因此在命名变量或函数或 其余元素时,不能 使用if、extern等这种C++ 要害字作为名字,不然将招致编译器 无奈确认是一个变量(或函数或其它C++元素)还是一条语句,进而 无奈编译 。

  假如要让某个标识符是特定变量或函数或类的名字,就需求 使用申明,在后续的文章中再具体 注明 。

  数字

  C++作为电脑编程语言,电脑是 解决数字的, 因此C++中的 根底东西便是数字 。C++中提供两种数字:整型数和浮点数,也便是整数和小数 。但由于电脑实际并不是 设想中的数字化的(详情参见《C++从零开始(三)》中的类型一节),所以整型数又分成了有符号和无符号整型数,而浮点数则由精度的区别而分成单精度和双精度浮点数,同样的整型数也依据长度分成长整型和短整型 。

  要在C++代码中 示意一个数字,直接书写数字即可,如:123、34.23、-34.34等 。由于电脑并非以数字为 根底而招致了前面数字的分类,为了在代码中 体现出来,C++提供了一系列的后缀进行 示意,如下:

  u或U 示意数字是无符号整型数,如:123u,但并不 注明是长整型还是短整型

  l或L 示意数字是长整型数,如:123l;而123ul便是无符号长整型数;而34.4l便是长双精度浮点数,等效于双精度浮点数

  i64或I64 示意数字是长长整型数,其是为64位操作系统定义的,长度比长整型数长 。如:43i64

  f或F 示意数字是单精度浮点数,如:12.3f

  e或E 示意数字的次幂,如:34.4e-2便是0.344;0.2544e3f 示意一个单精度浮点数,值为254.4

  当什么后缀都没写时,则依据有无小数点及位数来决定其具体类型,如:123 示意的是有符号整型数,而12341434则是有符号长整型数;而34.43 示意双精度浮点数 。

  为何要搞这么多事出来,还分什么有符号无符号之类的?这全是由于电脑并非基于数字的,而是基于状态的,详情在下篇中将 详尽 注明 。

  作为科学计算,可能 时常会碰到 使用非十进制数字,如16进制、8进制等,C++也为此提供了一些前缀以进行 支撑 。

  在数字前面外加0x或0X 示意这个数字是16进制 示意的,如:0xF3Fa、0x11cF 。而在前面加一个0则 示意这个数字是用8进制 示意的,如: 0347,变为十进制数就为231 。但16进制和8进制都不能用于 示意浮点数,不得不 示意整型数,即0x34.343是 舛误的 。

  [
字符串

  C++除了提供数字这种最 根底的 示意 模式外,还提供了字符及字符串 。这 彻底只是出于容易编写程序而提供的,C++作为电脑语言, 根本没有提供字符串的必要性 。不过由于人对电脑的 根本要求便是显示 后果,而字符和字符串都由于是人易读的符号而被用于显示 后果,所以C++专门提供了对字符串的 支撑 。

  前面说过,电脑只 意识数字,而字符便是文字符号,是一种图形符号 。为了使电脑 可以 解决符号,必须通过某种 模式将符号变成数字,在电脑中这通过在符号和数字中间 构建一个映射来实现,也便是一个表格 。表格有两列,一列便是我们欲显示的图形符号,而另一列便是一个数字,通过这么一张表就 可以在图形符号和数字中间 构建映射 。现在已经定义出一 标准表,称为ASCII码表, 几乎所有的电脑硬件都 支撑这个转换表以将数字变成符号进而显示计算 后果 。

  有了上面的表,当想 注明 后果为“A”时,就查ASCII码表,得到“A”这个图形符号对应的数字是65, 而后就告诉电脑输出序号为65的字符,最终屏幕上显示“A” 。

  这显而易见地 冗长得 异样,为此C++就提供了字符和字符串 。当我们想得到某一个图形符号的ASCII码表的序号时, 惟独通过单引号将那个字符括起来即可,如:’A’,其 动机和65是一样的 。当要 使用不止一个字符时,则用双引号将多个字符括起来,也便是所谓的字符串了,如:"ABC" 。 因此字符串便是多个字符连起来而已 。但依据前面的 注明易发现,字符串也需求映射成数字,但它的映射就不像字符那么 容易 可以通过查表就搞定的,关于此,将在后续文章中对数组作过介绍后再 注明 。

  操作符

  电脑的 根本是数字,那么电脑的所有操作都是转变数字, 因此很 畸形地C++提供了操作数字的一些 根本操作,称作操作符(Operator),如:+ - * / 等 。任何操作符都要返回一个数字,称为操作符的返回值, 因此操作符便是操作数字并返回数字的符号 。作为普通性地分类,按操作符同时作用的数字个数分为一元、二元和三元操作符 。

  一元操作符有:

  +   其后接数字, 一成不变地返回后接的数字 。如: +4.4f的返回值是4.4;+-Array.3f的返回值是-Array.3 。 彻底是出于语义的需求,如 示意此数为正数 。

  -   其后接数字,将后接的数字的符号取反 。如: -34.4f的返回值是-34.4;-(-54)的返回值是54 。用于 示意负数 。

  !   其后接数字,逻辑取反后接的数字 。逻辑值便是“真”或“假”,为了用数字 示意逻辑值,在 C++中规定,非零值即为逻辑真,而零则为逻辑假 。 因此3、43.4、’A’都 示意逻辑真,而0则 示意逻辑假 。逻辑值被 利用于后续的推断及循环语句中 。而逻辑取反便是先推断“!”后面接的数字是逻辑真还是逻辑假, 而后再将相应值取反 。如:

  !5的返回值是0,由于先由5非零而知是逻辑真, 而后取反得逻辑假,故最终返回0 。

  !!345.4的返回值是1,先因345.4非零得逻辑真,取反后得逻辑假,再取反得逻辑真 。 固然 惟独非零便是逻辑真,但作为编译器返回的逻辑真,其一概 使用1来代表逻辑真 。

  '   其后接数字,取反后接的数字 。取反是逻辑中定义的操作,不能 利用于数字 。为了对数字 利用取反操作,电脑中将数字用二进制 示意, 而后对数字的每一位进行取反操作(由于二进制数的每一位都不得不为1或0,正好 相符逻辑的真和假) 。如'123的返回值就为-124 。先将123 转成二进制数01111011, 而后各位取反得10000100,最终得-124 。

  这里的问题便是为何是8位而不是16位二进制数 。由于123小于128,被定位为char类型,故为8位(关于char是什么将下篇介绍) 。假如是'123ul,则返回值为42Array4Array67172 。

  为何要有数字取反这个操作?由于CPU提供了这样的指令 。而且其还有着很不错且很主要的 利用,后面将介绍 。

  关于 其余的一元操作符将在后续文章中陆续提到(但不 定然所有提到) 。

  二元操作符有:

  + - * / %

  其前后各接一数字,返回两数字之和、差、积、商、余数 。如:

  34+4.4f的返回值是38.4;3+-Array.3f的返回值是-6.3 。

  34-4的返回值是30;5-234的返回值是-22Array 。

  3*2的返回值是6;10/3的返回值是3 。

  10%3的返回值是1;20%7的返回值是6 。

  &&

  || 其前后各接一逻辑值,返回两逻辑值之“与”运算逻辑值和“或”运算逻辑值 。如:

  ’A’&&34.3f的返回值是逻辑真,为1;34&&0的返回值是逻辑假,为0 。

  0||’B’的返回值是逻辑真,为 1;0||0的返回值是逻辑假,为0 。

  & | ^ 其前后各接一数字,返回两数字之“与”运算、“或”运算、“异或”运算值 。如前面所说,先将两侧的数字转成二进制数, 而后对各位进行与、或、异或操作 。如:

  4&6的返回值是4,4转为00000100,6转为00000110各位相与得,00000100,为4 。

  4|6的返回值是6,4转为00000100,6转为00000110各位相或得,00000110,为6 。

  4^6的返回值是2,4转为00000100,6转为00000110各位相异或得,00000010,为2 。

  > < == >= <= !=

  其前后各接一数字,依据两数字是不是大于、小于、等于、大于等于、小于等于及不等于而返回相应的逻辑值 。如:

  34>34的返回值是0,为逻辑假;32<345的返回值为1,为逻辑真 。

  23>=23和23>=14的返回值都是1,为逻辑真;54<=4的返回值为0,为逻辑假 。

  56==6的返回值是0,为逻辑假;45==45的返回值是1,为逻辑真 。

  5!=5的返回值是0,为逻辑假;5!=35的返回值是真,为逻辑真 。

  >> <<

  其前后各接一数字,将左侧数字右移或左移右侧数字指定的位数 。与前面的 '、&、|等操作一样,之所以要提供左移、右移操作主要是由于CPU提供了这些指令,主要用于编一些基于二进制数的算法 。

  <<将左侧的数字转成二进制数, 而后将各位向左移动右侧数值的位数,如:4,转为00000100,左移2位,则变成00010000,得16 。

  >>与<<一样,只是是向右移动罢了 。如:6,转为00000110,右移1位,变成00000011,得3 。假如移2位,则有一位超出,将截断,则6>>2的返回值便是00000001,为1 。

  左移和右移有什么用?用于一些基于二进制数的算法,不过还 可以顺便作为一个 容易的优化 目的 。考量十进制数3524,我们将它左移2位,变成 352400,比原数 扩充了100倍,精确的说应该是 扩充了10的2次方倍 。假如将3524右移2位,变成35,相当于原数除以100的商 。

  同样,前面4>>2,等效于4/4的商;32>>3相当于32/8,即相当于32除以2的3次方的商 。而4<<2等效于4*4,相当于4乘以2的2次方 。 因此左移和右移相当于乘法和除法,只是不得不是乘或除相应进制数的次方罢了,但它的运行速度却远远高于乘法和除法, 因此说它是一种 容易的优化 目的 。

  ,

  其前后各接一数字, 容易的返回其右侧的数字 。如:

  34.45f,54的返回值是54;-324,4545f的返回值是4545f 。

  那它到底有什么用?用于将多个数字整和成一个数字,在《C++从零开始(四)》中将进一步 注明 。

  关于 其余的二元操作符将在后续文章中陆续提到(但不 定然所有提到) 。

  三元操作符惟独一个,为?:,其 格局为:<数字1>?<数字2>:<数字3> 。它的返回值为:假如<数字1>是逻辑真,返回<数字2>,不然返回<数字3> 。如:

  34?4:2的返回值便是4,由于34非零,为逻辑真,返回4 。而0?4:2的返回值便是2,由于0为逻辑假,返回2 。

   抒发式

  你应该发现前面的 荒谬之处了??12>435返回值为0,那为何不直接写0还吃饱了撑了写个12>435在那?这便是 抒发式的 意思了 。

  前面说“>”的前后各接一数字,然而操作符是操作数字并返回数字的符号,由于它返回数字, 因此 可以放在上面说的任何一个要求接数字的地方,也就 构成了所谓的 抒发式 。如:23*54/45>34的返回值便是0,由于23*54的返回值为1242; 而后又将1242作为“/”的左接数字,得到新的返回值27.6;最终将27.6作为“>”的左接数字进而得到返回值0,为逻辑假 。

   因此 抒发式便是由一系列返回数字的东西和操作符组合而成的一段代码,其由于是由操作符构成的,故 定然返回值 。而前面说的“返回数字的东西”则 可以是另一个 抒发式,或者一个变量,或者一个 存在返回值的函数,或者 存在数字类型操作符重载的类的对象等,反正 惟独是能返回一个数字的东西 。假如关于何谓变量、函数、类等这些名词感到 生僻,不需求去管它们,在后继的文章中将会逐个 注明 。

   因此34也是一个 抒发式,其返回值为34,只是是没有操作符的 抒发式罢了(在后面将会了解到34其实是一种操作符) 。故 抒发式的概念其实是很广的, 惟独有返回值的东西就 可以称为 抒发式 。

  由于 抒发式里有众多操作符,执行操作符的顺序依赖于操作符的优先级,就和数学中的一样,*、/的优先级大于+、-,而+、-又大于>、<等逻辑操作符 。不用去刻意记住操作符的优先级,当不能确定操作符的执行顺序时, 可以 使用小括号来进行指定 。如:

  ((1+2)*3)+3)/4的返回值为3,而1+2*3+3/4的返回值为7 。 留神3/4为0,由于3/4的商是0 。当 盼望进行浮点数除法或乘法时, 惟独让操作数中的某一个为浮点数即可,如:3/4.0的返回值为0.75 。

  & | ^ '等的 利用

  前面提过逻辑操作符“&&”、“||”、“!”等,作为 示意逻辑,其被C++提供丝毫都不值得惊奇 。然而为何要有一个将数字转成二进制数, 而后对二进制数的各位进行逻辑操作的这么一类操作符呢?首先是CPU提供了相应的指令,而且其还有着下面这个十分有 意思的 利用 。

  考量一十字路口,每个路口有三盏红绿灯,分别指明 是不是左转、右转及直行 。共有12盏,现在要为它编写一个操纵程序, 无论这程序的 性能 怎么,首先需求将红绿灯的状态转化为数字,由于电脑只晓得数字 。所以用3个数字分别 示意某路口的三盏红绿灯, 因此每个红绿灯的状态由一个数字来 示意, 假如红灯为0,绿灯为1(不考量黄灯或 其余状况) 。

  后来 蓦地发现,其实也 可以用一个数字 示意一个路口的三盏红绿灯状态,如用110 示意左转绿灯、直行绿灯而右转红灯 。上面的110是一个十进制数字,它的每一位实际都 可 认为0'Array十个数字,然而这里只 利用到了两个:0和1,觉得很 浪费 。故 取舍二进制数来 示意,还是110,然而是二进制数了,转成十进制数为6, 即便当为111时转成十进制数也只是7,比前面的110这个十进制数小多了,节约了……??什么??

  我们在纸上写数字235425234 定然比写134这个数字要更多地占用纸张( 假如字都一样大) 。 因此记录一个大的数比记录一个小的数要 花费更多的资源 。 几乎 荒谬! 无论是100还是1000,都只是一个数字,为何记录大的数字就更费资源?由于电脑并不是数字计算机,而是电子计算机,它是基于状态而不是基于数字的,这在下篇会 详尽 注明 。电脑必须 使用某种 示意 模式来代表一个数字,而那个 示意 模式和二进制很像,但并不是二进制数,故浮现记录大的数较小的数更耗资源,这也便是为何上面整型数要分什么长整型短整型的缘由了 。

  下面 接续上面的思量 。 使用了110这个二进制数来 示意三盏红绿灯的状态,那么现在要晓得110这个数字代表左转红绿灯的什么状态 。以数字的第三位 示意左转,不过电脑并不晓得这个, 因此如下:110&100 。这个 抒发式的返回值是100,非零,逻辑真 。 假如某路口的状态为010,则同样的010&100,返回值为0,逻辑假 。 因此 使用“&”操作符 可以将二进制数中的某一位或几位的状态提 存入来 。所以我们要了解一个数字代表的红绿灯状态中的左转红绿灯是不是绿灯时, 惟独让它和100相与即可 。

  现在要 维持 其余红绿灯的状态不变,仅仅使左转红绿灯为绿灯,如目前状态为010,为了使左转红绿灯为绿灯,值应该为110,这 可以通过010|100做到 。假如目前状态是001,则001|100为101,正确??直行和右转的红绿灯状态均没有 产生 变迁 。 因此 使用“|”操作符 可以给一个二进制数中的某一位或几位设置状态,但不得不设置为1,假如想设置为0,如101,要关掉左转的绿灯,则101&'100,返回值为001 。

  上面向来提到的路口红绿灯的状态实际编写时 可以 使用一个变量来 示意,而上面的100也 可以用一个标识符来 示意,如state&TS_LEFT,就 可以 示意 审查变量state所 示意的状态中的左转红绿灯的状态 。

  上面的这种 步骤被大量地 使用,如 缔造一个窗口,一个窗口可能有二三十个 格调,则通过上面的 步骤,就 可以只用一个32位长的二进制数字就 示意了窗口的 格调,而不用去弄二三十个数字来分别代表每种 格调是不是 存在 。