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
");
    else
        printf("-1 NOT less than (unsigned char) 1: K&R semantics
");
    
    
    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!
");
	}

	
	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 
", array[4] );
    }
    else {
        printf("error!
");
    }        
    system("PAUSE");
    return 0;
}

运行结果:

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

原文地址:https://www.cnblogs.com/super90/p/4983796.html