评论

收藏

[C++] 光天化日学C语言(12)- 类型转换 | 今天你打卡了吗?(建议收藏)

编程语言 编程语言 发布于:2021-07-02 17:42 | 阅读数:470 | 评论:0

  一、前言
    本文作者是从 2007 年开始学 C语言 的,不久又接触了C++,基本就是 C/C++ 技术栈写了 14 年的样子,不算精通,但也算差强人意。著有《夜深人静写算法》系列,且承诺会持续更新,直到所有算法都学完。主要专攻 高中 OI 、大学 ACM、 职场 LeetCode 的全领域算法。由于文章中采用 C/C++ 的语法,于是就有不少读者朋友反馈语言层面就被劝退了,更何况是算法。
  于是,2021 年 06 月 12 日,《光天化日学C语言》 应运而生。这个系列文章主要服务于高中生、大学生以及职场上想入坑C语言的志同道合之人,希望能给祖国引入更多编程方面的人才,并且让自己的青春不留遗憾!
  这一章的主要内容是类型转换。
DSC0000.gif
二、人物简介

  • 第一位登场的就是今后会一直教我们C语言的老师 —— 光天。
    DSC0001.gif
  • 第二位登场的则是今后会和大家一起学习C语言的没什么资质的小白程序猿 —— 化日。
    DSC0002.gif
三、概念
DSC0003.png


  • 类型转换 就是将数据(即 变量、数值、表达式 等的结果)从一种类型转换成另一种类型。
  • 今天的章节主要围绕以下内容展开:
    DSC0004.png
四、类型转换
1、自动类型转换
DSC0005.png


  • 这个过程不需要写代码的人干预,会自动发生。
  • 自动类型转换主要发生在两个时机:赋值 和 运算。
1)赋值

  • 将一种类型的数据赋值给另外一种类型的变量时会发生自动类型转换,如下代码所示:
#include <stdio.h>
int main() {
  float love = 520;
  return 0;
}

  • 这里的520原本是int类型的数据,为了赋值给love,他需要转换成float类型。
  • 再来看另一个例子:
#include <stdio.h>
int main() {
  int loveyou = 11 / 9;
  return 0;
}

  • love / 9的值明显不是一个整数,但是它需要赋值给int,所以需要先转换为int类型以后,才能赋值给变量loveyou。
  • 由于在赋值运算中,赋值号两边的数据类型不同时,需要把右边数据的类型转换为左边变量的类型,这可能会导致数据失真,或者精度降低(例如上面例子中所说的浮点数转整数,就会截掉小数部分)。
2)运算

  • 在不同类型的混合运算中,编译器也会自动地转换数据类型,将参与运算的所有数据先转换为同一种类型,然后再进行计算。转换的规则如下:
DSC0006.png

  转换原则如下:
  1)数据长度短的向输出长度长的进行转换;
  2)精度低的向精度高的进行转换;


  • 注意,所有的浮点运算都是以双精度进行的,即使运算中只有float类型,也要先转换为double类型,才能进行运算。
  • 当char和short参与运算时,必须先转换成int类型。
  • 来看一个计算圆周长的例子:
#include <stdio.h>
#include <math.h>
const float PI = acos(-1.0);  // 3.1415926535...
int main(){
  int c1, r = 10;
  double c2;
  c1 = 2 * PI * r;
  c2 = 2 * PI * r;
  printf("c1=%d, c2=%lf\n", c1, c2);
  return 0;
}

  • 输出结果为:
c1=62, c2=62.831855

  • 上述例子中,c1是int类型,c2是double类型,赋值号右边的内容是计算圆的周长,完全相同,但是就是由于被赋值的变量类型不同,从而导致运算结果截然不同。
  • 虽然表达式的结果都是double类型。但由于c1为int类型,所以赋值运算的结果仍为int类型,舍去了小数部分,导致数据失真。

二、强制类型转换

  • 自动类型转换是编译器根据代码的上下文环境自行判断的,有时候并不是那么智能,不能满足所有的需求。所以有时候需要写代码的人,也就是程序员能够自己在代码中明确地提出要进行类型转换,这就是强制类型转换。
DSC0007.png

1)强制类型转换的格式
(type_name) expression

  • 其中 type_name 为新类型名称,expression为需要进行强制类型转换的表达式。
2)64位整数强转

  • 让我们来看个非常容易犯错的例子:
#include <stdio.h>
int main(){
  long long x = 1 << 32;
  printf("%lld\n", x); 
  return 0;
}

  • 我们想要干的事情,就是计算 2 32 2^{32} 232 并且存到变量x中。
    DSC0008.gif
  • 然而,这个程序的输出结果为:
0

  • 回想一下,我们 光天化日学C语言(03)- 变量 这一节中学到的,整数的范围最大不会超过 2 32 − 1 2^{32}-1 232−1,所以这里显然是超了。
  • 更加具体的原因,这里的1是int类型,所以进行左移32位时,产生了溢出,所以变成了0,这里涉及到补码相关的知识,我会在后续章节详细进行讲解。
  • 所以,我们需要先把 1 强制转换成long long再进行左移运算,如下:
#include <stdio.h>
int main(){
  long long x = (long long)1 << 32;
  printf("%lld\n", x); 
  return 0;
}

  • 得到的结果为:
4294967296

  • 是我们期望的结果,即 2 32 2^{32} 232。
3)浮点数强转

  • 另一个比较经典的例子,就是我们计算除法的时候,如下:
#include <stdio.h>
int main(){
  int a = 10;
  int b = 3; 
  double c = a / b;
  printf("%lf\n", c); 
  return 0;
}

  • 得到的结果为:
3.000000

  • 原因是因为a和b都是int类型,如果不进行干预,那么a / b的运算结果也是int类型,小数部分将被丢弃;虽然是c是double类型,可以接收小数部分,但是在赋值之前,小数部分提前就被舍弃了,它只能接收到整数部分,这就导致除法运算的结果失真。
  • 修改方案如下:
#include <stdio.h>
int main(){
  int a = 10;
  int b = 3; 
  double c = (double) a / b;
  printf("%lf\n", c); 
  return 0;
}

  • 我们只需要将a或b其中之一转换成double,然后再参与运算即可。
  • 当然,我们还可以这么写:
#include <stdio.h>
int main(){
  int a = 10;
  int b = 3; 
  double c = (a + 0.0) / b;
  printf("%lf\n", c); 
  return 0;
}

  • 或者这么写:
#include <stdio.h>
int main(){
  int a = 10;
  int b = 3; 
  double c = (a * 1.0) / b;
  printf("%lf\n", c); 
  return 0;
}

  • 核心就是:不改变原有表达式的值,在其中添加一些double类型的数,使得整个表达式转换成double。
  • 使用强制类型转换时,有时候可能不是编译器想要的那样,因为这是写代码的人自己的行为,所以程序员自己要意识到其中潜在的风险。
  • 比如将指针转换成整型,或者将double转换成指针,当然,有些强制转换可能直接导致程序崩溃。
DSC0009.gif

  通过这一章,我们学会了:
  1)类型转换;
  2)自动类型转换;
  3)强制类型转换;


  • 希望对你有帮助哦 ~ 祝大家早日成为 C 语言大神!
课后习题
DSC00010.gif


  • 【第10题】给定 n 和 n 个正整数,输出它们的平均数
  • 【第12题】给定 r,求以 r 为半径的圆的周长和面积


  
关注下面的标签,发现更多相似文章