C++入门 函数的作用


  由算法得出代码

  本系列一开头就 注明了何谓程序,并 注明由于CPU的世界和人们存在的客观物理世界的不兼容而招致 根本不能将人编写的程序(也便是算法)翻译成CPU指令,但为了 可以翻译,就必须让人感觉CPU世界中的某些东西是人 认为的算法所 形容的某些东西 。如电脑屏幕上显示的图片,通过显示器对不同象素显示不同 色彩而让人 认为那是一幅图片,而电脑只晓得那是一系列数字,每个数字代表了一个象素的 色彩值而已 。

  为了实现上面的“让人感觉是”,得到算法后要做的的第一步便是找出算法中要操作的资源 。前面已经说过,任何程序都是 形容如何操作资源的,而C++语言 本身不得不操作内存的值这一种资源, 因此编程要做的第一步便是将算法中操作的东西映射成内存的值 。由于内存单元的值以及内存单元地址的延续性都 可以通过二进制数 示意出来, 因此要做的第一步便是把算法中操作的东西用数字 示意出来 。

  上面做的第一步就相当于数学建模——用数学语言将问题表述出来,而这里只是是用数字把被操作的资源表述出来罢了(应 留神数字和数的区别,数字在C++中是一种操作符,其有 有关的类型,由于最终对它进行计算得到的还是二进制数故 使用数字进行 示意而不是二进制数,以 加强语义) 。接着第二步便是将算法中对资源的全部操作都映射成语句或函数 。

  用数学语言对算法进行表述时, 比方将每10分钟到车站等车的人的数量映射为一随机变量,也就前述的第一步 。随后定此随机变量 听从泊松 分布,也便是上面的第二步 。到站等车的人的数量是被操作的资源,而给出的算法是每隔10分种转变这个资源,将它的值变成按给定参数的泊松函数 分布的一随机值 。

  在C++中,前面已经将资源映射成了数字,接着就要将对资源的操作映射成对数字的操作 。C++中能操作数字的就惟独操作符,也便是将算法中对资源的全部操作都映射成 抒发式语句 。

  当上面都 实现了,则算法中剩下的就惟独执行顺序了,而执行顺序在C++中便是从上朝下书写,而当需求逻辑推断的介入而转变执行顺序时,就 使用前面的if和goto语句(不过后者也 可以通过if后接的语句来实现,这样 可以削减goto语句的 使用,由于goto的语义是跳转而不是“所以就”),并可考量是不是 可以 使用循环语句以简化代码 。即第三步为将执行流程用语句 示意出来 。

  而前面第二步之所以还说可映射成函数,即可能某个操作 比较复杂,还带有逻辑的 象征,不能直接找到对应的操作符,这时就只好利用万能的函数操作符,对这个操作 反复 方才上面的三个步骤以将此操作映射成多条语句(通过if等语句将逻辑信息 体现出来),而将这些语句定义为一函数,供函数操作符 使用以 示意那个操作 。

  上面假如未明不要紧,后面有两个例子,都将分别 注明各自是如何进行上述步骤的 。

  排序给出三张卡片,上面 随便写了三个整数 。有三个盒子,分别标号为1、2和3 。将三张卡片随机放到1、2、3这三个盒子中,现在要求排序以使得1、2、3三个盒子中装的整数是由小到大的顺序 。

  给出一最 方便的算法:称1、2、3盒子中放的卡片上的整数分别为第一、二、三个数,则先将第一个数和第二个数 比较,假如前者大则两个盒子内的卡片 交换;再将第一个和第三个 比较,假如前者大则 交换,这样就 保障第一个数是最小的 。 而后将第二个数和第三个数 比较,假如前者大则 交换,至此排序 实现 。

  第一步:算法中操作的资源是装在盒子中的卡片,为了将此卡片映射成数字,就 留神算法中的卡片和卡片之前有什么不同 。算法中 划分不同卡片的唯一 步骤便是卡片上写的整数, 因此在这里就 使用一个long类型的数字来 示意一个卡片 。

  算法中有三张卡片,故用三个数字来 示意 。前面已经说过,数字是装在内存中的,不是变量中的,变量只是是映射地址而已 。在这里需求三个long类型数字, 可以借用定义变量时编译器自动在栈上 调配的内存来记录这些数字,故 可以如此定义三个变量long a1, a2, a3;来记录三个数字,也就相当于装三张卡片的三个盒子 。

  

  第二步:算法中的操作便是对卡片上的整数的 比较和 交换 。前者很 方便, 使用逻辑操作符就 可以实现(由于正好将卡片上的整数映射成变量a1、a2和a3中记录的数字) 。后者是 交换两个盒子中的卡片, 可以先将一卡片从一盒子中 存入来,放在桌子上或 其余地方 。 而后将另一盒子中的卡片 存入来放在 方才空出来的盒子 。最终将先 存入来的卡片放进刚空出来的盒子 。前面说的“桌子上或 其余地方”是用来 存放 存入的卡片,C++中惟独内存 可以 存放数字, 因此上面就必须再 调配一暂时内存 降暂时记录 存入的数字 。

  第三步:操作和资源都已经映射好了,算法中有假如的就用if替换,由什么 反复多少次的就用for替换,有什么 反复直到 怎么的就用while或do while替换,如上照着算法映射过来就完了,如下:

  void main()

  {

  long a1 = 34, a2 = 23, a3 = 12;

  if( a1 > a2 )

  {

  long temp = a1;

  a1 = a2;

  a2 = temp;

  }

  if( a1 > a3 )

  {

  long temp = a1;

  a1 = a3;

  a3 = temp;

  }

  if( a2 > a3 )

  {

  long temp = a2;

  a2 = a3;

  a3 = temp;

  }

  }

  上面就在每个if后面的复合语句中定义了一个暂时变量temp以借助编译器的静态 调配内存 性能来提供暂时 存放卡片的内存 。上面的元素 交换并没有依照前面所说映射成函数,是由于在这里其惟独三条语句且方便 了解 。假如要将 交换操作定义为一函数,则应如下:

  void Swap( long *p1, long *p2 ) void Swap( long &r1, long &r2 )

  { {

  long temp = *p1; long temp = r1;

  *p1 = *p2; r1 = r2;

  *p2 = temp; r2 = temp;

  } }

  void main() void main()

  { {

  long a1 = 34, a2 = 23, a3 = 12; long a1 = 34, a2 = 23, a3 = 12;

  if( a1 > a2 ) if( a1 > a2 )

  Swap( &a1, &a2 ); Swap( a1, a2 );

  if( a1 > a3 ) if( a1 > a3 )

  Swap( &a1, &a3 ); Swap( a1, a3 );

  if( a2 > a3 ) if( a2 > a3 )

  Swap( &a2, &a3 ); Swap( a2, a3 );

  } }

  

  先看左侧的程序 。上面定义了函数来 示意给定盒子中间的 交换操作, 留神参数类型 使用了long*,这里指针 示意 引用(应 留神指针不只 可以 示意 引用,还可有其它的语义,以后会提到) 。

  什么是 引用? 留神这里不是指C++提出的那个 引用变量, 引用 示意一个衔接关系 。 比方你有手机,则手机号码便是“和你通话”的 引用,即 惟独有你的手机号码,就 可以实现“和你通话” 。

  再 比方Windows操作系统提供的快捷 模式,其便是一个“对某文件执行操作”的 引用,它 可以指向某个文件,通过双击此快捷 模式的图标就 可以对其所指的文件进行“执行”操作(可能是用某软件 打开这个文件或是直接执行此文件等),但假如删除此快捷 模式却并不会删除其所指向的文件,由于它只是“对某文件执行操作”的 引用 。

  人的名字便是对“某人进行标识”的 引用,即说某人考上大学通过说那个人的名字则大家就 可以晓得具体是哪个人 。同样,变量也是 引用,它是某块内存的 引用,由于其映射了地址,而内存块 可以通过地址来被唯一表明其存在,不只仅是标识 。 留神其和前面的名字不同,由于任何对内存块的操作, 惟独晓得内存块的首地址就 可以了,而要和某人面对面讲话或吃饭,只晓得他的名字是不够的 。

  应 留神对某个东西的 引用 可以不止一个,如人就 可以有多个名字,变量也都有 引用变量,手机号码也 可以不止一个 。

   留神上面引入了函数来 示意 交换,进而招致了盒子也就成了资源, 因此必须将盒子映射成数字 。而前面又将盒子里装的卡片映射成了long类型的数字,由于“装”这个操作, 因此 可以想到 使用 可以标识装某个代表卡片的数字的内存块来作为盒子映射的数字类型,也便是内存块的首地址,也便是long*类型( 留神不是地址类型,由于地址类型的数字并不返回记录它的内存的地址) 。所以上面的函数参数类型为long* 。

  下面看右侧的程序 。参数类型变成long&,和指针一样,依然 示意 引用,但 留神它们的不同 。后者 示意它是一个别名,即它是一个映射,映射的地址是记录作为参数的数字的地址,也便是说它要求调用此函数时,给出的作为参数的数字 定然是有地址的数字 。所谓的“有地址的数字” 示意此数字是程序员 缔造的,不是编译器由于暂时原 因此生成的暂时内存的地址,如Swap( a1++, a2 );就要报错 。之前已经 注明,由于a1++返回的地址是编译器内部定的,就程序逻辑而言,其是不存在的,而Swap( ++a1, a2 );便是正确的 。Swap( 1 + 3, 34 );依然要报错,由于记录1 + 3返回的数字的内存是编译器内 部分配的,就程序逻辑上来说,它们并没有被程序员用某块内存记录起来,也就不会有内存 。

  一个很 方便的 断定 规定便是调用时给的参数类型假如是地址类型的数字,则 可以,不然不行 。

  还应 留神上面是long&类型, 示意所 润饰的变量不 调配内存,也便是编译器要静态地将参数r1、r2映射的地址定下来,关于Swap( a1, a2 );就分别是a1和a2的地址,但关于Swap( a2, a3 );就变成a2和a3的地址了,这样是 无奈一次就将r1、r2映射的地址定下来,即r1、r2映射的地址在程序运行时是 变迁的,也就不能且 无奈编译时静态一次确定 。

  为了实现上面的要求,编译器实际将会在栈上 调配内存, 而后将地址传递到函数,再编写代码以使得 如同动态绑定了r1、r2的地址 。这实际和将参数类型定为long*是一样的 动机,即上面的Swap( long&, long& );和Swap( long*, long* );是一样的,只是语法书写上不同,内部是 雷同的,连语义都 雷同,均 示意 引用( 固然指针不只仅只带有 引用的语义) 。即函数参数类型为 引用类型时,依然会 调配内存以传递参数的地址,即等效于指针类型为参数 。

  生意人过河问题3个生意人带着3个 佣人过河,过河的工具惟独一艘小船,不得不同时载两个人过河,包含划船的人 。在河的任何一边, 惟独 佣人的数量超过生意人的数量, 佣人就会联合起来将生意人杀死并 抢夺其财物,问应如何设计过河顺序 威力让全部人都安全地过到河的另一边 。

  给出最弱却万能的算法——枚举法 。坐船过河及划船回来的可能 方案为一个 佣人、一个生意人或两个生意人、两个 佣人及一个生意人一个 佣人 。

  故每次从上述的五种 方案中 取舍一个划过河去, 而后 审查河岸两侧的人数,看是不是会 产生 佣人杀死生意人,假如两边都不会,则再从上述的五个 方案中 取舍一个让人把船划回来, 而后再 审查是不是会 产生 佣人杀死生意人,假如没有就又再一次从五个 方案 当选一个划过河,如上 反复直到全部人都过河了 。