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

第一题

int main()
{
	unsigned int i = 0;
	for (i = 9; i >= 0; i--)
	{
		printf("%u\n", i);
	}
	return 0;
}

这个代码输入的结果是什么?

答:首先,代码肯定是一个死循环,原因是当i=0;i--后,i变成-1,对应的二进位制补码为11111111111111111111111111111111,因为我们的i是无符号数字,所以对应的数字是2^32-1,2^32-1大于0,所以再次执行循环,执行到结果为0为止,近似于死循环

结果如图所示

第二题

int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%d", strlen(a));
	return 0;
}

输入的结果是什么?

答:255,原因是strlen求字符串个数,strlen函数的返回值是size_t,是无符号数,strlen是检测'\0'前面的字符个数,’\0'对应的数字是0,所以我们只要找到0,求出0前面的元素个数就可以了,我们画一个图进行解释

 可以发现,在\0前面的数字为-1到-128,一共有128个数字,127到1一共127个数字,所以结果是255,由此可见,char类型的元素对应的数字的范围是[-128,127],

第三题

unsigned char i = 0;
int main()
{
	for (i = 0; i <= 255; i++)
	{
		printf("hello world\n");
	}
	return 0;
}

答:既然是无符号类型的char型,其对应的数字范围就是0——255,所以i所对应的数字始终满足for循环,所以死循环打印helloworld。

第四题

int main()
{
	if (strlen("abc") - strlen("abcdef") >= 0)
		printf(">\n");
	else
		printf("<\n");
	return 0;
}

打印结果是什么?

