跳到主要内容

数据类型与进制转换

char 类型

基础信息

  • 字符型。
  • 单位:一个字节(8 bit 位)。
  • 格式匹配符:
    • 数值型:
      • 有符号:%hhd —— char 显示数值专用格式匹配符。
      • 无符号:%hhu —— unsigned char 显示数值专用格式匹配符。
    • 字符型:%c
  • 取值范围:
    • 有符号:-128 ~ 127
    • 无符号:0 ~ 255

程序获取

#include <stdio.h>
#include <limits.h>
int main(void)
{
// 获取无符号数取值范围
printf("char 无符号 min = 0,max = %hhu\n", UCHAR_MAX);
// 获取有符号数取值范围
printf("char 有符号 min = %hhd,max = %hhd\n", CHAR_MIN, CHAR_MAX);
// 获取 char 占用的字节数
printf("char 大小 = %u\n", sizeof(char));
// 获取 unsigned char 占用的字节数
printf("unsigned char 大小 = %u\n", sizeof(unsigned char));
return 0;
}

ASCII 码

char 类型数据,数值对应一个 ASCII 码。

#include <stdio.h>
int main(void)
{
char ch = 'A'; // 定义变量 ch, 指定初值为 'A';
printf("ch = %c\n", ch); // c: character %c 用来显示字符的 格式匹配符。
ch = 'm'; // 给变量ch 赋值成 'm', 覆盖 原来的 'A';
printf("ch = %c\n", ch);
ch = 97; // 使用 范围内的数值 97 ,给 ch 赋值。
printf("ch = %c\n", ch); // 将数值97,按照字符格式打印输出。
ch = 98; // 使用 范围内的数值 98 ,给 ch 赋值。
printf("ch = %c\n", ch); // 将数值98,按照字符格式打印输出。
return 0;
}

ASCII 表说明

  • 0 ~ 32 的 ASCII 码对应的字符都不可见。
  • 常用的 ASCII 码:
    • 'a':ASCII 码值 97
    • 'A':ASCII 码值 65
    • '0':ASCII 码值 48
    • '\n':ASCII 码值 10
    • '\t':制表符,tab 键对应的字符(ASCII 码值 9)
    • '\0':ASCII 码值 0

练习:将大写字母转换成小写字母

#include <stdio.h>
int main(void)
{
char ch = 'R'; // char 变量定义
printf("R 转换的小写为:%c\n", ch+32); //ch+32 表达式,对应格式匹配符 %c
char ch2 = 'h'; // char 变量定义
printf("h 转换的大写为:%c\n", ch2-32); //ch2-32 表达式,对应格式匹配符 %c
char ch3 = '5';
// 借助字符5, 利用 ASCII特性,打印出 字符9
printf(" 打印字符9 = %c\n", ch3+4);
return 0;
}

转义字符

练习:

  • 在一个 printf 函数中,打印输出 hello 换行 world 换行。
  • 一个 printf 中打印 hello(一个 tab 缩进)world 换行。

说明:

  • '/':自右向左划,叫做“斜杠”。
  • '\':自左向右划,叫做“反斜杠”。——是转义字符。

转义字符的作用:

  1. 将普通字符,转换为特殊意。
    • 如:'\n':这是一个字符,代表换行。
    • 如:'\t':这是一个字符,代表一个制表符。
  2. 将特殊字符,还原成本身意。
    • 如:\\n:这样就将一个字符 '\n',还原成两个字符:'\''n'

hello 换行 world 换行的两种方法:

// 方法1
int main(void)
{
printf("hello\nworld\n");
return 0;
}
// 方法2
int main(void)
{
char ch = '\n'; //定义ch变量,初值为'\n'
printf("hello%cworld%c", ch, ch); // 等价于 printf("hello\nworld\n");
return 0;
}

hello(tab)world 换行:

int main(void)
{
char ch1 = '\t'; // 实现 一个tab缩进
char ch2 = '\n'; // 实现 一个换行。

printf("hello%cworld%c", ch1, ch2);
return 0;
}

练习:将特殊字符转换成本身意。

题目:屏幕上严格输出如下内容:【 '\n' 的值是 10】,要求显示时要有 ' '

int main(void)
{
printf("'\\n'的值是 %hhd", '\n');
return 0;
}

实型(浮点型)【了解】

用于显示小数。

