C语言每日一题(二)

9.1 数据类型转换

数据类型转换就是将数据从一种类型转换为另一种类型,数据可以是变量、数值、表达式等,数据类型的转换可以分为自动类型转换和强制类型转换。

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.在不同数据类型的混合运算中,编译器也会自动转换数据类型,通常是将参与运算的数据先转换为同一种类型,然后进行运算,进行转换的规则如下:

  • 按照数据长度增加的方向进行,以确保不丢失数据,比如int和long int参与的运算,会先把数据都转换为long int 类型的数据,然后进行运算。
  • 所有的浮点运算都是按照双精度(double)进行的,即使运算中有float类型数据也要先转换为double,因为浮点数的默认类型是double。
  • 在char与short 参与运算时,先要转换为int类型。

转换的图如下:
在这里插入图片描述

强制类型转换:
强制类型转换就是在写代码的时候强制转化数据类型的,强制转换的格式为:

(type_name) expression

type_name为新类型名称,为表达式。例如:

(float) a;  //将变量 a 转换为 float 类型
(int)(x+y);  //把表达式 x+y 的结果转换为 int 整型
(float) 100;  //将数值 100(默认为int类型)转换为 float 类型

下面是一个需要强制类型转换的经典例子:

#include <stdio.h>
int 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,仍然不能保留小数部分。

类型转换只是临时性的

无论是自动类型转换还是强制类型转换,都只是为了本次运算而进行的临时性转换,转换的结果也会保存到临时的内存空间,不会改变数据本来的类型或者值。请看下面的例子:

#include <stdio.h>
int 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 * 就是风险极高的一种转换,一般会导致程序崩溃。

9.2 指向数组的指针

判断下列程序的输出:

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个元素的指针,有两种情况例外:

  • sizeof返回整个数组所占用的字节,而不是数组第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

9.3 指针加法

运行这段程序:

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,指针指向的地址是根据数据类型有所不同的

9.4 预处理器(Preprocessor)

题目:

用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题 )?

分析:

一年有多少秒,365X24X60X60=31,536,000,但是如果运行的CPU是16位,16位无符号数的范围是0~65535,就会造成整型数据溢出,所以要将此数据声明为无符号长整型数据,即:

#define YEAR_TIME (365*24*60*60)UL

加上UL声明为无符号整型数据!

9.5 数组作为参数的sizeof

题目:

在 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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值