一、数据方面:
PART Ⅰ:
①对于数据的宏观理解——任何一个完整的程序都可以看成是一组数据+作用于这组数据上的操作的说明
②语言的演变过程:
机器语言——0和1,分别有低电压和高电压代替,不同类型的计算机需要不同语言,难写;
汇编语言:开发效率高于机器语言,可以直接和计算机底层的软件与硬件进行交互,但很难调试,很难读,开发效率仍然低下;
高级语言的显著特征:跨平台移植性好,且往往封装库函数
③编译方式:
解释程序——一行行读,不生成目标程序——Java&JS&PHP
编译程序——先把源程序作为输入,全部翻译成二进制代码,生成目标程序之后再执行——C&C++
④对于数据类型的思考——有符号数和无符号数的区别,在于如何解释数据的最高位!在有符号数的眼中,最高位为1代表是负数,最高位为0代表是正数。因此int型的负数被解释成unsigned型的时候,就会变成一个很大的数
⑤关于char型变量的值——不要直接用ASCII给char赋值,有的不同的计算机系统可能使用的不是ASCII编码,所以应当全部使用字符常量赋值——像char a='A';
⑥比较麻烦的一个问题——char在不同环境下,可能默认的是signed char或者是unsigned char,我们没法确定。但是如果显式声明出来的话,又可能使得某些只能用于signed char或者只能用于unsigned char类型的库函数无法正常使用,况且有些机器处理signed char的效率会更高,声明成unsigned往往还会牺牲一部分效率——由此最好的办法就是把将使用的字符的值限制在signed与unsigned的交集范围,也就是0-127中,来获得最好的移植性。
⑦接⑥,在作算数运算的时候,显式的声明出signed或者unsigned,以避免一些不必要的麻烦。
⑧表示一个对象所占用的空间时,或者是循环变量的大小&长度等时,用size_t来保证移植性。同时,表示单个对象的长度的时候,用rsize_t,虽然绝大多数时候它和size_t一样,但是这样的话可读性更好,一看就知道表示的是单个对象的长度,并且在溢出时会有安全检查。
⑨接⑧,一定不可以混用size_t与它所代表的真实类型,比如unsigned int,尤其是让这两个类型在一个表达式中共同作计算,在不同的环境下可能引起相互之间的转换,从而引起严重的问题。
⑩一定要注意unsigned型的整数!因为有符号整数类型与无符号整数类型在进行运算时,有符号整数类型会先自动转化成无符号整数类型。即使要用,在表达式中也应当显式的声明强转成有符号或者无符号,以避免由编译器来选择最终的结果,从而造成严重的后果。
PART Ⅱ:
①防止无符号整数的溢出问题——当数值超过限定长度时,它将会发生取模操作,从而使得数字回绕!——当异常出现的很大的数被应用于memcpy等地时,则会造成堆栈崩溃。
②对于浮点数——尽量少使用,而且一个十进制的小数能够用浮点数精确的表示的条件是,它的末位为5。至于十进制整数转二进制的那一套除以二取余,或者小数部分乘二取整取余的操作,结合十进制的进位机制理解即可。
留个尾巴:
③因此,表示浮点数的更好的办法是使用分数(?)
同时,应当不在浮点数作比较的时候使用"=="运算符,而采用"abs(a-b)<一个很小的数"的方式来比较大小。但是假如那个很小的数字是0.1,处理像1000000和1000001这种也是可以看作相等的数据时就会有局限性。同样的,0.5和0.4不能看作相等,于是引出了这样的比较方法:
double a=12414.123,b=12414.122;
double a1=fabs(a);
double b1=fabs(b);
double max_val=a1>b1?a1:b1;
//此时的比较条件就变成了:
if(fabs(a-b)/max_val<设定的一个很小的数)
④由此,应该尽可能的避免使用浮点数来作为计数器!有时候可能因为精度太低,导致无法正常++,也有时候会因为精度不全,导致计数多一些或者少一些。同时,当整数参与浮点数的运算时,应当先把整数强转为浮点数再使其参与运算。
⑤在数据类型转换的时候,一定要做范围检查!可以看limits.h头文件中的宏定义,来对转换之后的数据的范围作检查,以避免数据截断等问题造成数据丢失的问题。同时,应当使用更严格的一些类型,比如如果需要一个数字的大小严格>=4个字节,用long比int好,而且往往效率差不多。
⑥多使用typedef来指定一些数据类型——可以增强可读性,并且还可以提高跨平台的移植性,而且有时候也可以给一些自定义的数据类型赋予更加简洁的名称(包括一些很复杂的指针、数组类型)
⑦注意:typedef自身就是个存储类关键字,会占据存储类关键字的位置,于是在typedef声明中再进行static等声明是不合法的——注意,typedef与define有区别,define是预处理阶段完成的,不会做正确性检查,只有编译已展开的源程序的时候才会查错;而typedef是编译阶段完成的,并且不是简单的复制粘贴——比如typedef char* PCHAR;const PCHAR a;此时a是个char*const,而不是const char*——原因很简单,不是直接复制粘贴!PCHAR在编译器看来是一个指针,因此const就赋给了这个指针,是一个顶层const,所以本质上是char*const而不是const char*!
⑧为了可读性,不建议在一个声明中声明超过一个的变量!比如int a,*b;很丑,直接分行就好了。
⑨在局部变量的代码块,全局变量将不起任何作用,所以虽然语法允许,也不要在不同的作用域中使用同名变量!
⑩