C语言中数据类型转换“陷阱”

本文通过一个具体的C语言代码示例,深入解析了C语言中不同类型间的自动转换规则,特别是int到unsigned int的转换过程及背后的原因,并给出了详细的解释。

 

#include <stdio.h>

void foo(  )

        {

                unsigned int a=6;

                int b=-20;

 

                ( a+b>6 )?puts( ">6" ):puts( "<=6" );               

                printf( "%u/n",b );

                printf( "a+b= %u/n",a+b );  

        }

 

int main()

        {

                foo(  ) ;

                return 0;

 

        }

如果不看foo()中的两个printf的话,我也不会相信结果是输出了“>6”,真是自己掌握得不牢固,原来以为不用管那些太基本的东西(比如c默认的数据类型转换,此题出的恰恰就是这一点);

先看结果(我用的gcc4.4版本):

>6

4294967276

4294967282

究其原因就是在使用a+b的时候int-->unsigned int,-20会转换成4294967276,那为什么会是4294967276而不是别的那?

:-),有点意思了,朋友们应该还记得“溢出“这个概念吧,包括正溢和负溢,比如一个8bit的有符号数表示的数据范围是-128~127,当让这个数去等于大于127的时候会正溢,等于小于-128的时候回产生负溢,这时候的数据就像被放在一个环上一样,只能在-128~127之间,超过了会怎样?就像把int -20转成unsigned int,答案是:

产生负溢的时候:=环上的最大值-该数的绝对值   (在intel x86 cpu中int是32bit的,比如2^32=4294967296,然后4294967276=4294967296-|-20|,这就是问题的答案)

产生正溢的时候:=环上的最小值+该数的值

 

另外自己也找了一些关于c语言的数据类型的麼认转化:

http://blog.ednchina.com/sealove518/116361/message.aspx

● 字符型变量的值实质上是一个8位的整数值,因此取值范围一般是-128~127,char型变量也可以加修饰符unsigned,则unsigned char 型变量的取值范围是0~255(有些机器把char型当做unsighed char型对待, 取值范围总是0~255)。

● 如果一个运算符两边的运算数类型不同,先要将其转换为相同的类型,即较低类型转换为较高类型,然后再参加运算,转换规则如下图所示。

double ←── float 高

long

unsigned

int ←── char,short 低

● 图中横向箭头表示必须的转换,如两个float型数参加运算,虽然它们类型相同,但仍要先转成double型再进行运算,结果亦为double型。 纵向箭头表示当运算符两边的运算数为不同类型时的转换,如一个long 型数据与一个int型数据一起运算,需要先将int型数据转换为long型, 然后两者再进行运算,结果为long型。所有这些转换都是由系统自动进行的, 使用时你只需从中了解结果的类型即可。这些转换可以说是自动的,但然,C语言也提供了以显式的形式强制转换类型的机制。

● 当较低类型的数据转换为较高类型时,一般只是形式上有所改变, 而不影响数据的实质内容, 而较高类型的数据转换为较低类型时则可能有些数据丢失。

赋值中的类型转换

当赋值运算符两边的运算对象类型不同时,将要发生类型转换, 转换的规则是:把赋值运算符右侧表达式的类型转换为左侧变量的类型。具体的转换如下:

(1) 浮点型与整型 

● 将浮点数(单双精度)转换为整数时,将舍弃浮点数的小数部分, 只保留整数部分。

将整型值赋给浮点型变量,数值不变,只将形式改为浮点形式, 即小数点后带若干个0。注意:赋值时的类型转换实际上是强制的。

(2) 单、双精度浮点型 

● 由于C语言中的浮点值总是用双精度表示的,所以float 型数据只是在尾部加0延长为doub1e型数据参加运算,然后直接赋值。doub1e型数据转换为float型时,通过截尾数来实现,截断前要进行四舍五入操作。 

(3) char型与int型 

● int型数值赋给char型变量时,只保留其最低8位,高位部分舍弃。 

● chr型数值赋给int型变量时, 一些编译程序不管其值大小都作正数处理,而另一些编译程序在转换时,若char型数据值大于127,就作为负数处理。对于使用者来讲,如果原来char型数据取正值,转换后仍为正值;如果原来char型值可正可负,则转换后也仍然保持原值, 只是数据的内部表示形式有所不同。 

 

(4) int型与1ong型 

● long型数据赋给int型变量时,将低16位值送给int型变量,而将高16 位截断舍弃。(这里假定int型占两个字节)。 

将int型数据送给long型变量时,其外部值保持不变,而内部形式有所改变。 

(5) 无符号整数 

● 将一个unsigned型数据赋给一个占据同样长度存储单元的整型变量时(如:unsigned→int、unsigned long→long,unsigned short→short) ,原值照赋,内部的存储方式不变,但外部值却可能改变。

● 将一个非unsigned整型数据赋给长度相同的unsigned型变量时, 内部存储形式不变,但外部表示时总是无符号的。 

 

