❀C语言数据类型专题❀

1. C语言为什么有数据类型

1.1 C语言中的基本类型

❀C语言数据类型专题❀_第1张图片

1.2 整型数据类型

1.3 进制问题

1.4 数据在内存中的存储方式

数据在不同的系统中存储方式也不同,分为小端存放、大端存放。其中小端存放指的是高位数在高地址,低位数在低地址。
那么如何检测大小端存放:

  • 其中一种方法就是采用联合体来进行存放,里面放入一个short、一个char,那么这个时候如果给这个联合体中存放一个值,然后分别取char和short的值,如果char中的值是short的低位数,那么就是小端,否则就是大端存放。代码如下:
union Node {
     
	short a;
	char ch[2];
};

int main() {
     

	Node x;
	x.a = 0x0001;
	if (x.ch[0] == 1) {
     
		//小端
	}
	else {
     
		//大端
	}
	return 0;
}
  • 另一种方式就是采用指针的方式来实现这一功能,定义一个short类型的数,定义一个char类型的指针,指向那个short类型数的地址,然后获取char中的值,如果这个char是short中的低地址,那么就代表这个是小端存放,否则就是大端存放。代码如下:
short sa = 0x0001;
char* sp = (char*)&sa;
if (*sp == 1) {
     
	//小端
}
else {
     
	//大端
}

1.5 计算机存储整型数据是以补码形式存储

问题:为什么要以补码形式存储呢?
因为计算机中处理器的运算形式只有一种加法,所有的运算都是通过加法来实现的,对于下面这个式子:
w = a - b
在CPU中其实是通过下面这个式子实现的
[x-y]补 = [x+(-y)]补 = [x]补 + [-y]补
举一个简单有趣的例子,对于下面这段代码,可以看出计算机是以补码的形式存储数据的:

for (char a = 0; a < 128; ++a) {
     
	printf("%4d", a);
}
printf("\n");

对于上面这段代码中,a是char类型的字符,所占空间为一字节,一共八个比特位,所以其正数所能表示的范围是:

0000 0000 = 0
0111 1111 = 127

这个时候继续执行for循环,a++,变成了下面这个数值:

1000 0000 = -128

所以循环会一直循环下去,并且按照下面这个规律一直循环下去:

对于char和signed char,会保留最高位的符号位,也就是a++,会

0 => 127 => -128 => -1 => 0 => ···

❀C语言数据类型专题❀_第2张图片
对于unsigned char,会认为最高位的数字不为符号位,为数值,按照下面这个流程实现
❀C语言数据类型专题❀_第3张图片

1.6 类型转换的本质

如下面这段代码中,将char类型的转换给无符号整型和有符号整型,其多余位是如何扩充的?

char a = 5;
char b = -5;
int x = a;
int y = b;
unsigned int z = b;
printf("x = %d y = %d z = %d", x, y, z);
printf("x = %x y = %x z = %x", x, y, z);

那么下面来讲解,类型转换中的数据转换规则扩充或者缩小规则:

  • 表示范围小的整型变量赋值给表示范围大的整型变量的扩充方式:

与其自身有关,如果这个小的整型变量为有符号数,那么对于多余的空余位置填补的是其符号位的值,如果是无符号数,那么扩充的是0。
对于上面那个程序,其运行结果为:
在这里插入图片描述
其原理如下图:
❀C语言数据类型专题❀_第4张图片

  • 表示范围大的转换为表示范围小的数的时候,需要根据字节数进行相应的截取,一般从低地址开始截取目标范围小的数的字节数,然后将其赋值给这个字节小的元素

其原理如下图所示:
❀C语言数据类型专题❀_第5张图片

2. 算术类型转换和赋值类型转换

2.1 类型相容

指的是虽然类型不同,但是系统却可以自动识别进行转换

  • 但是这种类型相容仅仅针对基本数据类型,包括:char,short,int,long int,long long,unsigned char,unsigned short,unsigned int,unsigned long int,unsigned long long,float,double类型。
  • 指针、数组、结构体类型、联合体类型、枚举类型,不具备此性质

