本文共 4624 字,大约阅读时间需要 15 分钟。
数据类型转换就是将数据从一种类型转换为另一种类型,数据可以是变量、数值、表达式等,数据类型的转换可以分为自动类型转换和强制类型转换。
C语言中常量的默情况:整形默认为有符号int;浮点型默认为double;如果整形常量大小超出int,默认自然就为long int;如果需要无符号整形常量的话就可以在常量后面加u或是U,如0u或是0U,同理可以在常量后面加ul或UL表示无符号长整形常量;而浮点型常量后缀只有f或F,l或L,没有u或U,因为浮点数一般都为有符号 。
L表示长整型、U表示无符号、F表示符点
自动类型转换:
自动类型转换就是编译器默认进行的类型转换,进行自动类型转换的情况一般如下:
1.当将一种类型的数据赋值给另一种类型的变量时,就会自动发送转换,赋值的时候会把右边的数据类型转换成左边的数据类型,但是这样可能导致数据失真或者精度降低,所以自动类型转换不一定是安全的。
float f = 100;int k = f;
就像这样,先把100转换为float类型,然后赋值给f;再把f转换为int类型,赋值给k。
2.在不同数据类型的混合运算中,编译器也会自动转换数据类型,通常是将参与运算的数据先转换为同一种类型,然后进行运算,进行转换的规则如下:
转换的图如下:
强制类型转换:
强制类型转换就是在写代码的时候强制转化数据类型的,强制转换的格式为:(type_name) expression
type_name
为新类型名称,为表达式。例如:
(float) a; //将变量 a 转换为 float 类型(int)(x+y); //把表达式 x+y 的结果转换为 int 整型(float) 100; //将数值 100(默认为int类型)转换为 float 类型
下面是一个需要强制类型转换的经典例子:
#includeint main(){ int sum = 103; //总数 int count = 7; //数目 double average; //平均数 average = (double) sum / count; printf("Average is %lf!\n", average); return 0;}
运行结果:
Average is 14.714286!
sum 和 count 都是 int 类型,如果不进行干预,那么的运算结果也是 int 类型,小数部分将被丢弃;虽然是 average 是 double 类型,可以接收小数部分,但是心有余力不足,小数部分提前就被“阉割”了,它只能接收到整数部分,这就导致除法运算的结果严重失真。sum / count
在这段代码中,有两点需要注意: ( )
的优先级高于/
,对于表达式(double) sum / count
,会先执行(double) sum
,将 sum 转换为 double 类型,然后再进行除法运算,这样运算结果也是 double 类型,能够保留小数部分。注意不要写作(double) (sum / count)
,这样写运算结果将是 3.000000,仍然不能保留小数部分。类型转换只是临时性的
无论是自动类型转换还是强制类型转换,都只是为了本次运算而进行的临时性转换,转换的结果也会保存到临时的内存空间,不会改变数据本来的类型或者值。请看下面的例子:
#includeint main(){ double total = 400.8; //总价 int count = 5; //数目 double unit; //单价 int total_int = (int)total; unit = total / count; printf("total=%lf, total_int=%d, unit=%lf\n", total, total_int, unit); return 0;}
运行结果:
注意看第 6 行代码,total 变量被转换成了 int 类型才赋值给 total_int 变量,而这种转换并未影响 total 变量本身的类型和值。如果 total 的值变了,那么 total 的输出结果将变为 400.000000;如果 total 的类型变了,那么 unit 的输出结果将变为 80.000000。在C语言中,有些类型既可以自动转换,也可以强制转换,例如 int 到 double,float 到 int 等;而有些类型只能强制转换,不能自动转换,例如以后将要学到的 void * 到 int *,int 到 char * 等。
可以自动进行的类型转换一般风险较低,不会对程序带来严重的后果,例如,int 到 double 没有什么缺点,float 到 int 顶多是数值失真。只能强制进行的类型转换一般风险较高,或者行为匪夷所思,例如,char * 到 int * 就是很奇怪的一种转换,这会导致取得的值也很奇怪,再如,int 到 char * 就是风险极高的一种转换,一般会导致程序崩溃。
判断下列程序的输出:
int main( void ){ int a[5]={ 1,2,3,4,5}; int *p=( int* )( &a+1 ); printf( "%d,%d", *(a+1), *(p-1) );}
结果输出:
2,5
首先,指针的声明格式如示:类型 * [类型限定符列表] 名称 [= 初始化器];
int *a;int k = 5;a = &k;
第一行中声明了一个指向int类型的指针a;第二行中声明了一个int型变量k;第三行中将指针a指向变量k;
或者也可以在声明指针的时候就将对象的值赋值给指针:
int k=5, *a=&k;
需要注意的是,虽然指针指向的变量可能是不同类型的,但是在内存中指针的空间大小都是一样的,比如32位计算机中指针的大小通常是4Byte(32bit)。
然后,指针变量的加减运算与其数据类型有关(数据类型大小以字节为单位),比如上面的a++;k的地址就会加4,因为k是int,一般占4Byte。就像数组中一样,元素都是顺序排列的,+1就指向下一个元素。
在定义数组的时候,需要给出数组名和数组长度,数组名可以认为是被转换为了一个指针,指向数组的首个元素。但是!!! 数组名的本意表示的是整个数组,也就是表示数组所有元素的集合,在使用过程中经常会转换为指向第0个元素的指针,实际上数组名和数组首地址并不是等价的。 在绝大多数表达式中,数组名代表指向第0个元素的指针,有两种情况例外:
所以在这里:
int *p = (int*)( &a+1 );
首先执行&a,得到一个指向数组a整体的指针,然后&a+1代表在数组整体的基础上地址加一(这里的加一表示的是下一个元素的地址,并不是实际内存+1Byte,反而是实际内存+4Byte,在32位情况下),所以&a+1在逻辑上相当于a[5],然后再通过( int* )强制转换为int类型指针,使p指针指向a[5]的地址(逻辑上)。
所以在后面*(p-1)中,p相当于&a[5],所以可以看作a[5-1],即a[4],答案为5
运行这段程序:
int main( void ){ int *p=0; p++; printf("p=%d\n",p); return 0;}
int *p=0;
是定义了一个指向0地址的int型指针
p++
代表指针指向下一个int型地址
int型数据占4Byte,所以下一个int型地址就是在上一个int地址的基础上+4
所以此时p指向的地址就是0+4
即打印出p=4
注意:指针的大小是1Byte,指针指向的地址是根据数据类型有所不同的
题目:
用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题 )?
分析:
一年有多少秒,365X24X60X60=31,536,000,但是如果运行的CPU是16位,16位无符号数的范围是0~65535,就会造成整型数据溢出,所以要将此数据声明为无符号长整型数据,即:
#define YEAR_TIME (365*24*60*60)UL
加上UL声明为无符号整型数据!
题目:
在 32位系统下,执行如下代码,运算结果是多少?
void func( char str[100]){ printf("%d",sizeof( str ) );}int main(void){ char str[] = "www.firebbs.cn"; char *p = str ; int n = 10; printf("%d,%d,%d,",sizeof (str ),sizeof ( p ) ,sizeof ( n ) ); func(str); return 0;}
我都输出:
14,4,4,100
实际答案:
15,4,4,4
分析:
char str[] = "www.firebbs.cn";
注意str是一个数组,而且声明的时候是一个字符串,所以字符串最后还有一个结束符’\0’,所以sizeof(str)=14+5=15,注意sizeof作用于数组名的时候,返回的是整个数组的大小
char *p = str ;
,p只是一个指向str首地址的指针,所有指针的大小都是32bit(32位系统下),所以sizeof§=4
int n = 10;
,32位系统下的int一般为四个字节,即sizeof(n)=4
void func( char str[100]){ printf("%d",sizeof( str ) );}
传入函数之后,会把str当作指针处理,不会在栈里开辟100Byte的空间的,所以这里的str相当于一个char型的指针,32位下所有指针大小都是4Byte,所以sizeof(str)=4
转载地址:http://tvwzi.baihongyu.com/