● 计算机中数据用补码表示,int型量最高位是符号位,为1时表示负值,为0时表示正值。如果一个无符号数的值小于32768则最高位为0,赋给 int型变量后、得到正值。如果无符号数大于等于32768,则最高位为1, 赋给整型变量后就得到一个负整数值。反之,当一个负整数赋给unsigned 型变量时,得到的无符号值是一个大于32768的值。 

● C语言这种赋值时的类型转换形式可能会使人感到不精密和不严格,因为不管表达式的值怎样,系统都自动将其转为赋值运算符左部变量的类型。 

● 而转变后数据可能有所不同,在不加注意时就可能带来错误。 这确实是个缺点,也遭到许多人们批评。但不应忘记的是:c面言最初是为了替代汇编语言而设计的,所以类型变换比较随意。当然, 用强制类型转换是一个好习惯,这样,至少从程序上可以看出想干什么。 

 youkuaiyun.com//js/topnav.htmlDhttp

 

<think>嗯,我现在要理解C语言中的隐式类型转换。首先,我记得类型转换有两种:显式和隐式。隐式就是编译器自动进行的,不需要程序员手动写代码对吧。但具体是怎么转换的呢? 首先,可能是在不同数据类型混合运算的时候会发生隐式转换。比如,如果有一个int和一个float相加,会发生什么?应该是把int转成float,然后相加,结果是float类型。但是为什么是int转float而不是反过来呢?可能跟数据类型的优先级有关?比如,像C语言里有一个类型等级,比如char < short < int < long < float < double这样的顺序,当两个不同类型的数据运算时,低等级的类型会被提升到高等级的类型。这样就能保持精度,避免数据丢失。 那具体转换的规则是什么?比如算术运算中的转换规则。比如,如果两个操作数类型不同,但都属于整数类型,或者一个是浮点数一个是整数,这时候怎么处理?比如,当short和int在一起运算时,short会被转换成int,对吗?因为int的等级更高。那如果是int和unsigned int呢?这个时候可能要看它们的等级和符号,比如如果一个是有符号,另一个是无符号,可能会把有符号的转换成无符号的,这样可能导致一些意外的值,特别是当有符号数是负数的时候。这部分可能需要特别注意。 还有在赋值的时候会发生隐式类型转换。比如,把float赋值给int,这时候会发生截断,小数部分被丢弃。而如果反过来,把int赋值给float,可能会损失精度,特别是当int的值很大时,超过float能精确表示的范围。这时候可能就会有精度问题。 函数调用的时候也会有隐式转换吗?比如,如果函数参数声明是double,但传入的是int,这时候int会被转换成double。不过,如果参数类型不匹配又没有原型声明的话可能会有问题,比如旧的C风格函数声明可能导致不同的行为。比如,K&R C中可能不会检查参数类型,导致错误。但C99之后应该更严格了? 还有一个情况是条件表达式中的转换,比如在if语句中,非布尔类型的表达式会被转换成布尔值。比如,0转换成false,非0转换成true。但C语言里其实没有布尔类型,直到C99引入了_Bool,但传统的做法是用整型来表示,所以这里的隐式转换其实就是判断是否为0。 还有在switch语句中的case标签,表达式会被转换成switch表达式的类型吗?比如,如果switch是int类型,case标签中的char会被提升为int,这应该也是隐式转换的一种情况。 那隐式转换可能带来问题是什么呢?比如,当高精度类型向低精度类型转换时可能的数据丢失,或者符号转换导致的数值变化。例如,把有符号的int转换成无符号的int时,如果原来的int是负数,就会变成一个很大的正数,这可能引发逻辑错误。另外,浮点数转整数时的截断问题,比如3.9转成int就是3,这可能不是用户期望的结果。 还有,不同类型的指针之间赋值是否会有隐式转换?比如,void指针可以隐式转换为其他类型的指针吗?在C语言中,将void*赋值给其他类型的指针不需要显式转换,但C++需要。所以,在C中,比如int *p = malloc(sizeof(int)*10); 这里malloc返回void*,可以隐式转换成int*,这在C中是允许的。但如果在C++中就需要强制转换,所以这可能也是隐式转换的一个例子。 那在类型提升中的情况,比如整数提升(integer promotion)。比如,在表达式中,如果类型比int小(比如char、short),会被提升为int或者unsigned int,然后再参与运算。比如,两个char相加,会被提升为int,运算结果是int类型。这可能是因为int被认为是机器处理起来最高效的类型,或者足够大以容纳所有可能的运算结果,避免溢出? 不过,如果原来的类型是unsigned char,那么提升的时候是提升为int还是unsigned int呢?这取决于int能否表示unsigned char的所有值。比如,在大多数系统中,unsigned char是0~255,而int通常是32位的,可以表示这个范围,所以unsigned char会被提升为int。但如果int的范围不够(比如在一些嵌入式系统中,int是16位),那么可能提升为unsigned int? 这部分可能需要更仔细地查证。比如,C标准中的整数提升规则。根据C标准,整数提升是将比int等级低的类型转换为int或者unsigned int。具体来说,如果原类型的所有值都能用int表示,则提升为int,否则提升为unsigned int。例如,unsigned char在int足够大的情况下会被提升为int,否则可能提升为unsigned int。不过,在大多数现代系统中,int足够大,所以unsigned char会被提升为int。 还有,在运算时,比如如果两个操作数都是带符号的,但类型不同,如int和long,那么int会被转换为long。而如果一个带符号,一个无符号,这时候的转换规则可能比较复杂。例如,如果int和unsigned int比较,int会被转换为unsigned int,这可能带来负数变成大正数的问题,导致比较结果不符合预期。 举个例子,假设有一个int a = -1,unsigned int b = 0。当比较a < b时,a会被转换为unsigned int,即变成UINT_MAX,那么比较结果会是0(即false),但可能用户原本预期的是-1 < 0,结果应该是true。这就会导致逻辑错误。 所以,隐式类型转换虽然方便,但也容易引入难以察觉的bug,特别是在涉及无符号和有符号类型混合运算的时候。 另外,在函数返回类型不匹配时是否会有隐式转换?例如,函数声明返回int,但实际返回一个float,这时候会隐式转换成int吗?是的,这种情况下,返回值会被隐式转换为声明的返回类型。但这样的做法容易出错,最好避免。 总结一下,隐式类型转换主要发生在以下几种情况: 1. 算术运算中的操作数类型不同,转换为更高级别的类型。 2. 赋值时,右值类型转换为左值类型。 3. 函数调用时,实参类型转换为形参类型。 4. 条件表达式转换为布尔类型。 5. 整数提升,在表达式中提升较小的整数类型到int或unsigned int。 需要注意的问题包括数据精度丢失、符号转换错误、意外提升导致的行为变化等。为了避免这些问题,应该尽量避免混合类型运算,必要时使用显式类型转换,并注意数据类型的范围和符号。</think>在C语言中,隐式类型转换是编译器自动执行的数据类型转换,无需程序员显式干预。以下是逐步说明: --- ### **一、隐式转换的场景** 1. **算术运算中的类型提升** 当不同数据类型的操作数参与运算时,低等级类型会被转换为高等级类型,以保留精度。 **等级顺序**:`char → short → int → unsigned int → long → unsigned long → float → double → long double` **示例**: ```c int a = 5; float b = 3.14; float c = a + b; // a被隐式转换float ``` 2. **赋值时的类型转换** 右侧表达式的类型会被转换为左侧变量的类型。 **示例**: ```c int x = 10.5; // 10.5被截断为int类型,x = 10 ``` 3. **函数调用中的参数传递** 实参与形参类型不匹配时,实参会被转换为形参类型。 **示例**: ```c void func(double d) { /* ... */ } func(5); // int类型的5被转换为double ``` 4. **条件表达式** 非布尔值(如整数)会被隐式转换为0(假)或非0(真)。 **示例**: ```c if (5) { /* 条件为真 */ } ``` 5. **整数提升(Integer Promotion)** 比`int`小的整型(如`char`、`short`)会被提升为`int`或`unsigned int`。 **示例**: ```c char c1 = 100, c2 = 20; int result = c1 + c2; // c1和c2被提升为int后再相加 ``` --- ### **二、转换规则详解** 1. **混合符号类型的运算** - 若一个操作数为有符号,另一个为无符号,则有符号数会被转换为无符号类型。 **风险**:负数可能变为极大正数。 **示例**: ```c int a = -1; unsigned int b = 0; if (a < b) { /* 不会执行,因为a被转为UINT_MAX */ } ``` 2. **浮点与整数的转换** - 浮点数转整数:直接截断小数部分。 - 整数转浮点数:可能损失精度(如大整数转为`float`)。 **示例**: ```c int i = 123456789; float f = i; // 可能丢失精度 ``` 3. **指针类型转换** - `void*`可隐式转换为任意指针类型(仅在C语言中)。 **示例**: ```c int *p = malloc(sizeof(int) * 10); // void*隐式转为int* ``` --- ### **三、常见问题与风险** 1. **数据截断与精度丢失** ```c double d = 3.14159; int i = d; // i = 3,小数部分丢失 ``` 2. **符号错误(有符号 ↔ 无符号)** ```c unsigned int u = 10; int s = -5; if (s < u) { /* 结果为false,s被转为极大正数 */ } ``` 3. **整数提升的意外行为** ```c char c = 255; // 假设char为8位有符号,c = -1 int i = c; // i = -1(而非255) ``` --- ### **四、最佳实践** 1. **避免混合类型运算**,尽量统一操作数类型。 2. **显式类型转换**:用`(type)`明确意图,减少歧义。 ```c double d = (double)10 / 3; // 显式转换为浮点除法 ``` 3. **注意无符号类型的陷阱**,尤其在循环条件中。 --- 通过理解隐式类型转换的规则和潜在风险,可以有效避免程序中的隐蔽错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值