C语言:变量的深入理解

一.什么是变量

C语言中为什么要有类型?

本质是对内存的空间合理划分,按需索取。int就是在内存开辟4个子节的空间,不会多开辟,也不会少开辟。

C语言中的类型为什么有这么多种呢?

因为应用场景不同,解决应用场景对应的计算方式也是不同的,需要空间的大小同样也是不同的。就行定义身高,你就是要用浮点型。定义年龄你只会用int型,而不是用20.1来代表你的年龄。
本质就是用最小的成本来解决各种多样化的问题。

定义变量的本质

在内存中开辟特定的大小的空间,来保存数据。

为什么需要定义变量

换句话说,为什么需要变量,因为有数据需要保存起来,等待后续的处理,如果不定义变量来保存,之后在用的时候可能就没了。

定义变量的本质

本质就是在内存中开辟一个空间用来保存数据。但是为什么一定要在内存中开辟呢?因为定义变量也是程序逻辑的一部分,在定义的时候,写的程序已经被加载到内存中了(程序在被加载之前都存在硬盘当中)。

定义变量时的规则

以下讲的都是比较推荐的写法,当然你要怎么定义就怎么定义。

  1. 用最短的名字传递最多的信息。
  2. 全局变量名字前最好加一个g_。
  3. 宏定义,只读变量,枚举常量定义的字母要全部大写,单词之间要用_来隔开。
  4. 定义变量要初始化。

这里稍微讲一下为什么要初始化,在任何函数包括主函数在程序运行时都会经过压栈,才能给函数在内存中开辟好空间,但不是所有编译器在开辟好空间后都会给你初始化成0,也就是说如果在里面定义变量又不初始化的时候,那些变量的值可能就是随机值。而后来你又不小心用到这些没有初始化的变量就会出错。所以不管你用不用这个变量,一定要先初始化。

二.深刻理解signed/unsigned定义的变量

signed/unsigned一般修饰的都是整型。

1.运算时的符号位

如果这里有一个有符号的两个变量,在计算的时候符号位要不要进行计算?

直接说结论:要。

2.数据的存储情况

先看这样的一个代码:

int main()
{
	unsigned int a = -10;
	printf("%d\n", a);
	printf("%u\n", a);

	return 0;
}

这代码的结果:
在这里插入图片描述

为什么会有如此大的反差呢?
接下来我一步一步分析。

先看-10是如何存的。

unsigned int只是为10开辟了一块4字节的空间:
在这里插入图片描述

但是-10在存之前是不看a是什么类型的。也就是说-10在存之前已经转换成了对应的二进制补码:
在这里插入图片描述
假如a这个变量的类型是char类型的,存进来后就会发生截断。

以上就是-10如何存到内存当中的,主要记住的一个点就是:数据是直接转化成对应的二进制补码之后才开始存到内存中的。

然后再看如何取的:

	printf("%d\n", a);
	printf("%u\n", a);

-10已经存到对应的变量里的,现在-10默认的是unsigned int类型的。入乡随俗嘛,你进到a这个变量里了,你就是和a类型一样了。但是这也只是默认的。

那%d,%u是什么意思呢?就是你在取得时候也不会考虑a这个变量是什么类型的,我用%d的方式来取,我就当你a里面存的就是有符号类型的值。现在读我就当里面的二进制数:
11111111 11111111 11111111 11110110是一个负数的补码。所以在打印的时候把最高位当成符号位然后转化成原码打印出来。

%u也是一样,它也不管a是什么类型,就当里面存的二进制数是一个无符号整数。所以直接把上面那个二进制数转成10进制数后打印出来也就是我们上面看到的那个很大的数。

小总结一下
数据在存的时候不看要存的那个变量是什么类型的,直接先转化成对应的二进制补码,如果那个变量类型空间够就直接存,不够就发生截断在放进去。

取出的过程中,同样也不管你变量a是什么类型的,如果以%d形式打印我就认为你存的是一个有符号数。如果以%u打印我就认为你存的是一个无符号数。

3.unsigned定义时的小细节

推荐在定义无符号类型时后面+u。

int main()
{
	//大小写u都可以
	unsigned int b = 10U;
	printf("%d\n", b);
	printf("%u\n", b);

	return 0;
}

这样加上之后就知道你这个数字它就是无符号类型,当然你不加也没关系,加上的话,如果你这个数字是负数还会报错。

三.大小端问题

本质是数据和空间以字节为单位的一种映射关系。

在这里插入图片描述
在这里插入图片描述

四.signed定义的数据类型的取值范围

这里以signed char来举例。

char会开辟一个字节也就是8个比特位的空间,取值范围应该是:
在这里插入图片描述

因为它是有符号数据,所以最高位应该是符号位,所以取值范围应该是:
在这里插入图片描述

仔细看可以发现一个问题,0在这里好像出现了两次:
10000000,00000000也就是一个+0,一个-0.

