Skip to content

变量

c
// 数据类型 变量名 = 值
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)才可以, 所以 为了存储不同的数据, 才需要不同的数据类型, 好让编译器知道你的数据应该分配多大的空间来保存

类型转换

语法: (类型)被转换的表达式

c
#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);
}

常量

c
#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 表中字符对应的数字

c
#include <stdio.h>
int main() {
  char n = 'A';
  printf("%d", n); // 65 -> 输出字符的 Ascii 码
}

为什么 char 类型最大只能是 127

  1. 因为计算只认识二进制, 而 char 类型只有 1 个字节(8 位)
  2. 默认情况下第一个位置是 符号位 用来表示正数(0)和负数(1), 真正用于存储数据的只有 7 位置
  3. 127 的二进制是 01111111, 已经是 8 位能存储的最大10 进制值
  4. 在线进制转换工具
c
#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
c
#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 个字节
c
#include <stdio.h>
int main() {
  float n1 = 3.14;
  double n2 = 3.1415926;
  printf("%f \n %f", n1, n2);
}

数组类型

c
#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]);
  }
}

枚举类型

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

结构体类型

c
#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);
}

联合体类型

c
#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);
}

指针类型

c
#include <stdio.h>

int main() {
  int num = 10;
  int *ptr = &num; // 指针存储的 num 的内存地址

  printf("ptr is %p \n", ptr); // 输出内存地址

  // 指针对应内存地址的数据被修改为20
  *ptr = 20;

  // 此时再次获取 num 的值已经是被修改后的值了
  printf("num is %d \n", num); // 20
}

数据的编码规则

数据是怎样从十进制变成二进制存储的, 所有笔记都以 8 位 作为参考, 方便记录

字符

前面已经学习过了, 每一个字符对应的就是一个 ASCII 表中的一个数字, 所以说还是得研究 10 进制如何变成二进制的

主要研究的是有符号数

因为无符号数(都是正数)8个位置存储的都是数据, 不用存储符号,

所以无符号数的二进制就是它(它指 10 -> 2 进制转换的结果)本身

原码

最高位为符号位, 对其他的位进行本身的绝对值

txt
有符号十进制的 5  的二进制原码: 0 000 0101
有符号十进制的 -5 的二进制原码: 1 000 0101

反码

  • 正数: 反码和原码相同
  • 负数: 符号位一定是 1, 其余数据位对原码取反
txt
正数: 反码和原码相同
有符号十进制的 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)
txt
正数: 补码和原码相同
有符号十进制的 5  的二进制原码: 0 000 0101
有符号十进制的 5  的二进制补码: 0 000 0101

负数: 首先获取反码(上面已经获取过了,也知道如何计算反码了)
有符号十进制的 -5 的二进制反码: 1 111 1010
加一个二进制的一  : 0 000 0001
得到的结果就是补码: 1 111 1011

结论

如果是正数, 存储的是原码, 如果是负数存储的是补码

练习

txt
--------------------------------
写出 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
--------------------------------

Released under the MIT License.