html
因为前几天的笔试很大程度上刺激了我,让我愈加感受到本身的C/C++基础十分地薄弱,故而想要找几本经典的C/C++书原本深刻了解一下C/C++语言特性,以及其中须要注意的问题。程序员
google了一下C语言经典著做,获得了我想要的结果:web
《C专家编程》算法
《C语言详解》编程
《C语言核心技术》数组
《C陷阱与缺陷》安全
《C和指针》服务器
详细请到C经典著做书单察看介绍。数据结构
正是我想要补的,以前学习C语言,只是泛泛地学习了一些语言特性,而后编写了若干行的代码,知道了怎么使用指针,数组,结构体,以及用它编写二叉树,线性表等数据结构,还有实现了一遍大部分排序算法。ide
经常遇到这样的状况,程序写到一半,忘了这个代码的语言细节是什么的,例如++自增的使用,而这每每涉及到程序的正确性。有经验的程序员每每会发现i++和++i在不少状况下有很大不一样,一时的麻痹或者粗心,都有可能形成重大的错误。记得以前有一个这样的故事:
美国宇航局发射航天飞机,可是由于一个程序员将“;”写成了“,” ,最后形成了飞机失事。听说这是最昂贵的错误。
代码是人编写的,不免会有错误,可是不少错误都是咱们不清楚语言细节,或者某个语言的陷阱,从而失足跌倒。现实生活中不少代码都有错误,而***老是可以利用各类程序中的缺陷和漏洞,攻破咱们的系统,拿走咱们的数据和金钱。(我想起了《鹿鼎记》里面的,咱们的财宝和女人,哈哈)。因此要写可靠的代码,必需要懂得一些经常使用的语言细节和规避陷阱的方法。
咱们编程的时候每每运行出结果就ok了,抛下程序去玩了。这是很不正确的学习方法,正确的学习方法应该深刻理解程序在计算机程序在编译和运行时作了什么,故而多跟踪调试程序是有好处的。看看每一步都发生了什么,察看内存中的数据起了什么变化。
这里有一篇关于内存管理的很好的说明了哪些数据是存储在什么区的。学习过汇编语言的咱们,很容易就想起来咱们的程序包括一些段:数据段,代码段,堆栈段。那么在高级语言中,这些数据又是怎样存储的呢?以下:
栈stack: 存放局部变量,栈表示的是一种后进先出,表达了我程序调用的顺序,故而栈里面存放的是咱们的函数中的局部变量,包括变量,指针,参数等等。栈内数据是共享的,回收工做是由内存管理来作的。
堆heap: 说白了就是程序申请的内存区,这里一块,那里一块,堆是受到保护的,不能互相访问。而回收的时候,咱们要收回指向他们的指针,而后GC(gabage collection)来处理。通常malloc,或者是new的对象都是存储在堆里面的。
(全局)静态区: 全局区和静态区是是在一块儿的。包括全局变量global修饰的变量,static修饰的变量。有的时候,尽管没有static修饰,也算是静态区的。例如 char *p = "hello"; 字符串"hello"就属于静态区的,由于没有专门为它分配空间,如果char c_arr[] = "hello"; 则有为字符串分配内存,故而是在栈区。
文字常量区:常量字符串就是放在这里的。例如cout<< "hello world"<<endl; 那么字符串hello world就存放在常量区。
程序代码区:存放程序的二进制代码。
深入理解程序和数据的存储,会更加明白如何编写节省资源和高效的程序,特别是分析因为存储问题带来的性能瓶颈问题,这在web和服务器编程上都很重要。
我作了如下实验,对指针进行了sizeof关键字操做,以及指针的加法操做。
- // 关于数组的sizeof运算
- int a[100]={0,1};
- printf("%d\n",sizeof(a)); // 打印的是数组的不少信息,
- printf("%d\n",sizeof(a[100])); //
- printf("%d\n",sizeof(&a)); // 是a的地址
- printf("%d\n",sizeof(&a[0])); //
- fun(a); // 做为参数传递的数组,实际上是个指针
fun的定义以下:
- void fun(int b[100])
- {
- printf("%d\n",sizeof(b));
- }
- // 获得的结果是
- // 400
- // 4
- // 4
- // 4
- // 4
要是想在子函数中获得数组的大小,那么使用下面的fun:
- void fun(int (*a)[100])
- {
- printf("%d\n",sizeof(*a));
- }
调用的时候是
- fun(&a);
故而可以获得预期的400。
各类数据类型的转换,某些状况下系统是可以自动类型转换的,如赋值,表达式,循环条件判断中。遵循的是从低精度变到高精度的原则。而从高精度强制转换到低精度,则须要损失一部分的数据,因此应该斟酌为之,特别是不肯定转换结果的状况下。
只要弄明白了数据类型在计算机里面怎么存储,就可以明白什么样的转换是行的,什么样的转换是不行的。×××数据在内存中,存储的都是补码。对于负数,补码是反码+1,而对正数补码就是自己的码。例如char类型的(默认是unsigned)能够表示[0,255] ,若是是signed,那么就能够表示[-128,127],也就是说分了一半去表示负数。int同理,通常的程序中,不会有太多的类型转换,由于这潜在着不安全因素,例如if里面的表达式就是一个很好的例子。
对于bool型的,假若有个checked表示某个步骤是否经过检查, 用if(checked)。
对于int型,直接if( 0 == num) 比较
对于浮点型,用 if ( 0-EP<num && num > 0+EP) 来肯定是否和0相等
因此,咱们要用一样类型的去比较,C中能够while(a), a是一个int型,Java就比较严格,不容许将int转换为bool类型作判断。
这个就要参考程序语言的手册了,咱们本身写程序的时候,最保险的就是给各类表达式都加上括号,以免意外的求值出现。同级的按结合顺序来判断。例如+,-,*,/ , %是左结合;而对于!,=这样的,就属于右结合。有什么规律可循呢?全部的优先级中,只有三个优先级是从右至左结合的,它们是单目运算符、条件运算符、赋值运算符。其它的都是从左至右结合。固然,运算符是有级别的,级别高的先执行。具体请参考百度百科C运算符。
自增++等,++的位置很重要,i++,表示用完i再自增1;而++i表示先作自增,而后再使用新的i值。
自减--,同理。这两个颇有技巧的运算符使用时要特别当心。有时候让i用完,自增1颇有用,例如最寻常的for循环,以及排序里面,上次作一道题目,是插入排序,其中我就搞岔了++i和i++。看来还须要好好修炼哪~
by bibodeng 2013-04-16 21:45:44