博士
最后登录1970-1-1
在线时间 小时
注册时间2013-3-25
|
C语言中的类型转换有两种,自动与强制。
它们都有几种情况,如不同长度的转换;不同类型的转换;还有无符号与有符号数之间的转换。关键是两点,即长度不同时如何转换,在有无符号数参与时如何转换。
一般的处理方式是长变短时作一个简单的截短操作,内存的对齐方式影响这个结果。短的变长时,与符号有关,如果是有符号数,则有两种可能,符号扩展或简单地提升(即高位补0)。这个C标准没有定义,取决于编译器。所以,在将短的数据转换为长的数据时,最好是用强制的转换。无符号数就没关系了,结果是一样的。
1.强制类型转换
具体形式如下:
(类型)表达式
这个出错的机会比较小一点,因为由程序员自己控制。但要注意的,在对指针转换时,如果将一个
指向一个较小内存单元的指针转换为一个指向较大内存单元的指针,就会破坏其它内存单元的数据。
这个在自动转换中也有,故在下面一起描述。强制转换一般是在将一个空指针赋给一个在类型的指针
时的操作,如在malloc()操作时。
2.自动类型转换
这是最容易出错的,因为C语言对类型的检查比较少,这样设计的好处是给程序员提供编程上的方便,但任何事情都有两面性,自动类型转换有不少副作用。我们先来看一下自动转换在什么时候发生:
1)表达式求值
2)赋值
3)函数调用
这几种转换的细节都可以参考《C程序设计语言》(The C Programming Language, Brian
W.Kernighan, Dennis M.Ritchie)
一是有无符号数参与的表达式计算
C语言中对这个没有规定,取决于实现。看下面这个例子:- #include <stdio.h>
- int main(void)
- {
- long a;
- unsigned short b;
- unsigned long c, d;
- short e;
- a = -1L;
- b = 1U;
- d = a + b;
- printf("a = %dL, b = %uU, d = %uUL, a>b = %d\n",a, b, d, a > b);
- a = -1L;
- c = 1UL;
- d = c + a;
- printf("a = %dL, c = %uUL, d =%uUL, a>c = %d\n", a, c, d, a > c);
- e = -1;
- c = 1UL;
- d = e + c;
- printf("e = %d, c = %uUL, d =%uUL, e>c = %d\n", e, c, d, e> c);
- }
复制代码 运行结果如下(在我的环境中compaq Tru64, cc)
a = -1L, b = 1U, d = 0UL, a>b = 0
a = -1L, c = 1UL, d =0UL, a>c = 1
e = -1, c = 1UL, d =0UL, e>c = 1
我们不难发现,在比较操作中,将无符号的短整数扩展为了有符号的长整型。所以有-1L < 1U;又将有符号的短整数提升为了无符号的长整型,所以有-1 > 1UL;还将相同长度的两个数的有符号的长整数转换为无符号的长整数,所以有-1L > 1UL。所以,这里的规则似乎是在类型长短不一时,以较长的为准,长度相同时,有符号的转化为无符号的,但也仅仅是比较操作,其它呢?还是要看实现。在加法操作中,不管数据的长短,一律作为有符号数计算,实际上有符号有无符号的加减法结果是一样的,这里一样指的是操作后变量在内存中的二进制串是一样的,只是它作为有符号数还是无符号数展示而已。所以上例中d始终是0。
为了证明在不同的环境下结果不同,我又在windows下,用Turbo C运行了一下,结果如下:
a = -1L, b = 65535U, d = 1UL, a>b = 0
a = -1L, c = 65535UL, d =1UL, a>c = 0
e = -1, c = 1UL, d =0UL, e>c = 0
这就意味着你编程时要特别注意,在使用前要测试一下你的环境对这个是如何处理的,也就意味你的代码是不可移植的。所以一条很重要的编程规则就是,尽量避免使用无符号数。
二是在函数的调用中的自动类型转换。
C语言中,在将实参传给函数时,如果类型不匹配,会进行自动类型转换。如果在函数声明时没有给出参数列表,则C编译器会认为不知道参数是什么,不进行类型检查,这样可能会导致错误的函数调用。C语言的类型检查也不是太严格,甚至警告也不会给就自动转换了。无参数的则要么提升,要不就原样拷贝。这里,原样拷贝比提升安全,因为有类型检查,所以在函数内部没有转换。
在ANSI C标准之前,处理比较复杂,声明不能有形参,要是一个空列表,而且,调用时,会将单精度转换为双精度的,将short, char转换为int。那这样就有一个问题了,如果函数确实需要一个char怎么办呢?办法是在定义的内部转换。也就是形参全部用int或double型表示,然后在内部定义相应需要类型的局部变量,在内部来一个赋值的再转换,显然这种方法比较笨。但许多编译器为了与老版本兼容,还是采用的这种方式,在函数声明中,不强制要求写出参数列表,调用时也不作类型检查。然后在函数内部转换。当然,还一种方式就是不作任何类型检查,完全取决于程序员,这是最危险的方法。
这些都取决于编译器。
函数的返回值也要注意,如果没有显式的声明,则默认为int,对某些调用会出错。因为调用程序会按照默认的类型来取返回值,这样,如果返回值的类型不是这样,就极有可能得到一个错误的结果。返回值的存储地点由编译器决定,一般通过寄存器来实现。
这些问题本质上都是由于类型的转换引起的,C语言对类型的检查不那么严格,所以容易引起许多潜在的错误。
下面给一个例子说明一下:- #include <stdio.h>
- typedef struct tag_data
- {
- char c1;
- char c2;
- char c3;
- char c4;
- }DATA, *PDATA;
- int main(void)
- {
- DATA adata = {-1, 'a', 'b', 'c'};
- int iret = -1;
- printf("Enter a charactor\n");
- iret = scanf("%d", &adata.c1);
- printf("iret = %d, adata.c1 = %c, adata.c2 = %c, adata.c3 = %c, adata.c4 = %c\n", iret, adata.c1, adata.c2, adata.c3, adata.c4);
- printf("iadata.c1 = %d, adata.c2 = %d, adata.c3 = %d, adata.c4 = %d\n", adata.c1, adata.c2, adata.c3, adata.c4);
- return 0;
- }
复制代码 运行结果如下:
Enter a charactor
a
iret = 0, adata.c1 = , adata.c2 = a, adata.c3 = b, adata.c4 = c
adata.c1 = -1, adata.c2 = 97, adata.c3 = 98, adata.c4 = 99
再一次运行如下:
Enter a charactor
2048
iret = 1, adata.c1 = , adata.c2 =, adata.c3 = , adata.c4 =
adata.c1 = 0, adata.c2 = 8, adata.c3 = 0, adata.c4 = 0- 本文出处:http://www.360doc.com/content/12/0309/11/3972394_192947983.shtml
复制代码 |
|