01 C:穿越时空的迷雾(const关键字,算术类型的转换,以及有符号无符号类型转换常见的错误)
1.讨论关于编程中代码移植的相关概念
不可移植的代码:
编译器定义的:由编译器设计者决定的行为,这就导致不同的编译器采取的行为不同。如:整型数右移位时,是否扩展符号位。
未确定的:C标准中未明确规定应该怎样做的行为。如:参数求值的顺序
坏代码:
未定义:在一些不正确的情况下出现时,标准未规定应该怎样做。如:有符号整数溢出是该采取何种行为。
约束条件:C标准中提出的必须遵守的限制和标准如:%操作符的操作数必须属于整型
标准规定,编译器只有在违反语法规则和约束条件的情况下才能产生错误信息。
为了最大限度保证代码的可移植性,一些遵循标准的程序可能依赖某种编译器的不可移植的特性,所以一个严格遵循标准程序应该具有以下特性:
a.只使用已确定的特性。
b.不突破任何由编译器实现的限制
c.不产生任何依赖有编译器定义的或未确定的或未定义的特性的输出。
2.每个实参都应该具有自己的类型,实参的值通过赋值传递给它的形参类型的对象。【即参数的传递过程类似于赋值】
而赋值:两个操作数都是指向有限定符或无限定符的相容类型的指针, 左边指针所指向的类型必须具有右边指针所指向类型的全部限定符。
注意:函数声明中形参类型要与实参传递给他具体值,要能够符合赋值的语法。
否则会出现参数与类型不匹配的报错信息。
foo(const char** p){}
main (int argc,char **argv){
foo(arvg);
}
函数调用过程中参数传递过程中:p=argv 将char ** 类型的值赋给const char **,所以报错。
3.const关键字
在一个符号前加一个const限定符只是表示这个符号不能被赋值,即它的值对这个符号来说是只读的。
①const用在数据上:表示这个数据是只读的。如:const int limit = 10;
②常量指针:const int * p ,是一个指针。该指针指向此变量的值,不可以通过该指针来修改。
也就是说这个指针不能用于修改它所指向的数,但是任何时候,这个指针本身的值是可以改变的。
③指针常量:int * const p, 是一个常量(只读)。指针本身是常量,它本身的内容(地址)不可变,但是指针指向的内容是可以通过指针来改变的。
const常用之处:
限定函数的形参,这样该函数将不会修改实参指针所指向的数据。void function (const int a){}
const 和*组合:通常用于在数组形式的参数中模拟传值调用。给调用函数一个指向数组的指针,但是不能修改它。
4.在ANSI C标准下的寻常算术转换
字符和整型(整型转换):
char,short int, int (包括他们的有符号和无符号变型),如果int可以完整表示源类型的所有值,那么这些源类型转换为int,否则转换为unsigned int ;
注:char 与 char类型算术运算,先各自转换为int类型后再运算。
寻常算术转换:
许多操作数类型为算术类型的双目运算符会引发转换,目的是产生一个普通的类型。
首先,如果其中一个操作数是long double,另一个操作数也转换long double.
其次,如果其中一个操作数类型为double ,另一个操作数也转换 double.
再次,如果其中一个操作数类型为float ,另一个操作数也转换 float.
否则这两个数进行整型升级(如下):
首先,如果其中一个操作数是unsigned long int ,另一个操作数也被转换为unsigned long int。
其次,如果其中一个操作数是long int,另一个操作数是unsigned int ,如果long int 能够完整表示unsigned int 所有值,
那么unsigned int 转为long int,如果不能则两个操作数转换为unsigned long int.
再次,如果其中一个操作数为long int ,另一个操作数转换为long int.
再再次,如果其中一个操作数是unsigned int ,另一个操作数转换为 unsigned int.
其他情况,(如果都不属于以上)两个操作数都转换为int
K&R C采用无符号值保留原则:当一个无符号类型与int 货更小整型混合时结果是无符号类型。(缺陷有时负数会丢符号位)
ANCI C采用值保留原则:当几个整型操作数混合使用时,结果可能有符号,也可能无符号,取决于操作数的类型和相对大小。
虽然有所改进但是有些时候BUG依然存在。
例:使用VS 2013编译器获得的结果。
正确示例。
#include <stdio.h> #include <stdlib.h> int main(){ if (-1 < (unsigned char)1) printf("-1 is less than (unsigned char) 1: ANSI semantics\n"); else printf("-1 NOT less than (unsigned char) 1: K&R semantics\n"); system("PAUSE"); return 0; }
运行结果:
ANSI C的BUG,-1转换成unsigned int 的结果将是一个非常巨大的正整数使表达式的结果为假。
#include <stdio.h> #include <stdlib.h> #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0])) int main(){ int array[] = {23, 24, 24, 25, 25,25 ,265,19}; int d = -1; if (d <= TOTAL_ELEMENTS){ printf("%d", array[4]); }else { printf("error!\n"); } system("PAUSE"); return 0; }
运行结果:
解决:将TOTAL_EKEMENTS进行强制类型转换即可。
#include <stdio.h> #include <stdlib.h> #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0])) int main(){ int array[] = { 23, 24, 24, 25, 25, 25, 265, 19 }; int d = -1; if (d <= (int)TOTAL_ELEMENTS){ printf("%d \n", array[4] ); } else { printf("error!\n"); } system("PAUSE"); return 0; }
运行结果:
总结:尽量不要在代码中使用无符号类型,以免增加不必要的复杂。