c语言和c++语言中的类型转换

关于类型转换的问题,本来我并没有如何在意。因为一直都是写的c的代码比较多一点嘛,而c这方面的内容并不多,形式也很简单。所以一直以来都会有一点错觉,觉得这方面自己应该了解的差不多,够用了。偶尔写代码有些关于这部分的内容老是出错,我也都是尽量“绕”过去,没想过深究原因。这段时间的基础学习,真的是学到了很多,也让我意识到自己很多错误的观点。其中关于类型转换,我以前的理解确实是很肤浅的,直到现在我可能了解的也不是那么的到位,所以今天打算写一个这方面的博客,一是总结归纳,免得自己这几天刚刚有的一些了解又丢了;二是加深理解,写的时候也许会有一些收获;三是开启一个这方面的文章,以后如果我有了更深的理解,会在这篇文章里进行更正,希望使得此树之根须越扎越深。

类型转换主要可以笼统的分为两大类:隐式类型转换显式类型转换

注意:不论是隐式类型转换还是显式类型转换,在进行算术类型转换时,高精度转为低精度都会有精度损失,低精度转为高精度是没有。比如在有符号的char类型在与int类型进行转换时。将有符号char类型转换成int类型时,值是无损失的(不会发生变化的)。而int类型赋值给有符号char类型时其实进行的是截断式赋值。 例子:

1
2
3
4
5
6
signed char sc1 = -128;
//结果i1值为-128,将10000000自动扩展为111...10000000
int i1 = sc1;
int i2 = -1073741823;
//结果sc2值为1,发生了截断式赋值,将11000...00000001截断为00000001
signed char sc2 = i2;

隐式类型转换

隐式类型转换不需要程序员介入,是自动进行的。通常我们注意的比较多的换转换通常是算数类型的隐式类型转。 ## 算数类型的隐式类型转换 在表达式求值时,算数转换的规则定义了一套类型转换的层次,其中的大方向是将运算对象转换成最宽的类型。具体规则如下:

  1. 首先会进行整形提升,如果运算对象双方是bool、char、signed char、unsigned char、short、与unsigned short等可以不损失精度的放入int类型中,它们就会提升为int类型。如果它们中有不能用int类型无损存储的,比如unsigned int、long等类型,则会将较小的一方提升为精度较高一方的类型。

  2. 如果运算对象双方一个为无符号类型,一个为有符号类型,并且无符号类型不小于有符号类型,则将带符号的运算对象转换成无符号的。

  3. 如果带符号类型大于无符号类型,则结果需要依赖机器。如果此带符号类型可以无损存储此无符号类型,则将无符号类型转换成带符号类型。否则,结果反之。比如两个运算对象分别是longunsigned int类型,则怎么转换就需要依赖机器中long类型占用空间是否比int多。

总结:表达式求值在根据double>float>int之类的隐式类型转换后求出来的值在赋值时还会再进行一次隐式类型转换。例如:int c=5; char d='0'; char a = c + d;

其他隐式类型转换

  1. 数组名转成指针:例如int a[10]; int *p=a;中将数组名转成指针,但是当数组被用作decltype关键字的参数或者作为取地址符(&)、sizeof以及typeid等运算符的运算对象时,上述转换不会发生;

  2. 指针的转换:比如,第一、常量正数值0或者字面值常量nullptr能转换成任意指针类型;第二、指向任意非常量的指针能转换成void *;第三、指向任意对象的指针能转换成const void*;

  3. 转成bool类型(通常在判断表达式中);

  4. 转换成常量:允许将非常量类型赋值给常量;

显式类型转换

显式类型转换也叫作强制类型转换,除非必须,否则不建议使用。并且在这方面,c语言提供的强制类型转换是及其不完善的,c++对与强制类型转换比之要完善很多,不过依然不建议使用。

整数类型转换机器码操作

  1. 同位宽的无符号与有符号的整数类型进行转换时,内存中机器码形式不变,变得是机器码的解读方式,以补码形式进行解读还是无符号数形式进行解读。
  2. 在进行扩展位宽的整数类型转换时,无符号数进行零扩展,有符号数进行符号扩展。(这两种方式都能保证扩展后原值不变,原理可参考补码原理)
  3. 在进行截断位宽的整数类型转换时,采用截断式转换
  4. 同时进行无符号与有符号和不同位宽类型转换时,首先进行位宽变换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>
//#include <stdlib.h>

typedef unsigned char *byte_pointer;

void show_bytes(byte_pointer start, int len) {
int i;
for (i = 0; i < len; i++)
printf(" %.2x", start[i]);
printf("\n");
}

void test() {
/* $begin extend */
short sx = -12345; /* -12345 */
unsigned short usx = sx; /* 53191 */
int x = sx; /* -12345 */
unsigned ux = usx; /* 53191 */
unsigned uy = sx; /* Mystery! */

// 16位有符号数-12345与其机器码
printf("16位有符号数-12345与其机器码: %d:\t", sx);
show_bytes((byte_pointer) &sx, sizeof(short));

// 将16位有符号数-12345转换成16位无符号数后其值与机器码
printf("将16位有符号数-12345转换成16位无符号数后其值与机器码: %u:\t", usx);
show_bytes((byte_pointer) &usx, sizeof(unsigned short));

// 将16位有符号数-12345扩展成32位有符号数后其值与机器码
printf("将16位有符号数-12345扩展成32位有符号数后其值与机器码: %d:\t", x);
show_bytes((byte_pointer) &x, sizeof(int));

// 将16位无符号数53191扩展成32位无符号数后其值与机器码
printf("将16位无符号数53191扩展成32位无符号数后其值与机器码: %u:\t", ux);
show_bytes((byte_pointer) &ux, sizeof(unsigned));

// 将16位有符号数-12345转成32位无符号数后其值与机器码
printf("将16位有符号数-12345转成32位无符号数后其值与机器码: %u:\t", uy);
show_bytes((byte_pointer) &uy, sizeof(unsigned));
/* $end extend */
}

int main(){
test();
return 0;
}

运行结果如下:

1
2
3
4
5
6
xhy@ubuntu:~/c_learn/datatype_int_convert$ ./test 
16位有符号数-12345与其机器码: -12345: c7 cf
将16位有符号数-12345转换成16位无符号数后其值与机器码: 53191: c7 cf
将16位有符号数-12345扩展成32位有符号数后其值与机器码: -12345: c7 cf ff ff
将16位无符号数53191扩展成32位无符号数后其值与机器码: 53191: c7 cf 00 00
将16位有符号数-12345转成32位无符号数后其值与机器码: 4294954951: c7 cf ff ff

参考资料

  • [1] C++ Primer(第5版)
  • [2] 深入理解计算机系统(第三版)

c语言和c++语言中的类型转换
http://line.com/2018/09/08/2018-09-08-c-cpp-type-convert/
作者
Line
发布于
2018年9月8日
许可协议