距离上次更新本文已经过去了 695 天,文章部分内容可能已经过时,请注意甄别

@[toc]

引子

上篇博客向大家介绍了 int 类型在内存中的存储方式【链接】

本篇博客我们继续往后,看看整形家族里的 char 类型是如何在内存中存储的吧!


char 类型

字符类型包括以下两种形式

  • unsigned char 无符号

  • signed char 有符号

用一个简单的 printf 来看看它们在打印上的区别

c
1
2
3
4
5
6
7
8
9
10
#include<stdio.h>
int main()
{
char a=-1;
signed char b=-1;
unsigned char c=-1;
printf("a=%d b=%d c=%d\n",a,b,c);

return 0;
}

打印结果为

c
1
a=-1 b=-1 c=255

这里我们可以得出两个结论

1. 在 VS 编译器下,char 默认为 signed char

2.unsigned char 的处理方式和 signed 不同

无符号数的处理可以看我之前写的这一篇博客👉【链接】


存放和提升

例一:-1

先写出 - 1 的原反补码

c
1
2
3
10000000 00000000 00000000 00000001  -1原码
11111111 11111111 11111111 11111110 反码
11111111 11111111 11111111 11111111 补码

在内存中存放的时候,a、b、c 存放的都是补码的最后一个字节,即 11111111

而当我们用 % d 打印的时候,会发生整型提升

整型提升的概念我写过另外一篇博客👉【点我】

这里就是有符号和无符号处理不同的地方了

因为 a、b 都是有符号的 char,所以编译器会将最高位视为符号位进行提升

所以 a、b 进行整型提升后的结果如下

c
1
11111111 11111111 11111111 11111111//提升后a、b的补码

而 c 是无符号 char 类型,编译器会在最高位补 0

c
1
00000000 00000000 00000000 11111111//提升后c的补码

此时最高位为 0,编译器将其视为正数,此时补码就是原码,打印 255

符号位 0 为正,1 为负

正数的原码、反码、补码相同

例二:-128

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

打印的结果并不是 - 128

c
1
4294967168

-128 的原反补码

c
1
2
3
10000000 00000000 00000000 10000000  -128原码
11111111 11111111 11111111 01111111 反码
11111111 11111111 11111111 10000000 补码

a 中存放的是 1000 0000

最高位视为符号位,整型提升后

c
1
11111111 11111111 11111111 10000000//新的补码

但因为这里是 **% u 打印,视作无符号数 **

此时原反补码一致,直接视作原码进行计算

打印结果即为 4294967168

image-20211214144712057

如果我们让 a 等于 128,结果相同

image-20211214144833988

char 变量中存放数值的范围

先来看看这样一串代码

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <string.h>
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));

return 0;
}

代码的结果如下:255

image-20220105164552434

在初始化的时候,我们设定了 a [1000],a 数组里面理应有 1000 个元素

那 strlen 函数求得的结果为何是 255,而不是 1000 呢?


实际 char 类型中的数据范围分为有符号和无符号两种

  • signed char 1byte-8bit,取值范围 - 128~127
  • unsigned char 1byte-8bit,取值范围 0~255

以二进制表示,如下图所示

image-20220105165554012

因为无符号 char 类型中,八位补码视作原码进行计算

而有符号 char 类型中,在计算的时会将首位视为符号位

c
1
2
3
11111111 //补码
11111110 //反码,补码减1
10000001 //源码,读出-1
c
1
2
3
10000001//补码
10000000//反码
11111111//首位符号位不读,其他位读出-127
c
1
2
3
4
10000000//补码
这个二进制位不能-1,直接视作源码计算
不能忘记这是个负数!
10000000//源码读出128,加上负号为-128

下面这幅图可以形象地表示出 char 类型数据范围

image-20220105171220668

c
1
2
3
4
5
6
7
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));

上面那串代码里面的 for 循环,实则是一直给数组 a 中从 - 1 开始赋值到 - 128,再从 127 赋值到 1,0…… 如此循环赋值,直到 i=1000 跳出循环。

而我们的 strlen 函数在计算数组长度的时候,遇到第一个 0 就会停止计算

这样我们就得出了答案为 255!

如何查询范围定义?

我们可以通过头文件 <limits.h> 来查找范围定义

在 VS2019 里面右键该头文件,点击 “转到文档”

image-20220105171716169

这里我们就能看到各种数据类型的数据范围

image-20220105171852074

我们还可以看看其他的数据类型

有符号 short 为例,它的范围是 - 32678 到 32767

image-20220105172001869

image-20220105172502211


结语

以一串代码示例结束本篇博客吧!

image-20220105172544311

感谢你看到最后!

点个赞呗,这对我灰常重要!!😘