您的位置:首页 > 编程语言 > C语言/C++

C++ primer plus 6th ed. 中文版读书笔记【第二章】

2014-02-14 14:15 337 查看

写在前面

        第一篇博客,没什么深入可谈,虽已上大三不过编程经验却积累的可怜。想借毕业前及考研前的压力认真复习一遍C++,故选择此书进行学习。此前犹豫C++ primer 和 C++ primer plus 的选择问题,大抵翻阅后者看起来比较基础易懂,话说回来,都是好书,可一本先精读吧!

写作风格

        虽说是读书笔记,不过我不会详细分析或复制书中的话语进行总结,而是在阅读到某一知识点时结合自己两年半所学内容进行思想发散。一篇博客是一章内容,我会边阅读边整理,进行多次更新拓展。我喜欢这种由一个问题延伸出其他问题再依次解决的学习方式,虽然文章内容会较为浅显,也会有许多漏洞,但希望有相同兴趣的朋友进行交流、批评指正!

第2章 开始学习C++

1、P14 - “通常并不从程序的其他部分调用main( )”

        这一章内容比较简单基础,不过这句话引起了我的注意,以前好像还真没仔细想过这个问题,于是写了段神奇的代码:

void func();

int main(){
func();
}
void func(){
main();
}
        肯定是个死循环没错,不过至少可以肯定main( ) 也是个“正常的函数”,可以被调用。而与其他函数不同的是,main( ) 被启动代码调用,该函数头“ int main( )” 描述的是 main( ) 和操作系统之间的接口(P14 倒数第五段)。为了验证入口一说,我又写了如下无脑代码,对main( ) 进行了声明操作:
int main();
void func(){
cout<<"in func";
main();
}

int main(){
cout<<"in main";
func();
}
 
      先出来的是“ in main ” 没错,不会像其他函数一样依照顺序进行调用。

        接下来的问题是,如何停止死循环?

int num = 0;
void func();
int main(){
cout<<"in main"<<endl;
if(num == 4){
return 0;
}
func();
}
void func(){
cout<<"in func"<<endl;
num++;
main();
}
        我用了这种很笨的方法停止,虽然会有其他方法,不过我想到了一个其他问题:递归调用。大一学C的时候老师就提到过这个词,一直没有好好分析,隐约记得和堆栈有关,查阅多方资料* 整理了一下。资料中提出了这样一个问题:给出一个值4267,依次输出每个字符,即:‘4’ ‘2’ ‘6’ ‘7’。先不考虑递归和转成字符的问题,只考虑栈,有如下的实现方式:

#include <iostream>
using namespace std;
//利用栈调用
const int StackSize = 10;

template <class T>
class SeqStack
{
public:
SeqStack( ) ; //构造函数,栈的初始化
void Push(T x); //将元素x入栈
T Pop( ); //将栈顶元素弹出
T GetTop( ); //取栈顶元素(并不删除)
private:
T data[StackSize]; //存放栈元素的数组
int top; //栈顶指针,指示栈顶元素在数组中的下标
};

template <class T>
SeqStack<T>::SeqStack()
: top( 0 )
{ }

template <class T>
void SeqStack<T>::Push( T x )
{
data[top++] = x;
}

template <class T>
T SeqStack<T>::Pop()
{
return data[--top];
}

template <class T>
T SeqStack<T>::GetTop()
{
return data[top-1];
}

int main(){
SeqStack<int> a;
int num = 4267;
int remainder;
for(int i = 0;i<4;i++){
remainder = num % 10;
num = num/10;
a.Push(remainder);
}
for(int i=0;i<4;i++){
cout<<a.Pop()<<endl;
}

return 0;
}
        考虑到如果直接输出余数则是逆序,所以想到了堆栈的“先进后出“。前半段代码等于说自己写了个堆栈,而后参考资料的解法,意识到了递归的妙处(用C实现):

#include <stdio.h>

int binary_to_ascii( unsigned int value)
{
unsigned int quotient; //quotient: 商

  quotient = value / 10;
  if( quotient != 0)
    binary_to_ascii( quotient);
  putchar ( value % 10 + '0' );
}


        图解如下:


        执行除法之后,堆栈的内容如下:



  

        接着,if语句判断出quotient的值非零,所以对该函数执行递归调用。当这个函数第二次被调用之初,堆栈的内容如下:

         



        

        堆栈上创建了一批新的变量,隐藏了前面的那批变量,除非当前这次递归调用返回,否则他们是不能被访问的。再次执行除法运算之后,堆栈的内容如下:

 



        quotient的值现在为42,仍然非零,所以需要继续执行递归调用,并再创建一批变量。在执行完这次调用的出发运算之后,堆栈的内容如下:
 



        此时,quotient的值还是非零,仍然需要执行递归调用。在执行除法运算之后,堆栈的内容如下:
 



        用图解的方式看就比较明白了,总之递归有他的好处,不过貌似效率较低(有待查证),用起来需要头脑比较清晰,找到那个跳出递归的”临界点“。

2、P44 - 关于溢出与cout的8、10、16进制输出控制

        以前对于溢出的理解仅仅停留在“溢出会出错”这一个浅显的层面,可读到这一块时却让我有了全新的认识。程序不多说,有一个书中插图我认为十分有用且易懂。

        


        之所以觉得这个溢出结果有用,是认为在出现一个相对结果时能更快想到溢出错误。更多的溢出可能发生在int型在32位机的代码在16位机上编译时产生的溢出吧,书中对此有一个插图,那个-29499得出的原因也可以解释了。

        

        


        几个数字值得我们记忆(不同类型所表示的数据范围):

        signed short: -32768 ~ +32767

        unsigned short: 0 ~ 65535
        32位单元有40亿个值。

        溢出后的另一个问题是,它显示出的溢出后的变值(比如溢出后显示-1)进行进制转换的结果,与普通值-1进行进制转换的结果有差别么?在验证之前首先有三行有用的代码值得记忆。
        
      
cout<<dec; //以十进制结果输出
cout<<oct; //以八进制结果输出
cout<<hex; //以十六进制结果输出


        我用如下程序进行验证:
        
       



        结果如下:

        


        最后的s_value和n_compare的值的结果我还有稍许疑问,进制的知识特别是负数的转换忘差不多了,我会尽快查阅资料补全。原以为结果是一样的,不过百度知道一个人提到溢出后的表象值进行进制转换的结果不相同。有待验证。

        待续。

*递归部分资料参考整理自:http://www.cnblogs.com/zhangqqqf/archive/2008/09/12/1289730.html,十分感谢!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ 读书笔记
相关文章推荐