新手小白写的第一篇博客,记录自己的学习过程,希望我写的文章能给你们帮助。如果有不足的地方欢迎在评论区留言交流。
- 首先我要介绍数据分为哪些类型。
- 其次介绍整型在数据中是怎样存储的。
- 最后介绍浮点型在内存中的存储。
一,数据类型
基本的内置类型:
char
//字符数据类型 ,大小是1Bytes。
short
//短整型,大小是2Bytes。
int
//整形 ,大小是4Bytes。
long
//长整型 ,对于32位机器,大小是4Bytes;对于64位机器,大小是8Bytes.
long long
//更长的整形,大小是8Bytes.
float
//单精度浮点数 ,大小是4Bytes。
double
//双精度浮点数 ,大小是8Bytes。
c语言中没有字符串类型string
1.1数据类型归类
整型家族,浮点型家族,构造类型(也叫自定义类型),指针类型和空类型。
整型家族:
char
unsigned char
signed char
short
unsigned short
signed short
int
unsigned int
signed int
long
unsigned long
signed long
long long
unsigned long long
signed long long
注意:
1,除了char类型外,其他的基本内置类型都相当于有符号类型。如:int = signed int,signed通常不写出来。
2,对于char类型,char是signed char还是unsigned char,标准是未定义的,取决于编译器的实现。
3,对于有符号类型,其数据二进制位的最高位是符号位,0表示正,1表示负,剩余的都是有效位;而对于无符号类型,其数据二进制位没有符号位,全是有效位。
4, char之所以被归类为整型家族,是因为char在内存中存储的是ASCII值,如果以 %c 输出,会根据 ASCII码表转换成对应的字符,如果以 %d 输出,那么还是整数。
浮点型家族:
float
double
只要表示小数就可以用浮点型。
float精度低,存储的数值范围较小,一般小数点后6位;double精度高,存储的数据范围更大,一般小数点后15位。
构造类型:
- 数组类型:
int arr1[5]; //数据类型为:int [5]
int arr2[10]; //数据类型为:int [10]
char arr3[5]; //数据类型为:char [5]
- 结构体类型
struct
- 枚举类型
enum
- 联合类型
union
指针类型:
int *
char *
float*
void*
空类型
void
void可用于函数的传参,当函数不需要传参的时候,可以传void;void可用于函数的返回值,当函数不返回值的时候,函数的返回类型为void。void还可以用于指针类型。
二、整型在内存中的存储
一个变量的创建是要在内存中开辟空间的,而空间的大小是根据不同的数据类型决定的。
而数据则存储于变量之中。当数据的长度小于变量的长度时,在存储过程中,数据的长度会提升为变量的类型的长度,也就是发生整型提升;当数据的长度大于变量的长度时,在存储过程中,数据的长度会截断为变量的类型的长度,也就是发生整型截断;另外,当做算数运算时,表达式中的长度可能小于int长度的整型值,都必须先转化为int或者unsigned int,然后才能被CPU运算。
当数据的长度小于变量的长度时,如下图:数据补齐内存低地址,高地址位全补0。
当数据的长度大于变量的长度时,如下图:数据低位补齐内存低地址,数据其他位被截断未被存储!
当做算数运算时,如下图:
那么整型提升是具体如何操作的呢?
对于有符号的类型:
如:char a1 = -1;//11111111 a1的二进制补码(内存中存储的是补码)
高位补充符号位,即1,提升之后的结果是://11111111111111111111111111111111
又如:char a2 = 1;//00000001 a2的二进制补码
高位补充符号位,即0,提升之后的结果是://00000000000000000000000000000001
对于无符号类型: 高位直接补0即可。
下面我来介绍整形在内存中的大小端存储。
2.1大小端存储
什么是大小端存储?
大端存储:是指数据的低位存储在内存高地址,数据的高位存储在内存低地址;
小端存储:是指数据的低位存储在内存低地址,数据的高位存储在内存高地址。
以下以0x11223344的存储为例,来探讨大小端是如何存储的:
我们很好的发现:小端存储是把一个数据的低位存放在低地址,高位存放在高地址;大端存储是把一个数据的低位存放在高地址,高位存放在低地址。
不同的编译器存储模式是不一样的。我们常用的X86
结构是小端存储,运用于嵌入式开发的KEIL C51
则是大端存储。很多的ARM, DSP都为小端存储。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
练习:设计一个程序来判断当前机器的字节序。
思路:选一个较为简单的数来实现,如:
int a = 1;
其二进制表达为:0x00 00 00 01
1,通过取地址,若首地址内容是1则为小端存放;若首地址内容是0则为大端存放。
2,注意:取出a的地址类型为int*
,解引用*a
访问4个字节,但是我们需要访问1个字节,此时需要强制类型转换为char*
。
int main()
{
int a = 1;
if(*((char*)&a) == 1)
printf("小端存储\n");
else
printf("大端存储\n");
return 0;
}
执行结果:小端存储
2.2整型存储的一些练习题
以下再给几个练习来巩固对整型存储的学习:
//输出什么?
#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存储到字符型a中会发生整形截断:
//-1
//10000000 0000000 0000000 00000001–原码
//111111111 11111111 11111111 111111110–反码
//111111111 11111111 11111111 111111111–补码
// 11111111–截断后的补码存储到 a
2.%d-以十进制的形式打印有符号的整型整数要整型提升,高位补符号位1:
//1111111 11111111 11111111 1111111–a发生整型提升
//1111111 11111111 11111111 1111110–反码
//1000000 00000000 00000000 0000001–>原码 -1
3.由于char和signed char是一样的,所以b的处理和a一样。
4.对于unsigned char的处理,在整型提升的时候高位全补0:
//0000000 00000000 00000000 1111111 --整型提升
//0000000 00000000 00000000 1111111
//0000000 00000000 00000000 1111111–>正数的原反补都相同
5.最终打印的结果就是经过原码换算的结果:a = -1, b = -1, c = 255
#include <stdio.h>
int main()
{
char a = -128;
printf("%u\n", a);
return 0;
}
解题步骤:
1.首先普通整型-128存储到字符型a中会发生整形截断:
//-128
//10000000 00000000 00000000 10000000–原码
//11111111 11111111 11111111 01111111–反码
//11111111 11111111 11111111 10000000–补码
// 10000000–截断后的补码
2.%u-以十进制的形式打印无符号的整型整数要整型提升,高位补符号位1:
//11111111 11111111 11111111 10000000–整型提升–补码
//11111111 11111111 11111111 10000000–原码–正数的原码,反码和补码都相等
3.经过计算得十进制为: 4294967168
#include <stdio.h>
int main()
{
char a = 128;
printf("%u\n", a);
return 0;
}
解题步骤:
1.首先普通整型128存储到字符型a中会发生整形截断:
//128
//00000000 00000000 00000000 10000000–原码,反码,补码
// 10000000–截断后的补码
2.%u-以十进制的形式打印无符号的整型整数要整型提升,高位补符号位1:
//11111111 11111111 11111111 10000000–整型提升–补码
//11111111 11111111 11111111 10000000–原码–正数的原码,反码和补码都相等
3.经过计算得十进制为: 4294967168
int i = -20;
unsigned int j = 10;
printf("%d\n", i + j);
//按照补码的形式进行运算,最后格式化成为有符号整数(发生算术转换)
解题步骤:
1.-20和10只需要计算出补码即可,无需进行整型截断与提升:
//-20
//1000000000000000000000000010100
//1111111111111111111111111101011
//1111111111111111111111111101100–补码
//10
//0000000000000000000000000001010–补码
//i + j
//1111111111111111111111111110110–补码
//1111111111111111111111111110101–反码
//1000000000000000000000000001010–原码
2.结果: -10
unsigned int i;
for(i = 9; i >= 0; i--)
{
printf("%u\n",i);
}
解题步骤:
1.注意unsigned int的范围: i ∈[0, 2^32 - 1].
2.前10个数的打印都没问题,分别是9,8,7,6,5,4,3,2,1,0。当0 - 1时,其结果应该是i的上限,即2^32 - 1=4,294,967,295,这是由于i的范围决定的,而且4,294,967,295满足i >=0, 之后一直死循环打印。
还可以这样理解(写出-1的补码):
//0 - 1 = -1
//1000000000000000000000000000001–原码
//1111111111111111111111111111110–反码
//1111111111111111111111111111111–补码–>4,294,967,295
int main()
{
char a[1000];
int i;
for(i = 0; i<1000; i++)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));
return 0;
}
解题步骤:
1.注意char的范围: i ∈[-128,-1]∪[0, 127].
2.前256项的打印结果依次是:-1 -2 -3 -4 …-128 127 126 125 … 3 2 1 0 ,-128 - 1的值一定是127.
3.strlen()
这个函数求字符串长度求的是'\0'
前面的字符的个数
4.因此,打印的结果是255.
#include <stdio.h>
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello world\n");
}
return 0;
}
解题步骤:
1.注意unsigned char的范围: i ∈[0, 255].
2.i的执行结果:0,1,2,3,…,254,255,0,1,2,3,…,254,255,…
3.所以最终会死循环打印hello world