C语言学习记录(十二)——数据的存储

本文深入探讨了整型在内存中的存储方式,包括原码、反码、补码以及小端和大端字节序的概念,并通过实例解释了不同字节序对数据存储的影响。同时,介绍了浮点型在内存中的存储规则,以32位为例展示了指数和尾数的表示。最后,通过一系列练习题加深了读者对这些概念的理解。
摘要由CSDN通过智能技术生成

一、整形在内存中的存储

int a = 20;
int b = -10;
原码、反码、补码

三种表示方法均有符号位数值位两部分,符号位用0表示“正”,用1表示“负”。
原码:直接将二进制按照正负数的形式翻译成二进制。
反码:将原码的符号位不变,其他位依次取反就可以得到。
补码:反码+1就得到补码

int a =20          0000 0000 0000 0000  0000 0000 0001 01002进制)
当在PC端存储时,因为PC是小端存储,所以存放的结果为: 14 00 00 0016进制)

int b = -10(原码)1000 0000 0000 0000 0000 0000 0000 1010     (2进制)
         (取反码)1111 1111 1111 1111 1111 1111 1111 0101
         (求补码)1111 1111 1111 1111 1111 1111 1111 0110
                           在小端存储上所存储的是:f6 ff ff ff

大端存储模式:是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中。
小端存储模式:是指数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中。

面试题:
  请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序:

思路:
    取到的低位,是所预判的低位数据,则它就是小端
//code one
#include<stdio.h>
int main(){
	int a = 0x11223344;  
	   //   44 33 22 11
	//为了取出一个字节,我们知道char*类型是可以取出1个字节的
	char *p = (char *)&a;  //要将&a的int*类型转换为 char* 类型
	if (*p == 0x44){
		printf("小端");
	}
	else{
		printf("大端");
	}
	return 0;
}

练习题1:

输出什么?
#include<stdio.h>
int main(){
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("a=%d, b=%d, c=%d", a, b, c);
	return 0;
}
结果为 -1 -1 255
在第a的例子中
-1 的原码  1000 0000 0000 0000  0000 0000 0000 0001
     反码  1111 1111 1111 1111  1111 1111 1111 1110  
     补码  1111 1111 1111 1111  1111 1111 1111 1111
因为要赋值给char类型的 a, char是1个字节,且其一个字节是8位
此时就会发生截断  所以就会把末尾的8位截取出来  1111 1111
所以char类型的 a 就为 1111 1111
当我们以%d形式打印a时,即printf("%d\n",a);
我们要进行类型的提升,因为char只有8位,而要打印整形要打印32位

进行提升,补符号位的最高位(当时有符号类型时),因为时char有符号类型,所以补1
1111 1111 1111 1111  1111 1111 1111 1111
因为是以%d进行打印,所以认为,提升后的为补码
所以对他-1,再取反得到
1000 0000 0000 0000  0000 0000 0000 0001
b与a的情况相同,加了signed与不加是同样的意思
分析c的例子
与a的情况类似,都是-1所以都会得到 1111 1111
但当以%d形式打印时,进行类型提升
因为是无符号char 所以补0 
0000 0000 0000 0000  0000 0000 1111 1111
通过%d打印c,因为最高位是0,所以会认为它是一个整数,所以不用进行原补反码操作
所以值为255

练习题2

#incude<stdio.h>
int main(){
    char a = -128;
    printf("%u\n", a);
    return 0;
}
-128 原码 1000 0000 0000 0000  0000 0000 1000 0000
     反码 1111 1111 1111 1111  1111 1111 0111 1111
     补码 1111 1111 1111 1111  1111 1111 1000 000
     发生截断  1000 0000
     %u以整形进行打印,进行类型提升
     1111 1111 1111 1111  1111 1111 1000 000
     %u打印,最高位是数值位,不需要求补码
     结果为 4294967168

有符号char类型的取值范围是-128 ~ 127,一个char是8位

1111 1111   代表的是 -1
1000 0000   代表的是-128
0111 1111   代表的是127
0000 0000   代表的是0

在这里插入图片描述
无符号char类型的取值范围是-128 ~ 127

1111 1111  代表255
0000 0000  代表0

在这里插入图片描述


练习题3

#include<stdio.h>
int main(){
    char a = 128;
    printf("%u\n", a);
    return 0;
}
0000 0000 0000 0000  0000 0000 1000 0000
不是负数,不需要求补码
则a内存放的是1000 000
进行整形提升,发现原本是有符号类型的,得到:
1111 1111 1111 1111  1111 1111 1000 0000
这里在使用%u,无符号形式打印,则不需要求原码
则输出一个很大的值。
如果使用%d进行打印,则需要求原码。

练习题4

#include<stdio.h>
int main(){
    int i = -20;
    unsigned int j = 10;
    printf("%d\n", i+j);
    return 0;
}
-20   1000 0000 0000 0000  0000 0000 0001 0100
反码  1111 1111 1111 1111  1111 1111 1110 1011
补码  1111 1111 1111 1111  1111 1111 1110 1100
i是int类型,所以不发生截断

