注册 登录
查看: 569|回复: 2

【转】C语言中的类型转换

[复制链接]
发表于 2014-2-18 22:29:18 | 显示全部楼层 |阅读模式
C语言中的类型转换有两种,自动与强制。

它们都有几种情况,如不同长度的转换;不同类型的转换;还有无符号与有符号数之间的转换。关键是两点,即长度不同时如何转换,在有无符号数参与时如何转换。

一般的处理方式是长变短时作一个简单的截短操作,内存的对齐方式影响这个结果。短的变长时,与符号有关,如果是有符号数,则有两种可能,符号扩展或简单地提升(即高位补0)。这个C标准没有定义,取决于编译器。所以,在将短的数据转换为长的数据时,最好是用强制的转换。无符号数就没关系了,结果是一样的。


  1.强制类型转换

        具体形式如下:

              (类型)表达式

        这个出错的机会比较小一点,因为由程序员自己控制。但要注意的,在对指针转换时,如果将一个

             指向一个较小内存单元的指针转换为一个指向较大内存单元的指针,就会破坏其它内存单元的数据。

             这个在自动转换中也有,故在下面一起描述。强制转换一般是在将一个空指针赋给一个在类型的指针

             时的操作,如在malloc()操作时。


2.自动类型转换

这是最容易出错的,因为C语言对类型的检查比较少,这样设计的好处是给程序员提供编程上的方便,但任何事情都有两面性,自动类型转换有不少副作用。我们先来看一下自动转换在什么时候发生:

1)表达式求值

2)赋值

3)函数调用

这几种转换的细节都可以参考《C程序设计语言》(The C Programming Language, Brian

W.Kernighan, Dennis M.Ritchie)




一是有无符号数参与的表达式计算

C语言中对这个没有规定,取决于实现。看下面这个例子:
  1. #include <stdio.h>


  2. int main(void)

  3. {
  4. long a;
  5. unsigned short b;
  6. unsigned long c, d;
  7. short e;

  8. a = -1L;
  9. b =   1U;
  10. d = a + b;
  11. printf("a = %dL, b = %uU, d = %uUL, a>b = %d\n",a, b, d, a > b);

  12. a = -1L;
  13. c = 1UL;
  14. d = c + a;
  15. printf("a = %dL, c = %uUL, d =%uUL, a>c = %d\n", a, c, d, a > c);

  16. e = -1;
  17. c =   1UL;
  18. d = e + c;

  19. printf("e = %d, c = %uUL, d =%uUL, e>c = %d\n", e, c, d, e> c);
  20. }

运行结果如下(在我的环境中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语言对类型的检查不那么严格,所以容易引起许多潜在的错误。

下面给一个例子说明一下:
  1. #include <stdio.h>

  2. typedef struct tag_data

  3. {
  4. char c1;
  5. char c2;
  6. char c3;
  7. char c4;
  8. }DATA, *PDATA;

  9. int main(void)
  10. {
  11. DATA adata = {-1, 'a', 'b', 'c'};
  12. int iret = -1;
  13. printf("Enter a charactor\n");
  14. iret = scanf("%d", &adata.c1);
  15. 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);
  16. printf("iadata.c1 = %d, adata.c2 = %d, adata.c3 = %d, adata.c4 = %d\n", adata.c1, adata.c2, adata.c3, adata.c4);
  17. return 0;
  18. }
运行结果如下:

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
  1. 本文出处:http://www.360doc.com/content/12/0309/11/3972394_192947983.shtml
回复

使用道具 举报

发表于 2014-2-19 08:31:32 | 显示全部楼层
都看晕了,好复杂啊
回复 支持 反对

使用道具 举报

发表于 2014-2-19 08:39:07 | 显示全部楼层
嗯  好帖子  很喜欢山外这个客户端型的论坛 大家很方便交流  呵呵
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回列表 返回顶部