C语言强制类型转换

C语言中的类型转换   

      

                 C语言中的类型转换有两种,自动与强制。

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

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

 

  1.强制类型转换

        具体形式如下:

              (类型)表达式

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

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

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

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

 

2.自动类型转换

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

1)表达式求值

2)赋值

3)函数调用

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

W.Kernighan, Dennis M.Ritchie) 

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

C语言中对这个没有规定,取决于实现。看下面这个例子:

#include

 

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

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

 我们看到,第一次运行,结果与我们设想的不一样,从scanf的返回值可以看到,它运行出错了,因为它需要一个整型!所以给它一个字符型变量,它就报错返回了。第二次运行,结构体中的c2,c3,c4全部改变了,这是为什么?因为在scanf中,我们使用的%d,这样,scanf在读到%d的时候,它就判定,与此对应的参数为一个指向整型的指针,它在读到一个数后会将它存到这个指向所指向的区域之中,我们知道,32位的机器上整型占四个字节,所以读入的这个数就会存放在这个指针所指向的地址起的四个连续字节中 ,而恰好这四个字节编译器分配给了结构体变量adata,所以其它的数据就被破坏了。第一次运行时没有写内存就出错返回了,所以并没有改写,这也是为什么c2,c3,c4的输出还是原来所赋的初始值的原因。这里实际上就是把一个指向小内存单元的指针给了一个需要大内存单元的对象时产生的后果。至于这个指针破坏那些数据,要看它附近的内存空间分配给了哪些变量,而这个又是与机器的体系结构,编译器的实现有关的。

        最后要说明的是,不同的编译器与环境对这个处理也是不同的。如在Trubo C中,运行如下:

Enter a charactor
2048
iret = 1, adata.c1 = , adata.c2 =, adata.c3 = b, adata.c4 = c
adata.c1 = 0, adata.c2 = 8, adata.c3 = 98, adata.c4 = 99

这是因为它把int当成2个字节,虽然在32位的机器上,windows平台下。

所以,我们总结一下,要保证类型安全我们应该注意什么

1)要有类型安全的意识

编程时要时刻警惕,否则一不小心,出了问题想半天也找不出问题出在哪里。

2)避免将长类型的变量赋给短类型的变量

    这样会造成由于短类型的容量有限造成信息丢失。

3)避免将小内存的指针赋给一个需要大内存指针的对象

    原因上面说了。

4)尽量避免使用无符号数。


       这个不是绝对的,一般它在如下情况中使用,将一个有符号的短类型转换为长类型时,先强制转换该数据为无符号的,再进行提升,将之转换为有符号的类型。如char转换为int型时,先用类型(unsigned char)强制转换一下,再转换为整型。

5)记住没有无符号数时的自动转换规则

转载于:https://my.oschina.net/mavericsoung/blog/123130

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值