C语言-深度刨析数据在内存中的存储(一)——大小端模式及提升和截断

一、数据类型

基本数据类型

char 
 以下只是表示数值范围不同,字节数还是相同
	unsigned char
	signed char
short
	unsigned short
	signed short
int
	unsigned int
	signed int
long
	unsigned long
	signed long
float
double

构造类型:

数组
结构体 struct
枚举 enum
联合 union

指针类型:

int *pi;
char *pc;
float *pf;
void *pv;

空类型----->void

二、整型在内存中的存储

计算机符号数的基本概念

计算机有三种符号数,即为原码、反码、补码。
三种方法均有符号位和数值位两部分,符号位均为0正1负。

原码

将十进制直接转换位对应的二进制数

反码

原码符号位不变,其他位按位取反

补码

在求出反码的基础上,低位+1

正数的原码=反码=补码
对于整型来说,数据存放内存中其实存放的是补码(尤其用来表示负数)。因为使用补码,可以将符号位和数值域统一处理;因为CPU只有加法器,所以用补码表示可以统一加减法运算。

大端机和小端机

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

int val = 1;
低位         高位
 01	 00	 00	 00
 00	 00	 00	 01

隐式类型的转换
若为大转小,则截断,如int->char,四个字节截成一个字节(往往截的是低位)。
若为小转大,则提升,即高位补位,若为无符号数则补0,若为有符号数则补符号位。

char->int ,有符号数则补符号位。
unsigned char->int,无符号数则补0
注:提升看的是本身的类型,而不是要转的类型

char->unsigned int,看本身,本身为有符号数(char),则补符号位,而不是看转成的类型(unsigned int)。

百度2015年系统工程师笔试题

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

//代码1
#include <stdio.h>
int check_sys()
{
 int i = 1;
 return (*(char *)&i);
}
int main()
{
 int ret = check_sys();
 if(ret == 1)
 {
 printf("小端\n");
 }
 else
 {
 printf("大端\n");
 }
 return 0;
}

代码中的 return (*(char *)&i);可以理解为:

int *pi = &i;
char *pc = pi;
char pt = *pc;
return pt;
练习

程序运行后会输出什么
①:

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;
}

分析:

分析代码可得,a,b,c均为char类型,1字节,在printf中,需要将他们转化为int类型进行打印,因此,就用到了隐式类型的转换,即小转大,即提升,看清a,b为有符号数,因此在提升时,补符号位,而c为无符号数,即高位补0。

因此程序运行的结果为-1,-1,255。
在这里插入图片描述
②、③:

2.
#include <stdio.h>
int main()
{
    char a = -128;
    printf("%u\n",a);
    return 0;
}

3.
#include <stdio.h>
int main()
{
    char a = 128;
    printf("%u\n",a);
    return 0;
}

分析代码可知,a为-128,它以补码的形式存储在计算机中,即11111111,11111111,11111111,10000000。题目要打印出unsigned int,即应该提升,因为本身类型为char,有符号数,则将表示数据的部分 10000000,进行提升,最高位补符号位,易知该数据的符号位为1,则提升的结果为11111111,11111111,11111111,10000000。打印的结果为232-1-127。
因为第3题也是取表示数值的部分进行提升,即10000000,因此打印的结果应和第二题的结果一样,均为232-1-127。

第2题结果如下:
在这里插入图片描述
第3题结果如下:
在这里插入图片描述
④:

4.
int i= -20;
unsigned  int  j = 10;
printf("%d\n", i+j);
//按照补码的形式进行运算,最后格式化成为有符号整数

分析:i 为int类型,j为unsigned int类型,虽然两者对应的字节一样,均为4Byte,但是表示的范围却不同,若要将两者相加,则需对 i 进行提升。

i = -20 ,其补码为:11111111,11111111,11111111,11101100
j=10,其补码为:00000000,00000000,00000000,00001010
将 i 中表示数值部分截取出来,进行提升,结果和其补码相同,则
i   11111111,11111111,11111111,11101100
+
j   00000000,00000000,00000000,00001010
= 11111111,11111111,11111111,11110110
数值部分化为原码为:10001010,符号位为1,则其数值为-10 。

⑤:

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

按照正常思路,该程序运行后,打印出的结果应该为9,8,7,6,5,4,3,2,1,0。但是该程序运行后,打印的结果如下图:
在这里插入图片描述

会进入一个死循环。再来观察一下代码,可看出 i 的类型为unsigned int,unsigned int 代表着该数是一个无符号整数,即没有负数,结果均为正数,因此,当 i=0时,i- -;按逻辑 i 应该为-1,但是由于 i 是无符号整型,因此,在计算机中的补码为01111111,11111111,11111111,11111111,为正数。对应的结果为232-1即为图中的4294967295。因此会陷入死循环。
因此,得出有符号数的 0 - 1 = -1,无符号数的 0 - 1 = 232- 1 。

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

首先,需知道strlen(a)遇到会一直遇到char数组的’\0’才会结束,’\0’表示空字符,二进制表示为0000,0000。因此,在for循环中,当-1 - i 的二进制为0000,0000时,该数组赋值就会结束,i 是int类型的数据,-1 - i 也是int 类型的数据,将一个int类型的数据赋值给char类型时,需要进行截断,即将表示4Byte截为1Byte。再分析可知,-1 - i = -1 +(-i),每次都是求 -i 的二进制数,然后加上-1的二进制数。

可看到,每次加-1的二进制数,结果会慢慢的靠近0000,0000。因此,要使1111,1111(255)全部变为0000,0000,总共需要255次。因此,程序打印的结果为255。

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

分析代码可得,i 为 unsigned char 类型,即只有一个字节(1Byte),且为无符号数,代码 i= 0 中的0为字面常量,是int类型的,要将int 给 char 类型需要截断,因此 i = 0 的时,i 在计算机中表示为0000,0000,之后的每次+1,都是在截断之后进行+1操作,而 unsigned char 在计算机中的最大值为 1111,1111。,换算为10进制就是255,也就是说 i 的值最大取到255,但是跳出循环的条件时 i > 255当 i 再次加1后,按逻辑来讲,结果为256,二进制为1,0000,0000,应该跳出循环,但char类型只有1Byte,因此,保留在 i 中的二进制为 0000,0000,结果变为0,不满足循环跳出的条件,继续循环。因此,该程序一旦运行就会造成无限的循环。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值