基础信息

  • float:单精度浮点型。%f,大小 4 字节(可以使用 sizeof() 求取)。
    • 4.35 默认会被编译器理解为 double 类型。
    • float v = 4.567f; 编译器理解为 float。
    • %f 格式匹配符,默认保留 6 位小数。
  • double:双精度浮点型。%lf,大小 8 字节。
    • double d = 5.68;

取值范围

使用头文件 #include <float.h> 获取浮点型取值范围。

int main(void)
{
printf("float 范围:%f ~ %f\n", FLT_MIN, FLT_MAX);
printf("double 范围:%lf ~ %lf\n", DBL_MIN, DBL_MAX);
return 0;
}

精度问题

  • float 类型:精度 6~7 位:
    • 整数部分 + 小数部分 ≤ 6 位,准确。
    • 整数部分 + 小数部分 == 7 位,可能准确,也可能不准确。
    • 整数部分 + 小数部分 > 7 位,不准确。
  • double 类型:精度 15~16 位:
    • 整数部分 + 小数部分 ≤ 15 位,准确。
    • 整数部分 + 小数部分 == 16 位,可能准确,也可能不准确。
    • 整数部分 + 小数部分 > 16 位,不准确。
  • 不同平台(操作系统),对应 float、double 类型实现的精度有可能不同。以上是 Windows 下特性。
  • float 和 double 不存在“无符号”类型。

bool 类型

C 语言原来没有 bool 类型。C99 标准中新增了 bool 类型。C++ 自带 bool 类型。

用处,表示:

  • 好、坏
  • 真、假
  • 对、错
  • 是、否

取值:

  • true —— 真 —— 1
  • false —— 假 —— 0

C 语言使用 bool 的条件:

  • 编译器要支持 c99 标准。
  • 导入 #include <stdbool.h>

bool 类型的大小:1 字节(sizeof() 求取)。

bool 没有专用的格式匹配符,打印时使用 %d 来打印。

int main(void)
{
bool aa = true; // 定义bool 类型变量 aa , 初值为 true == 1
printf("aa = %d\n", aa);
aa = false; // 给 bool 类型变量 aa 赋值为 false == 0
printf("aa = %d\n", aa);
return 0;
}

进制和转换

计算机只使用 2 进制。8 进制、10 进制、16 进制统统都是给人类用的!!!

存储知识

  • 1 bit 位就是一个二进制位,存 0 或 1。
  • 一个字节(Byte)1B = 8 bit 位。
  • “内存单元”是计算机内存存储的最小单位,一个内存单元 == 1 字节。
  • 1KB = 1024B
  • 1MB = 1024KB
  • 1GB = 1024MB
  • 1TB = 1024GB

2 进制

  • 取值 1 或 0。逢 2 进 1,借 1 当 2。
  • 没有格式匹配符。
  • 10 转 2:除 2 反向取余法。
  • 2 转 10:掌握 2^0 ~ 2^10 各自的取值:
2^012^664
2^122^7128
2^242^8256
2^382^9512
2^4162^101024
2^532

计算方法:对应二进制位,有 1 累加 2 的指数次幂值,有 0 略过。

  • 11101 —— 29
  • 10110 —— 2 + 4 + 16 = 22

8 进制

  • 逢 8 进 1,借 1 当 8。
  • 取值:0/1/2/3/4/5/6/7,最大值 7。
  • 格式匹配符:%o%#o
  • 表示语法:0 开头。
  • 8 进制 → 10 进制
    • 056 == 8^0 × 6 + 8^1 × 5 = 6 + 40 = 46
    • 0123 = 8^0 × 3 + 8^1 × 2 + 8^2 × 1 = 3 + 16 + 64 = 83
  • 10 进制 → 8 进制:除 8 反向取余法。135 —— 0207
  • 8 转 2:将 8 进制数,自左向右,每一位用 421 码展开。
    • 练习:056 —— 10111004735 —— 1001 1101 1101053261 —— 101 0110 1011 0001
  • 2 转 8:自右向左,每 3 个二进制位一组,不足 3 位补 0。
    • 1101010101111010 → 分组:1 101 010 101 111 010 → 补齐:001 101 010 101 111 010 → 八进制数为:0152572
    • 10110111011 → 分组:10 110 111 011 → 补齐:010 110 111 011 → 八进制数为:02673

