分享

C语言中的类型转换 -1L 1UL

 yyy2k3 2012-03-09

C语言中的类型转换   

      

                 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怎么办呢?办法是在定义的内部转换。也就是形参全部用intdouble型表示,然后在内部定义相应需要类型的局部变量,在内部来一个赋值的再转换,显然这种方法比较笨。但许多编译器为了与老版本兼容,还是采用的这种方式,在函数声明中,不强制要求写出参数列表,调用时也不作类型检查。然后在函数内部转换。当然,还一种方式就是不作任何类型检查,完全取决于程序员,这是最危险的方法。

    这些都取决于编译器。

            函数的返回值也要注意,如果没有显式的声明,则默认为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

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多