但是计算机为了提高效率不可能让这两个都表示0,同时为了让效率最大化也不能丢掉其中一个0.所以现在需要用多的那个0来表示一个其它数据,而多的那个0最好用10000000,因为00000000来表示0是最好不过的了。

现在的问题就是10000000用来表示谁呢?经过上图可以发现-127~127都有人来表示了,所以10000000只有128和-128两种选择。经过重重考量其实用10000000来表示-128是最好的了。为什么呢?首先这是有符号类型,也就是说最高位是符号位,而10000000最高位是1,说明它是一个负数,如果用它表示128这个整数就会有歧义。所以这里把它定义成128是最好的了。

所以通过这些可以判断出有符号char类型的取值范围是-128~127.

但是还有最后一个问题,凭啥?凭啥你说它代表-128就是-128.下面我们来看看-128是怎么存到空间中的:

先看-128的原码。
在这里插入图片描述

发现它是9位的,存到char里面要发生截断:
在这里插入图片描述

现在发现了一件神奇的事情-128的原码是110000000,它转化成补码后还是110000000.

然后在做一件事情:把10000000取出来:
在这里插入图片描述

因为之前发生了截断,所以不能正常的提取出来。所以在这里做了一个规定(主要啊,不是我规定的)10000000代表的就是-128,以后看到10000000就知道它是-128.

为了加深记忆,这里我画了一个图:
在这里插入图片描述

五.浮点数的一些奇奇怪怪的理解

我们先看一个代码:

int main()
{
	double a = 1.0;
	double b = 0.9;
	printf("%.50lf\n", a);
	printf("%.50lf\n", b);
	printf("%.50lf\n", a - b);

	return 0;
}

再来看结果:
在这里插入图片描述
打印数据的后50位,发现了一个神奇的现象,1确实还是1,但是0.9和1-0.9好像就不是原来的数了。

接下来在看段代码:

int main()
{
	double a = 1.0;
	double b = 0.9;
	if (a - b == 0.1)
	{
		printf("相等");
	}
	else
	{
		printf("不相等");
	}

	return 0;
}

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

发现1-0.9竟然不等于0.1,这两个不正常的现象是为什么呢?因为浮点数本身放在内存中后会有精度丢失的,可能比原来小一丢丢,也有可能大一丢丢。所以浮点数本身是不能直接比较的。

但如果我们就是想比较,该怎么做呢?比如说想比较a-0.1和b的值是否相等。此时可以在其基础上加上一个修正值。我们可以包含一个头文件:

#include <float.h>

这个文件里有两个宏定义:
在这里插入图片描述

这两个宏是什么意思呢?我拿其中一个说:有一个数字它加上任何一个数字,都能改变这个数字的大小。而DBL_EPSILON是这些数字中最小的那一个。

这个DBL_EPSILON就是我们需要添加的修正值。如果判断两个数是否相等,这两个数相减,值应该等于0对吧,但是因为精度的确实,这个值肯定会比0稍微大那么一丢丢丢丢或者小一丢丢丢丢。但是如果这个值在-DBL_EPSILON和DBL_EPSILON之间的话,我们还是可以认为它们俩相等的,所以代码可以这样写:

#include <float.h>
#include <math.h>
int main()
{
	double a = 1.0;
	double b = 0.9;
	//需要比较的两个值是a-0.1和0.9
	if (fabs(a - 0.1 - 0.9) < DBL_EPSILON)
	{
		printf("相等");
	}
	else
	{
		printf("不相等");
	}

	return 0;
}

既然相间的范围在-DBL_EPSILON和DBL_EPSILON之间,是不是可以写成绝对值在0和DBL_EPSILON之间。

所以如果只在这个区间内,我们还是可以认为这两个数相等的。
在这里插入图片描述

既然这样,我在提一个问题,上面的小于号可不可以写成小于等于:

#include <float.h>
#include <math.h>
int main()
{
	double a = 0.0;
	double b = 0.9;
	if (fabs(a - 0.0) <= DBL_EPSILON)
	{
		printf("相等");
	}
	else
	{
		printf("不相等");
	}

	return 0;
}

上面先简化一下代码让a和0比较。首先答案时不推荐加上等号

首先这个代码是用来判断a和0是否相等,如果上面的条件加上等号就说明在a==DBL_EPSILON的时候,a和0相等这个逻辑也是成立的。但是DBL_EPSILON是引起一个数中变化最小的那个值,满足:b + DBL_EPSILON != b的这个条件。如果说a在等于DBL_EPSILON的时候同时满足a = = 0,不就相当于a = = DBL_EPSILON = = 0.但是0的概念是:b + 0 = = b.这和b + DBL_EPSILON != b有冲突,所以不能加等号。

六.数据在内存中的存储

数据在内存中的存储(一)

数据在内存中的存储(二)

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值