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;
}

运行结果:

 总结:尽量不要在代码中使用无符号类型,以免增加不必要的复杂。

posted on 2015-11-21 15:22  super90  阅读(265)  评论(0)    收藏  举报

导航