飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 编程基础概念

    • 冯·诺依曼体系结构
    • 数据在计算机中的表示
    • 编程语言的层次
    • C语言的起源与发展
    • C99标准的主要改进
    • 开发环境搭建
    • 第一个C程序
    • 编译与运行流程
    • 可移植性风险的三级体系
  • 第2章 数据类型与运算

    • 字符集与标识符
    • 关键字
    • 注释
    • char 类型
    • short 与 int
    • long 与 long long
    • 有符号与无符号
    • 取值范围与 limits.h
    • float 与 double
    • long double
    • _Bool 类型
    • 变量声明与定义
    • 常量
    • 转义序列
    • 算术运算符
    • 赋值运算符
    • 自增自减运算符
    • 关系与判等运算符
    • 逻辑运算符
    • 位运算符
    • 条件运算符
    • 逗号运算符
    • 运算符优先级
    • 隐式类型转换
    • 显式类型转换
  • 第3章 控制流

    • 表达式语句与空语句
    • 复合语句
    • if 语句
    • switch 语句
    • while 循环
    • do-while 循环
    • for 循环
    • break 与 continue
    • goto 语句
    • return 语句
  • 第4章 函数与模块化编程

    • 函数定义
    • 函数声明与原型
    • main 函数
    • 函数调用机制
    • 传值调用
    • 数组参数
    • 作用域
    • 存储期
    • 链接属性
    • static 与 extern
    • 递归
    • 头文件与源文件
    • 头文件保护
    • include 规则
  • 第5章 数组与字符串

    • 一维数组声明与初始化
    • 数组的存储模型
    • 数组访问与越界
    • 数组操作
    • 二维数组
    • 变长数组 VLA
    • 字符串基础
    • 字符串输入输出
    • 字符串处理函数
    • 字符串与数字转换
  • 第6章 指针

    • 指针的概念
    • 指针的声明与使用
    • 指针运算
    • const 与指针
    • 数组名与指针
    • 指针遍历数组
    • 指针与多维数组
    • 指针作为函数参数
    • 函数返回指针
    • 函数指针
    • 二级指针
    • 复杂声明解析
  • 第7章 结构体、联合体与枚举

    • 结构体定义与声明
    • 结构体初始化
    • 结构体成员访问
    • 结构体嵌套
    • 结构体指针
    • 结构体与函数
    • 联合体
    • 联合体与类型双关
    • 枚举类型
    • 位域
    • 内存对齐与填充
  • 第8章 动态内存管理

    • malloc 与 free
    • calloc 与 realloc
    • 内存泄漏
    • 悬垂指针
    • 内存分配策略
    • 自定义内存池
    • Valgrind 与内存检测
    • 内存碎片
    • 内存对齐分配
    • 常见内存错误
  • 第9章 文件输入输出

    • 文件打开与关闭
    • 文本读写
    • 格式化输入输出
    • 二进制读写
    • 文件定位
    • 错误处理
    • 标准流
    • 临时文件
    • 文件操作示例
  • 第10章 预处理器

    • 预处理器基础
    • 宏定义
    • 带参数的宏
    • 条件编译
    • 头文件包含
    • 预定义宏
    • 宏的高级技巧
    • 预处理器陷阱
    • 编译器特定扩展
  • 第11章 标准库概览

    • 标准库概述
    • assert.h
    • ctype.h
    • errno.h
    • float.h
    • limits.h
    • locale.h
    • math.h
    • setjmp.h
    • signal.h
    • stdarg.h
    • stddef.h
    • stdlib.h
  • 第12章 进阶主题

    • 内联函数
    • 变长数组 VLA
    • 复数类型
    • 布尔类型
    • stdint 与 inttypes
    • 灵活数组成员
    • 匿名结构体与联合体
    • 静态断言
    • 线程支持
    • 原子操作

数据在计算机中的表示

计算机底层只认识 0 和 1,所有数据——数字、文字、图像、声音——最终都要编码成二进制。理解这些编码方式,能帮助你写出更高效的 C 程序,也能避免许多与类型、溢出相关的隐蔽错误。

二进制、八进制与十六进制

