变量
// 数据类型 变量名 = 值
char char_a = 'A';
short num1 = 1;
int num2 = 10;
float num3 = 3.14;
long num4 = 5000;
long long num5 = 800000;
变量类型
- char
- short
- int
- float
- long
- ....
为什么有这么多数据类型?
因为计算机只能识别 0
1
表示通电,未通电,所以计算机只能认识二进制, 比如要存储一个 3
那么二进制就是 11
此时只需要 2 个位置(bit)就够了, 但是 如果要存储一个 5
那么二进制就是 0101
此时就需要 4 个位置(bit)才可以, 所以 为了存储不同的数据, 才需要不同的数据类型, 好让编译器知道你的数据应该分配多大的空间来保存
类型转换
语法: (类型)被转换的表达式
#include <stdio.h>
int main() {
char c = 'C';
int n = (int)c;
printf("n value is : %d \n", n);
int x = (float)c + 1;
printf("f value is : %d \n", x);
}
常量
#include <stdio.h>
int main() {
const int a = 1;
a = 2;
// 常量是不能修改的, 编译器报错
// error: cannot assign to variable 'a' with const-qualified type 'const int'
}
存储单位
Bit : 位(比特)
Byte : 字节(操作系统最小存储单位) (1b = 8bit)
KB : 千字节(1kb = 1024b)
MB : 兆字节(1mb = 1024kb)
GB : 吉字节(1gb = 1024mb)
TB : 太字节(1tb = 1024gb)
字(Word):
- 现代计算机中一个字通常由若干个字节组成: 4 字节、8 字节等
- 字长是计算机一次能够处理的数据位数
- 字的大小在不同的计算机体系结构中有所不同, 在 32 位系统中一个字是 4 个字节, 在 64 位系统中一个字是 8 个字节
位和字的关系
位和字的组成关系:
- 一个字(Word)有多个位(Bit)组成, 32位操作系统, 一个字由 32 个位组成
- 字的长度决定了它能表示的数据范围和精度
- 较长的字可以表示更大范围的数值和更复杂的数据结构
位和字存储和处理关系
计算机在存储和处理数据时, 通常是以字为单位进行的, 内存中的数据存储和 CPU 的运算操作都是基于字进行的, 位则是构成字的基本元素, 通过对位的组合和操作来实现对字的处理
数据类型
基本数据类型
有些还未学到, 可以先看看
整数类型
- char: 占 1 个字节(8位)
- short: 占 2 个字节(16位)
- int: 占 4 个直接(32位)
- long: 在 32 位系统占 4 个字节, 在 64 位系统中 8 个 或 4 个字节(编译器不同可能影响)
- long long: 占 8 个字节(64位)
为什么 char 类型只能存储一个字符
其实存储的不是一个字符而是 ASCII 表中字符对应的数字
#include <stdio.h>
int main() {
char n = 'A';
printf("%d", n); // 65 -> 输出字符的 Ascii 码
}
为什么 char 类型最大只能是 127
- 因为计算只认识二进制, 而 char 类型只有 1 个字节(8 位)
- 默认情况下第一个位置是
符号位
用来表示正数(0)和负数(1), 真正用于存储数据的只有 7 位置 - 127 的二进制是 01111111, 已经是 8 位能存储的最大10 进制值
- 在线进制转换工具
#include <stdio.h>
int main() {
char n = 127;
printf("%d", n); // 65 -> 输出字符的 Ascii 码
// 编译器报警告:
// warning: implicit conversion from 'int' to 'char' changes value from 128 to -128
char n = 128;
printf("n is %d", n); // -128
printf("\n --- \n");
char n2 = 129;
printf("n is %d", n2); // -127
}
有符号和无符号
- 默认情况下, 第一位是
符号位
,无符号表示第一个位置也用于存储数据二不是用于存储符号
- 有符号可以存储负数, 无符号不能存储负数
- 也就是说无符号的 char 类型, 能表示的最大的值是
11111111
也就是 10 进制的255
#include <stdio.h>
int main() {
unsigned char n = 255;
printf("n is %d \n", n); // 255
// 编译器警告:
// warning: implicit conversion from 'int' to 'unsigned char' changes value from 256 to 0
unsigned char n2 = 256;
printf("n is %d \n", n2); // 0
// 编译器警告:
// warning: implicit conversion from 'int' to 'unsigned char' changes value from 257 to 1
unsigned char n3 = 257;
printf("n is %d \n", n3); // 1
}
浮点数类型
- float: 单精度浮点数, 占 4 个字节
- double: 双精度浮点数, 占 8 个字节
#include <stdio.h>
int main() {
float n1 = 3.14;
double n2 = 3.1415926;
printf("%f \n %f", n1, n2);
}
数组类型
#include <stdio.h>
int main() {
// 类型 变量明[数组长度] = {数组元素1, 数组元素2 ...数组元素n}
int nums[3] = {1, 3, 5};
// 遍历数组
for (int i = 0; i < 3; i++) {
printf("item is %d\n", nums[i]);
}
}
枚举类型
#include <stdio.h>
// 定义枚举(全部大写)
enum SEASON {
SPRING, // 0 春
SUMMER, // 1 夏
AUTUMN, // 2 秋
WINTER // 3 冬
};
int main() {
// 给变量赋值(使用枚举的值)
enum SEASON current = SUMMER;
switch(current) {
case SPRING:
printf("现在是春天");
break;
case SUMMER:
printf("现在是夏天");
break;
case AUTUMN:
printf("现在是秋天");
break;
case WINTER:
printf("现在是当天");
break;
}
}
结构体类型
#include <stdio.h>
// 定义结构体(类似class的概念)
struct Person {
unsigned int age; // 年龄
char name[64]; // 名称
};
int main() {
// 类似实例化的概念
struct Person p = { 18, "tom" };
// 取值
printf("My name is %s, I'm %d years old.", p.name, p.age);
printf("\n --- \n");
// 赋值
p.age = 20;
printf("My name is %s, I'm %d years old.", p.name, p.age);
}
联合体类型
#include <stdio.h>
#include <string.h>
// 定义联合体
union User {
unsigned int age;
char name[64];
};
int main() {
union User u = {
// 初始化时候只能初始化其中一个成员变量
.age = 10
};
strcpy(u.name, "hack"); // 给 u.name 赋值
// 取值
printf("name: %s, age: %d", u.name, u.age);
printf("\n --- \n");
// 赋值
u.age = 20;
printf("name: %s, age: %d", u.name, u.age);
}
指针类型
#include <stdio.h>
int main() {
int num = 10;
int *ptr = # // 指针存储的 num 的内存地址
printf("ptr is %p \n", ptr); // 输出内存地址
// 指针对应内存地址的数据被修改为20
*ptr = 20;
// 此时再次获取 num 的值已经是被修改后的值了
printf("num is %d \n", num); // 20
}
数据的编码规则
数据是怎样从十进制变成二进制存储的, 所有笔记都以 8 位
作为参考, 方便记录
字符
前面已经学习过了, 每一个字符对应的就是一个 ASCII 表中的一个数字, 所以说还是得研究 10 进制如何变成二进制的
主要研究的是有符号数
因为无符号数(都是正数)8个位置存储的都是数据, 不用存储符号,
所以无符号数的二进制就是它(它指 10 -> 2 进制转换的结果
)本身
原码
最高位为符号位, 对其他的位进行本身的绝对值
有符号十进制的 5 的二进制原码: 0 000 0101
有符号十进制的 -5 的二进制原码: 1 000 0101
反码
- 正数: 反码和原码相同
- 负数: 符号位一定是 1, 其余数据位对原码取反
正数: 反码和原码相同
有符号十进制的 5 的二进制原码: 0 000 0101
有符号十进制的 5 的二进制反码: 0 000 0101
所谓数据位取反,就是原来位置是 0 取反就是 1, 原来位置是 1 取反就是 0, 所以:
-5 的原码是: 1(符号位置不变) 0(取反) 0(取反) 0(取反) 0(取反) 1(取反) 0(取反) 1(取反)
-5 的反码是: 1(符号位置不变) 1(结果) 1(结果) 1(结果) 1(结果) 0(结果) 0(结果) 1(结果)
所以得到的反码就是: 1 111 1001
补码
- 正数: 补码和原码想同
- 负数: 符号位一定是 1, 然后用反码加一(二进制的 1)
正数: 补码和原码相同
有符号十进制的 5 的二进制原码: 0 000 0101
有符号十进制的 5 的二进制补码: 0 000 0101
负数: 首先获取反码(上面已经获取过了,也知道如何计算反码了)
有符号十进制的 -5 的二进制反码: 1 111 1010
加一个二进制的一 : 0 000 0001
得到的结果就是补码: 1 111 1011
结论
如果是正数, 存储的是原码, 如果是负数存储的是补码
练习
--------------------------------
写出 7 的原码,反码,补码:
原码: 0 000 0111
反码: 0 000 0111
补码: 0 000 0111
--------------------------------
写出 -7 的原码,反码,补码:
原码: 1 000 0111
反码: 1 111 1000
↓ ↓ ↓
加一: 0 000 0001
↓ ↓ ↓
补码: 1 111 1101
--------------------------------
写出 -3 的原码,反码,补码:
原码: 1 000 0011
反码: 1 111 1100
↓ ↓ ↓
加一: 0 000 0001
↓ ↓ ↓
补码: 1 111 1101
--------------------------------
写出 -4 的原码,反码,补码:
原码: 1 000 0100
反码: 1 111 1011
↓ ↓ ↓
加一: 0 000 0001
↓ ↓ ↓
补码: 1 111 1100
--------------------------------