答:(strlen("abc") - strlen("abcdef")这个结果等于-3,但是由于strlen函数的返回值是size_t,也就是无符号数,既然是无符号数,那么-3对应的二进位制10000000000000000000000000000011,因为无符号数不分正负,1并不代表符号位,所以结果是非常大的整数,所以打印的结果是大于号

 结果如图所示

算术转换

 当下面的类型与上面的类型进行计算时,先把下面的类型转换为上面的类型,同类型后再进行计算

浮点型在内存中的存储

常见的浮点数:3.14159

1E10=1.0*10^10

浮点型数字家族包括:float,double,long double

浮点型表示的范围在float.h中定义

整型类型

 浮点型类型

 浮点型存储的例子

int main()
{
	int n = 9;
	float*pFloat = (float*)&n;
	printf("n的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	*pFloat = 9.0;
	printf("num的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	return 0;
}

 运行结果如图所示

 我们把这个代码的意思说一下:

首先创建整型n,n=9,取出n的地址,把这个地址强制类型转化为float类型,然后传递给float类型的指针变量*pFloat,,然后我们打印n的值,我们的打印pFloat指针指向参数的值,我们将*pFloat赋值为9.0,打印n的值,我们再打印*pFloat的值

为什么结果差异这么大呢?接下来,我们来讲解浮点型在内存中存储和取出的规则

浮点型存储规则

任何一个二进位制的浮点数都可以写成这种形式

 1:(-1)^S表示符号位,当S为0时,为正数,当S为1时,为负数

2:M表示有效数字,大于等于1,小于2

3:2^E表示指数位

我们举个例子

 v=5.0f;

换算成上面这种形式

首先,为正数,(-1)^0

5对应的2进位制数字是101,所以M=1.01

E的结果为2

换算后得到v=(-1)^0*1.01*2^2

 我们再化简一个

v=9.5f

首先,为正数,(-1)^0

9.5对应的二进位制数字为1001.1,所以M=1.0011

所以E=3

换算后可得v=(-1)^0*1.0011*2^3

有一些数字是不能精确保存的

例如V=9.6f,有效数字是很长很长的,float类型只占4个字节,32个比特位,所以我们无法精确保存一些数字

 

 1:在计算机内部存储有效数字M时,会首先去除M的1,因为有效数字始终大于等于1且小于等于2,我们存储的数字M就为0.xxxxxxx,

在我们要从计算机中读取数字M,时,我们再+1表示真正的有效数字,这样做的目的是节省一个比特位

接下来,我们分析E

E是一个无符号整数,无符号整数就决定了E不会为负,但是在科学计数法的表示中,指数部分也可以用-号表示,例如

v=0.5f

换算成二进位制结果为0.1=1*2^(-1),由此看见,指数部分可以为-1,如何表示-1呢?

我们知道,在32位平台上,E所占的字节数为8,因为E是无符号整数,所以E的结果为0——255,在64位平台上时,E所占的字节数为16,因为E是无符号整数,所以E的结果为0——2047,

我们引入一种方法,既然我们的E不能存储负数,在32位平台上,我们让E的真实值加一个中间值127,这样保证了存进去的E值不会为负数,在64位平台上,我们加上一个中间值1023

我们举一个存储的例子

float f=5.5

如何进行存储呢?

答:首先,存储s,s占一个比特位,因为5.5为整数,所以结果为(-1)^0,所以S为0

5.5=101.1=1.011*2^2,所以M的结果为1.011,占23个比特位,前四位存储的是1.011,后面的19个结果都为0

接下来,我们求E,E的真实值为2,E+127=129,所以我们存进去的E为10000001,所以我们存储的值对应的二进位制为0 10000001 0110000000000000000000

我们在内存中的存储是16进位制表示的,一个16进制是由四个二进位制组成的,所以存储的结果为0100 0000 101 1000 0000 0000 0000 0000,

对应的结果为4 0 b 8 0 0 0 0  对应的就是40 b8 00 00,因为我们的存储是小端存储,则结果为00 00 b8 40,我们进行运行检测一下

运行结果如图所示

接下来,我们讲解如何从内存中取出。

E从内存中取出分三种情况

E不全为0或不全为1:E-127求出E的真实值,然后在M(有效数字)前面补一,根据二进位制数字求出FLOAT类型的数字

例子01000000010110000000000000000000,如图所示,S是最前面的0,因为E:10000001不为全0或不为全1,我们让E-127,结果为2,所以E的真实值为2,0110000000000000000000为有效数字M,我们在M的最前面加上一个1,就为真实的M的值,也就是11011,因为E为2,所以M的数字实际上是对应的Float类型的数字为5.5

所以对应的类型为(-1)^0*110.11*2^2

当E全为0时,E的真实值就是-127,因为-127+127=0,换算成二进位制数字就是2^(-127),这个数字是无限趋近于0的,所以我们可以换一种简单的方法,既然这个结果很小,那我们就省略求M有效数字的真实值,不再+1,因为1.xxxx乘2^E是非常小的数字,所以1是可有可无的,我们直接把E的值锁定为1-127,或1-1023

当E全为1时,11111111=255,所以E的有效数字=255-127=128,2^128是无穷大的数字,所以表示+-无穷大的数字

接下来,我们分析上面举过的例子

int main()
{
	int n = 9;
	float*pFloat = (float*)&n;
	printf("n的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	*pFloat = 9.0;
	printf("num的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	return 0;
}

第一个printf,因为我们存进去的是整型9,我们拿出来的也是整型类型,所以我们打印的结果也为9

第二个printf,我们看这句话float*pFloat = (float*)&n;这里的意思是取出n的地址,将n强制类型转化为浮点型,整型n对应的二进位制为00000000000000000000000000001001,我们既然把这个二进位制转为浮点型,我们进行解引用浮点型,就是按照浮点型的方式进行转化:

s=0,E=00000000,m=0.000000000000000000000000101,我们上面已经知道,当E的结果为全0时,对应的数字为1-127=-126,并且有效数字M不再+1,所以对应的浮点数为1*0.000000000000000001001*2^(-126),是非常小的数字,近似为0,所以打印的结果为0

第三个printf,我们通过解引用的方法,将浮点型类型的指针指向地址的元素赋值9.0,所以我们是用浮点型数字存进去的9.0,也就是9.0对应的二进位制数字为1001,所以N=3+127=130,s=0,有效值为1.001,所以对应的二进位制数字为01000001010010000000000000000000,我们用整型的方式读二进位制序列可得到的结果是非常大的

第四个printf,我们以浮点型的形势存进去9.0,则用浮点型拿出来的结果也是9.000000

void move_odd_even(int arr[10],int sz)
{
	int left = 0;
	int right = sz - 1;
	while (left < right)
	{

		while (arr[left] % 2 == 1)
		{
			left++;
		}
		while (arr[right] % 2 == 0)
		{
			right--;
		}
		if (left < right)
		{
			int tmp = 0;
			tmp = arr[left];
			arr[left] = arr[right];
			arr[right] = tmp;
		}
	}
}
int main()
{
	int arr[10] = { 0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		scanf("%d", arr + i);
	}
	move_odd_even(arr,sz);
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

   第一个问题:    for (i = 0; i < sz; i++)
    {
        scanf("%d", arr + i);
    }为什么这里没有取地址

答:因为我们的arr本身就是地址,arr+i可以表示数组下标为i元素的地址,所以不再需要取地址

第二个问题while (arr[left] % 2 == 1)
        {
            left++;
        }
        while (arr[right] % 2 == 0)
        {
            right--;
        }这串代码的意义

答:第一个while循环,其满足条件是下标为left的元素为奇数,换言之就是只要是奇数,就跳过,直到找到第一个偶数为止,第二份while循环则是以找到第一个奇数为限制条件,找到之后,我们再执行if语句,如果左边的依旧小于右边的,那么我们就把这两个元素进行交换,就是把左边的偶数换到右边,右边的奇数换到左边,这是一整个循环,所以我们要在外面再嵌套一个while循环,循环条件是left<right

但是,代码依旧有不足的地方,例如

假如我们输入的全是奇数,则我们执行第一个while循环,始终满足条件,当我们输入到第十个数字时,依旧满足,我们再让left++,然后访问,就会造成越界访问,如何解决这种问题

我们在两个while循环加上left<right

 如图所示

最后一题

 

int main()
{
	int m = 0;
	int n = 0;
	scanf("%d%d", &m, &n);
	int arr1[1000] = { 0 };
	int arr2[1000] = { 0 };
	int i = 0;
	for (i = 0; i < m; i++)
	{
		scanf("%d", &arr1[i]);
	}
	for (i = 0; i < n; i++)
	{
		scanf("%d", &arr2[i]);
	}
	int j = 0;
	int k = 0;
	while (j < n&&k < m)
	{
		if (arr1[j] < arr2[k])
		{
			printf("%d", arr1[i]);
			j++;
		}
		else 
		{
			printf("%d", arr2[k]);
				k++;
		}
	}
	
	if (j < n)
	{
		for (; j < n; j++)
		{
			printf("%d", arr1[i]);
		}
	}
	else
	{
		for (; k < m; k++)
		{
			printf("%d", arr2[k]);
		}
	}
	return 0;
}

问题1:    for (i = 0; i < m; i++)
    {
        scanf("%d", &arr1[i]);
    }是什么意思

答:,这种方法是实现输入数组内部元素的过程,&arr[i]表示数组下标为i元素的地址,把我们输入的数字,存入数组内部

我们对函数部分进行解读

while (j < n&&k < m)
	{
		if (arr1[j] < arr2[k])
		{
			printf("%d", arr1[i]);
			j++;
		}
		else 
		{
			printf("%d", arr2[k]);
				k++;
		}
	}
	
	if (j < n)
	{
		for (; j < n; j++)
		{
			printf("%d", arr1[i]);
		}
	}
	else
	{
		for (; k < m; k++)
		{
			printf("%d", arr2[k]);
		}
	}

j,k是我们创建的两个变量,while (j < n&&k < m),while循环的条件是j和k的下标都没有到达数组下标的最大值,为什么要这样做,原因是在j=n或k=m之前的计算模式相同,之后的计算模式不同

if (arr1[j] < arr2[k])
		{
			printf("%d", arr1[i]);
			j++;
		}
		else 
		{
			printf("%d", arr2[k]);
				k++;
		}

假设j对应的数组是第一个数组,k对应的是第二个数组,假设第一个数组相对与第二个数组小时,打印这个数字,然后继续沿这一个数组,跳过这个元素,如果第二个数组大,情况则是相同,最终的结果是某一个数组首先达到结尾


	if (j < n)
	{
		for (; j < n; j++)
		{
			printf("%d", arr1[i]);
		}
	}
	else
	{
		for (; k < m; k++)
		{
			printf("%d", arr2[k]);
		}
	}

我们进行判断,如果是第一行数组未达到末尾,也就是第二行数组首先达到末尾,那我们依次打印剩余的第一行数组的元素,反之,也相同。

假设,我们要创建一个数组来接收这些元素,并把数组的方法进行打印,那么该如何修改呢?

int main()
{
	int m = 0;
	int n = 0;
	int r = 0;
	scanf("%d%d", &m, &n);
	int arr1[1000] = { 0 };
	int arr2[1000] = { 0 };
	int arr3[2000] = { 0 };
	int i = 0;
	for (i = 0; i < m; i++)
	{
		scanf("%d", &arr1[i]);
	}
	for (i = 0; i < n; i++)
	{
		scanf("%d", &arr2[i]);
	}
	int j = 0;
	int k = 0;
	while (j < n&&k < m)
	{
		if (arr1[j] < arr2[k])
		{
			arr3[r++]=arr1[j];
			j++;
		}
		else 
		{
			arr3[r++] = arr2[k];
				k++;
		}
	}
	
	if (j < n)
	{
		for (; j < n; j++)
		{
			arr3[r++] = arr1[i];
		}
	}
	else
	{
		for (; k < m; k++)
		{
			arr3[r++] = arr2[k];
		}
	}
	for (i = 0; i < m + n; i++)
	{
		printf("%d", arr3[r]);
	}
	return 0;
}

问题1:arr3[r++]=arr1[j];r++是什么意思,r++是后置++,是先使用,再++,所以我们这里是把arr1[j]先赋给arr3[r],然后r再++

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值