二进制是计算机的基础,每一位只能是 0 或 1。八进制和十六进制是二进制的紧凑写法,在 C 语言中直接支持:

int dec = 42;       /* 十进制 */
int oct = 052;      /* 八进制:以 0 开头,等于十进制 42 */
int hex = 0x2A;     /* 十六进制:以 0x 开头,等于十进制 42 */

printf("%d %d %d\n", dec, oct, hex);   /* 输出:42 42 42 */
printf("%o %x\n", dec, dec);            /* 输出:52 2a */

十六进制特别适合表示内存地址和位模式,因为每 4 位二进制恰好对应 1 位十六进制数字:0xFF = 1111 1111。

位、字节与字

**位(bit)**是信息的最小单位,取值 0 或 1。

**字节(byte)**由 8 个位组成,是内存寻址的最小单位。C 语言中 sizeof(char) 恒为 1,而 1 字节等于 8 位(CHAR_BIT 在 <limits.h> 中定义,标准规定至少为 8)。

#include <limits.h>

printf("CHAR_BIT = %d\n", CHAR_BIT);    /* 通常是 8 */
printf("sizeof(char) = %zu\n", sizeof(char));   /* 恒为 1 */

**字(word)**是 CPU 一次能处理的整数位数,与架构相关:32 位系统的字长是 32 位(4 字节),64 位系统是 64 位(8 字节)。C 语言中的 int 通常等于机器字长,但标准只保证它至少为 16 位。

整数的存储模型

整数在内存中以二进制补码(Two's Complement)形式存储,这是现代计算机的通用标准。

无符号整数:直接按二进制存储,所有位都参与表示数值。

unsigned char a = 200;      /* 二进制:1100 1000 */

有符号整数:最高位是符号位,0 表示正,1 表示负。负数的表示采用补码——对其绝对值的二进制取反再加 1。

signed char b = -5;
/* 5 的二进制:0000 0101 */
/* 取反:      1111 1010 */
/* 加 1:      1111 1011  → 这就是 -5 的补码表示 */

补码的优势在于:加减法可以用同一套电路处理,符号位自然参与运算。

signed char x = 127;
signed char y = x + 1;      /* 0111 1111 + 1 = 1000 0000 = -128,溢出! */

有符号整数溢出在 C99 中是未定义行为,编译器可能做出各种优化假设,实际结果取决于具体平台和编译器。

浮点数的存储模型

浮点数采用 IEEE 754 标准,将二进制科学计数法编码为三部分:

  • 符号位:1 位,0 为正,1 为负
  • 指数位:存储指数(经过偏移处理)
  • 尾数位:存储小数部分
float f = 0.1f;
/* 实际存储的二进制值并非精确的 0.1 */
printf("%.20f\n", f);   /* 输出:0.10000000149011611938 */

由于二进制无法精确表示某些十进制小数(如 0.1、0.2),浮点数运算存在固有精度误差:

if (0.1 + 0.2 == 0.3)           /* 可能为假! */
    printf("Equal\n");
else
    printf("Not equal\n");       /* 通常输出这个 */

/* 正确做法:使用误差范围 */
#define EPSILON 1e-6
if (fabs((0.1 + 0.2) - 0.3) < EPSILON)
    printf("Approximately equal\n");

float 通常占 4 字节(32 位),有效精度约 7 位十进制数字;double 占 8 字节(64 位),有效精度约 15 位。科学计算和对精度敏感的场景应优先使用 double。

字符的编码

字符在计算机中也是数字。ASCII 编码用 7 位表示 128 个字符:数字 0–9 对应 48–57,大写字母 A–Z 对应 65–90,小写字母 a–z 对应 97–122。

char c = 'A';
printf("%d\n", c);          /* 输出 65 */
printf("%c\n", c + 32);      /* 输出 'a':大写转小写 */

/* 判断字符类型 */
if (c >= '0' && c <= '9')
    printf("Digit\n");
if (c >= 'A' && c <= 'Z')
    printf("Uppercase\n");

C99 引入了宽字符(wchar_t)和多字节字符支持,用于处理 ASCII 之外的字符集(如中文、日文)。

上一页
冯·诺依曼体系结构
下一页
编程语言的层次