坚持就是胜利
文章目录
一、数据类型详细介绍
前面我们学习了基本的内置类型,以及它们所占存储空间的大小。
c
char //字符数据类型 1 字节
short //短整型 2 字节
int //整型 4 字节
long //长整型 4/8 字节 sizeof(long) >= sizeof(int)
long long //更长的整型 8 字节
float //单精度浮点数 4 字节
double //双精度浮点数 8 字节
1、什么是 内置类型?
答:C语言本身自带的类型
2、为什么整型分为 short int long?
答:比如存储年龄 age,用 int 太大了,用 short 就可以。short 取值范围:-32768 ~ 32767
c
#include <limits.h> //注意头文件
//在此头文件下,可以查询内置类型的最大值,最小值
int main()
{
INT_MAX; //转到定义
return 0;
}
类型的意义:
1、使用这个类型开辟内存空间的大小(大小决定了使用范围)。
2、如何看待内存空间的视觉。
1、类型的基本归类
有符号数、无符号数,只针对 整型。
浮点数 没有 有符号数和无符号数 的说法和区分。
(1)整型家族
c
//字符 char 在存储的时候,存储的是 ASCII值,是整型所以归类的时候,放在整型家族。
char
unsigned char
signed char
short
unsigned short
signed short
int
unsigned int
signed int
long
unsigned long [int]
signed long [int]
c
对于整型家族的类型来说,有:"有符号"和"无符号"的区分
1、char 到底是 signed char 还是 unsigned char 不确定
(C语言没有给出明确的规定)
(char 在 VS 上是 signed char)
2、short == signed short
unsigned short
3、int == signed int
unsigned int
4、long == signed long
unsigned long
(2)浮点数家族
c
float
double
(3)构造类型 (自定义类型)
c
1、数组类型
2、结构体类型 struct
3、枚举类型 enum
4、联合类型 union
//int arr1[10] ------> int [10]
//int arr2[20] ------> int [20]
//char arr3[10] ------> char [10]
//数组 arr1 和 arr2 和 arr3 类型各不相同
(4)指针类型
c
1、int* pi;
2、char* pc;
3、float* pf;
4、void* pv;
5、结构体指针类型;
(5)空类型
c
void 表示 空类型(无类型)
通常应用于 函数的返回类型、函数的参数、指针类型。
//void* ps;
二、整型在内存中的存储解析
一个变量的创建,是要在内存中开辟空间的。
空间的大小,是根据不同的类型而决定的。
1、原码、反码、补码
计算机中的整数有 3 种 2进制 表示方法,即原码、反码、补码。
三种表示方法均有 符号位 和 数值位 两部分,符号位都是用 0 表示 "正",用 1 表示 "负",
而数值位:
正整数的原码、反码、补码都相同。
负整数的三种表示方法各不相同。
c
原码
直接将数值按照正负数的形式翻译成二进制就可以得到原码
反码
将原码的符号位不变,其他位依次按位取反就可以得到反码
补码
反码 + 1 就得到补码
对于整型来说:数据存放内存中,其实存放的是补码。
为什么呢?
c
在计算机系统中,数值一律用补码来表示和存储。
原因在于,使用补码,可以将符号位和数值域统一处理;
同时,加法和减法也可以统一处理(CPU只有加法器),
此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
c
为什么用 补码 计算,而不用 原码 计算?
答:用 原码 计算,有些运算会出错
计算 1 - 1 (由于CPU只有加法器,所以 1 - 1 就是 1 + (-1))
1+(-1)
原码 计算
00000000 00000000 00000000 00000001 数值 1
10000000 00000000 00000000 00000001 数值 -1
10000000 00000000 00000000 00000010 相加等于 -2 ,计算错误
补码计算:将 符号位 和 数值域 统一处理
00000000 00000000 00000000 00000001 数值1原码
00000000 00000000 00000000 00000001 数值1反码
00000000 00000000 00000000 00000001 数值1补码
10000000 00000000 00000000 00000001 数值-1原码
11111111 11111111 11111111 11111110 数值-1反码
11111111 11111111 11111111 11111111 数值-1补码
00000000 00000000 00000000 00000001 数值1补码
11111111 11111111 11111111 11111111 数值-1补码
1 00000000 00000000 00000000 00000000 只能存32位
00000000 00000000 00000000 00000000 结果为 0 ,正确
2、大小端字节序介绍
什么是 字节序?
答:以 字节 为单位,讨论存储顺序的。
(1)什么是大端小端?
大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中。
小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中。
(2)为什么有大端和小端?
为什么会有大小端模式之分呢?
这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为 8 bit。但是在C语言中除了8 bit 的 char 之外,还有 16 bit 的 short型 ,32 bit 的 long型 (要看具体的编译器),另外,对于 位数 大于8位 的处理器;例如 16位 或者 32位 的处理器,由于寄存器宽度大于 一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
例如:一个 16bit 的 short型 x,在内存中的地址为 0x0010,x 的值为 0x1122,那么 0x11 为高字节,0x22 为低字节。对于大端模式,就将0x11 放在低地址中,即 0x0010 中,0x22 放在高地址中,即 0x0011 中。小端模式,刚好相反。
我们常用的 x86 结构 是 小端模式,而 KEIL C51 则为大端模式。很多的 ARM,DSP 都为 小端模式。有些 ARM 处理器 还可以由 硬件 来选择是大端模式还是小端模式。
(3)设计一个小程序来判断当前机器的字节序
c
由于 int a;
所以 &a 是 int* 类型
需要判断 int a 的 第1个字节
将 int* 转换为 char* 即可!
再 解引用*
c
#include <stdio.h>
int main()
{
int a = 1;
//char* p = &a; //这么写是错误的,因为 &a 是 int* 类型
char* p = (char*)&a;
if (*p == 1)
{
printf("小端存储\n");
}
else
{
printf("大端存储\n");
}
return 0;
}
c
#include <stdio.h>
int main()
{
int a = 1;
if (*(char*)&a == 1)
{
printf("小端存储\n");
}
else
{
printf("大端存储\n");
}
return 0;
}
c
#include <stdio.h>
int check_sys()
{
int a = 1;
if (*(char*)&a == 1) //再精简为:if(*(char*)&a)
{
return 1;
}
else
{
return 0;
}
}
int main()
{
int ret = check_sys();
if (ret == 1)
{
printf("小端存储\n");
}
else
{
printf("大端存储\n");
}
return 0;
}
c
#include <stdio.h>
int check_sys()
{
int a = 1;
return *(char*)&a; //一步步精简成这样
}
int main()
{
int ret = check_sys();
if (ret == 1)
{
printf("小端存储\n");
}
else
{
printf("大端存储\n");
}
return 0;
}
3、练习
(1)
c
对于整型家族的类型来说,有:"有符号"和"无符号"的区分
1、char 到底是 signed char 还是 unsigned char 不确定
(C语言没有给出明确的规定)
(char 在 VS 上是 signed char)
2、short == signed short
unsigned short
3、int == signed int
unsigned int
4、long == signed long
unsigned long
c
#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); // a=-1 b=-1 c=255
return 0;
}
c
//知识点(之前学习过)
//1、长度大于 int 的,就不需要进行 "整型提升"
//2、有符号的整型提升:高位补充符号位
//3、无符号的整型提升,高位补 0
//4、%d 十进制的形式打印有符号整型整数
//正确的解答过程
//整数 -1 存入 char a 中
//整数 -1 原码:10000000 00000000 00000000 00000001
// 反码:11111111 11111111 11111111 11111110
// 补码:11111111 11111111 11111111 11111111
//截断,因为只有 1 字节 才能存入 char a 中:11111111
//%d 十进制的形式打印有符号整型整数
//对char a 进行 整型提升
//补码:11111111 11111111 11111111 11111111
//反码:10000000 00000000 00000000 00000000
//补码:10000000 00000000 00000000 00000001
//结果显示:-1
//signed char b 和 char a 是一样的解答过程
//在本电脑中的VS编译器,char 类型是 signed char
//unsigned char c
//整数 -1 存入 unsigned char c 中
//整数 -1 原码:10000000 00000000 00000000 00000001
// 反码:11111111 11111111 11111111 11111110
// 补码:11111111 11111111 11111111 11111111
//截断,因为只有 1 字节 才能存入 unsigned char c 中:11111111
//无符号整型提升,高位补 0
//补码:00000000 00000000 00000000 11111111
//%d 十进制的形式打印有符号整型整数
//因为此时已经按照 %d 的形式输出,最高位为 0,是 正数
// 正数的原码、反码、补码相同
//原码:00000000 00000000 00000000 11111111
//结果:255
(2)
c
#include <stdio.h>
int main()
{
char a = -128;
printf("%u\n", a);
return 0;
}
c
//知识点:
//%u 十进制的形式打印无符号的整型整数
//整数-128,存入 char a 中
//-128补码:10000000 00000000 00000000 10000000
// 反码:11111111 11111111 11111111 01111111
// 补码:11111111 11111111 11111111 10000000
//截断:10000000 存入 char a 中
//%u 十进制的形式打印无符号的整型整数
//整型提升,也就是对 char a 进行整型提升
//char a 是有符号的,符号位是 1
//补码:11111111 11111111 11111111 10000000
//%u 是无符号数,内存中就没有符号位,内存中存入的就是:11111111 11111111 11111111 10000000
//无符号数可以看成 正数,补码,反码,原码都是一样的
//结果就是:11111111 11111111 11111111 10000000
//十进制就是:4294967168
(3)
c
#include <stdio.h>
int main()
{
char a = 128;
printf("%u\n", a);
return 0;
}
c
//知识点:
//%u 十进制的形式打印无符号的整型整数
//整数128,存入 char a 中
//128补码:00000000 00000000 00000000 10000000
//正数的原码、反码、补码相同
//截断:10000000 存入 char a 中
//%u 十进制的形式打印无符号的整型整数
//整型提升,也就是对 char a 进行整型提升,char a 是有符号的,符号位为 1
//char a 是有符号的,符号位是 1
//补码:11111111 11111111 11111111 10000000
//%u 是无符号数,内存中就没有符号位,内存中存入的就是:11111111 11111111 11111111 10000000
//无符号数可以看成 正数,补码,反码,原码都是一样的
//结果就是:11111111 11111111 11111111 10000000
//十进制就是:4294967168
(4)
c
#include <stdio.h>
int main()
{
int i = -20;
unsigned int j = 10;
printf("%d\n", i + j);
return 0;
}
c
//知识点:
//1、补码得到原码有两种方式:
// (1)补码-1得到反码,反码取反得到原码
// (2)补码先取反再加1,得到原码
//整数-20原码:10000000 00000000 00000000 00010100
// 反码:11111111 11111111 11111111 11101011
// 补码:11111111 11111111 11111111 11101100
//unsigned int j = 10原码:00000000 00000000 00000000 00001010
//无符号数的反码、原码、补码相同
// 补码:00000000 00000000 00000000 00001010
//i+j 补码相加 11111111 11111111 11111111 11101100
// 00000000 00000000 00000000 00001010
// 11111111 11111111 11111111 11110110
//截断:11111111 11111111 11111111 11110110
// (1)补码-1得到反码,反码取反得到原码
//补码:11111111 11111111 11111111 11110110
//反码:11111111 11111111 11111111 11110101
//原码:10000000 00000000 00000000 00001010
//结果:-10
//(2)补码先取反再加1,得到原码
//补码:11111111 11111111 11111111 11110110
//取反:10000000 00000000 00000000 00001001
//+1 :10000000 00000000 00000000 00001010
//结果:-10
(5)
c
#include <stdio.h>
#include <windows.h>
int main()
{
unsigned int i;
for (i = 9; i >= 0; i--)
{
printf("%u\n", i);
Sleep(1000); //单位:毫秒 5000毫秒
}
return 0;
}
死循环,因为 unsigned int i 是 恒大于等于零 的。
c
//9 8 7 6 5 4 3 2 1 0 的原码、反码、补码 相同
//0 原码:00000000 00000000 00000000 00000000
//-1 原码:10000000 00000000 00000000 00000001
// 反码:11111111 11111111 11111111 11111110
// 补码:11111111 11111111 11111111 11111111
//0-1 0+(-1)
//0 补码:00000000 00000000 00000000 00000000
//-1补码:11111111 11111111 11111111 11111111
//相加 :11111111 11111111 11111111 11111111
//以 %u 输出:4294967295
(6)比较难,好好理解
c
#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\n", a[i]);
}
printf("%d\n", strlen(a)); //答案是:255
return 0;
}
结果:255
原因:strlen() 统计的是 '\0',之前出现的数字的个数!!!
(7)好题
c
#include <stdio.h>
unsigned char i = 0; //unsigned char 的取值范围:0 ~ 255
int main()
{
for (i = 0; i <= 255; i++) //此时:i<=255 条件恒成立,所以死循环
{
printf("hell0 world\n"); //死循环
}
return 0;
}
微软雅黑字体
黑体
3号字
4号字
红色
绿色
蓝色