16 进制

  • 逢 16 进 1,借 1 当 16。
  • 取值:0~9,10-A/a,11-B/b,12-C/c,13-D/d,14-E/e,15-F/f,最大值 F/f。
  • 格式匹配符:%x%#x
  • 表示语法:0x 开头。
  • 16 进制 → 10 进制:将每一个 16 进制位展开相加得 10 进制。
  • 10 进制 → 16 进制:除 16 反向取余法。370x25430x2B
  • 16 转 2:将 16 进制数,自左向右,每一位用 8421 码展开。
    • 0xFAC121111 1010 1100 0001 0010
  • 2 转 16:自右向左,每 4 个二进制位一组,不足 4 位补 0。
    • 101001011101011001 → 分组:10 1001 0111 0101 1001 → 补齐:0010 1001 0111 0101 1001 → 16 进制数为:0x29759
    • 1110110100111101 → 分组:1110 1101 0011 1101 → 补齐:1110 1101 0011 1101 → 16 进制数为:0xED3D

小结

使用频率最高的是 10 进制,次高的是 16 进制。

int m = 0x15F4;
int n = 0345;
int k = 0101101;
  • 不能直接将变量值赋值为 2 进制。
  • 上述的赋值,会被编译器理解为 8 进制数。

常用的格式匹配符

格式匹配符说明
%d有符号十进制整数(int)
%c字符
%x十六进制整数
%u无符号十进制整数
%s字符串(后续课程讲,打印字符串)
%o八进制整数
%#o八进制整数(带前缀 0)
%#x十六进制整数(带前缀 0x)
%hhdsigned char(数值)
%hdshort
%ldlong
%lldlong long
%hhuunsigned char(数值)
%huunsigned short
%luunsigned long
%lluunsigned long long
%ffloat
%lfdouble

进制输出示例:

#include <stdio.h>
int main(void)
{
int a = 56; // 10进制数作a变量初始化。 —— 定义。
printf("10进制:a = %d\n", a);
printf("8进制:a = %o\n", a);
printf("8进制:a = %#o\n", a); //在%和x中间添加#,可以在输出时,显示8进制前缀。
printf("16进制:a = %x\n", a);
printf("16进制:a = %#x\n", a); //在%和x中间添加#,可以在输出时,显示16进制前缀。
return 0;
}

编码和存储

无符号存储

unsigned int a = 12; // 占用 4 字节 32 个 bit位存储。
空间:00000000 00000000 00000000 00000000 ———— 4 字节。
存储:00000000 00000000 00000000 00001100

unsigned short b = 15; //占用 2 字节 16 个 bit位存储。
空间:00000000 00000000 ———— 2 字节。
存储:00000000 00001111

有符号存储

  • 需要拿出一个二进制位,专门存储符号,标识正、负。
  • 选用最高位为符号位。
    • 正:0
    • 负:1

有符号正数

采用“源码”存储。

int a = 5;
空间:0 0000000 00000000 00000000 00000000
存储:0 0000000 00000000 00000000 00000101 --- 表示存储正数 5

有符号负数

有符号的负数,采用“补码”存储。

  • 源码:数值的二进制位直接存储。
  • 反码:符号位不变,将其余数值位取反。
  • 补码:反码 + 1。
int b = -33;
空间:0 0000000 00000000 00000000 00000000
源码:1 0000000 00000000 00000000 00100001
反码:1 1111111 11111111 11111111 11011110
补码:1 1111111 11111111 11111111 11011111 —— 负33 在计算机中实际的存储形式。

小结

  • int —— 4 字节 —— 32 bit 位
    • 有符号:31 个数值位。取值范围 -2^31 ~ 2^31 - 1 → -2147483648 ~ +2147483647
    • 无符号:32 个数值位。取值范围 0 ~ 2^32 - 1 → 0 ~ 4294967295
  • short —— 2 字节 —— 16 bit 位
    • 有符号:15 个数值位。取值范围 -2^15 ~ 2^15 - 1 → -32768 ~ 32767
    • 无符号:16 个数值位。取值范围 0 ~ 2^16 - 1 → 0 ~ 65535

因此,知道数据类型占用内存的大小,就能算出该类型无符号数、有符号数对应的取值范围。

数据溢出

📷(此处原文为配图,略)

不常用关键字

📷(此处原文为配图,略)