2.2 不同类型数据的混合运算

  • 在运算过程中,当某个二元运算符两边的操作数类型不一样但是属于类型相容时,系统会将精度低的操作数变换到与另一个操作数精度相同,然后再进行运算
  • 赋值类型转换,当赋值号的左值和右值数据类型不一致但是符合数据相容时,由系统自动进行类型转换

数据类型转换有两种,隐式类型转换和显示类型转换

2.2.1 隐式转换的规则

❀C语言数据类型专题❀_第6张图片

从上面这个图可以看出,在进行一些运算的时候,C语言编译器会自动的对一些数据类型进行相应的隐式转换,使其扩展成int型,然后对int类型的数据进行操作,比如下面这段代码,就是很好的体现:

char a = -5;
unsigned char b = 10;
if (a > b) {
     
	printf("%d > %d \n", a, b);
}

上面的代码中对于a和b的比较,我们将其先转换为int类型的数据,然后对int类型的数据进行比较,那么就如同下面表示的那样:

a和b在比较的时候,需要转化为整型进行比较,而b又是无符号整型,所以在进行转换的时候a和b都化成了无符号整型
对于a,其是负数,在计算机中用补码存储,其原码为1000 0101,所以其反码为1111 1010,所以其补码为1111 1011,所以如果将a转化为整型的时候,其会扩充为4个字节,会自动填充符号位,为
1111 1111 1111 1111 1111 1111 1111 1011,最后将该数转换为无符号整型,即没有符号位
对于b,其是无符号char类型,在转换的时候自动填充0,也就是说其填充为:
0000 0000 0000 0000 0000 0000 0000 1010

由上面的分析可以明显看出,a在转换为无符号整型之后,其大小远远比b大,所以这里面是输出a>b

  • C语言编译器在转换类型的时候,会首先将要转换的值,变换成需要的类型,放到寄存器eax中,然后再将寄存器中的值,赋值给需要的数,也就是说下面这段代码是不能执行通过的,因为放到寄存器中之后,不能对寄存器中这个将亡值进行赋值:
char ch = 'a';
int x = 0;
x = ch;
(int)ch = x;
  • 上面这段代码是错误的,不能改变将亡值,但是下面这段代码是正确的
char ch = 'a';
int x = 0;
x = ch;
x = (int)ch;

隐式转换的例子

  • 示例一
char a = 100;
char b = 200;
char c = a + b;
printf("%d %d \n", c, a + b);

unsigned char x = 100;
unsigned char y = 200;
unsigned char z = x + y;
printf("%d %d \n", z, x, y);

上面式子运行结果为
在这里插入图片描述
那么为什么会出现这种现象呢?
可以通过下图来找到结果:
❀C语言数据类型专题❀_第7张图片

  • 示例二:
	char c = 128;
	unsigned char uc = 128;
	unsigned short us = 0;
	us = c + uc;
	printf("%x \n", us);


	us = (unsigned char)c + uc;
	printf("%x \n", us);

	us = c + (char)uc;
	printf("%x \n", us);
	us = (unsigned short)c + uc;
	printf("%x \n", us);

运行结果:
❀C语言数据类型专题❀_第8张图片
原理:
❀C语言数据类型专题❀_第9张图片

2.3 指针类型的转换

对于指针类型的转换,虽然指针都是四个字节,但是在其所指元素类型不一样的时候,需要对其进行强转,因为如果指针所指类型不一样,再转换的时候,内存默认给指针所指空间,分配原来所指类型大小,而加了强转之后,使得空间大小改变,如下面这段代码中指针的转换类似:

	int a = 0x41440000;
	int* ip = &a;
	
	float* fp = (float*)&a;
	printf("%x \n", *ip);
	printf("%f \n", *fp);

运行结果:
在这里插入图片描述

你可能感兴趣的:(C语言)