10    0000 0000 0000 0000  0000 0000 0000 1010

要进行i+j,则:
    1111 1111 1111 1111  1111 1111 1110 1100
+   0000 0000 0000 0000  0000 0000 0000 1010
=   1111 1111 1111 1111  1111 1111 1111 0110     
以%d进行打印,因为最高位是1为负数,所以需要求原码
1111 1111 1111 1111  1111 1111 1111 0110
0000 0000 0000 0000  0000 0000 0000 0001
1111 1111 1111 1111  1111 1111 1111 0101
1000 0000 0000 0000  0000 0000 0000 1010   -> -10

练习题5:

code one
#include<stdio.h>
int main(){
    unsigned int i;
    for (i = 9; i >= 0; i--){
        printf("%u\n", i);
    }
    return 0;
}
此代码会死循环,因为在i=0后会再执行一次i--,此时本应等于-1,但因为是无符号类型,所以i会得到一个非常大的值(FF FF FF FF)
code two
#include<stdio.h>
int main(){
    char i = 0;   char的取值范围是 -128 ~ 127
    for (; i < 130; i++){
        printf("%d", i);
    }
    return 0;
}    同样死循环

练习题6:

#include<stdio.h>
int main(){
    char a[1000];
    int i;
    for (i = 0; i<1000; i++)
    {
        a[i] = -1-i;
    }
    printf("%d", strlen(a));
    return 0;
}
数组里面放的是
-1 -2 -3 -4 .... -128 127 126 ... 1 0 -1
strlen遇到0时就会停止
所以结果打印出的是255
 0 ,\0NULL  都表示为 0
  '0'   字符零   表示ASCII码的48

练习题7:

#include<stdio.h>
unsigned char i = 0;
int main(){
    for( i = 0; i <= 255; i++)
    {
        printf("Hello world\n");
    }
}   死循环

二、浮点型在内存中的存储

12.5
整数为变为2进制,0.5×2,直至尾数为0时

1100.1

以32位为例
1.1001 * 2^3,将1001存放在尾数部分
  将指数位变为科学计数法
在这里插入图片描述




习题1

int main(){
    unsigned char a = 200;
    unsigned char b = 100;
    c = a + b;
    printf("%d %d", a+b, c);
    return 0;
}
在计算c时:
   a 200        1100 1000
 + b 100        0110 0100
 = c       0001 0010 1100
 因为定义的c为unsigned char类型,所以只保留1个字节,即八位,0010 1100
 此时使用%d打印出c时,得出的是44

  由于printf是可变参数的函数,所以后面参数的类型是未知的,所以甭管你传入的是什么类型,printf只会根据类型的不同将用两种不同的长度存储。其中8字节的只有long longfloatdouble(注意float会处理成double再传入),其他类型都是4字节。所以虽然a + b的类型是char,实际接收时还是用一个四字节整数接收的。另外,读取时,%lld%llx等整型方式和%f%lf等浮点型方式读8字节,其他读4字节。

习题2

unsigned int a = 0x1234;
unsigned char b = *(unsigned char *)&a;32位大端模式处理器上变量b等于?
大端序中,低地址到高地址的四字节十六进制排列分别为00 00 12 34,其中第一个字节的内容为00,故位 0x00

习题3:打印出杨辉三角

1
11
121
1331
#include<stdio.h>
void yangHuiTriangle(int n){
	int data[30][30] = { 1 }; //填入第一行的数字
	int i, j;
	for (i = 1; i < n; i++){
		data[i][0] = 1; //每行的第一列都是1
		for (j = 1; j <= i; j++) //从第二行开始填入数据
		{
			data[i][j] = data[i - 1][j] + data[i - 1][j - 1]; //递推公式
		}
	}
	for (i = 0; i < n; i++){
		for (j = 0; j <= i; j++){
			printf("%d ", data[i][j]);
		}
		printf("\n");
	}
}
int main(){
	int n = 10;
	yangHuiTriangle(n);
}

  由于在填充第n行的杨辉三角时,只跟第n-1行的杨辉三角产生了联系,不会跟之前行产生联系,所以没有必要保存每一行的数据,填一行打印一行即可,这样能让空间复杂度从n^2降低到n

void yangHuiTriangle(int n)
{
	int data[30] = { 1 };
	int i, j;
	printf("1\n"); //第一行就直接打印了
	for (i = 1; i < n; i++) //从第二行开始
	{
		for (j = i; j > 0; j--) //从后向前填,避免上一行的数据在使用前就被覆盖
		{
			data[j] += data[j - 1]; //公式同上,由于变成了一维,公式也变简单了。
		}
   
		for (j = 0; j <= i; j++) //这一行填完就直接打印了。
		{
			printf("%d ", data[j]);
		}
		putchar('\n');
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值