闻道有先后,术业有专攻。
C语言由B语言演化而来,由贝尔实验室提出,诞生1972年,目前为C11。
编程语言简述:
机器语言
由0和1组成
机器语言:1000100111011000
汇编语言(Assembly Language)
用符号代替了0和1,比机器语言便于阅读和记忆
汇编语言:mov ax, bx
高级语言
C\C++\Java\Swift等,更接近人类自然语言
高级语言:ax = bx;
汇编语言的特点
可以直接访问、控制各种硬件设备,比如存储器、CPU等,能最大限度地发挥硬件的功能
汇编指令是机器指令的助记符,同机器指令一一对应。每一种CPU都有自己的机器指令集\汇编指令集,所以汇编语言不具备可移植性
知识点过多,开发者需要对CPU等硬件结构有所了解,不易于编写、调试、维护
不区分大小写,比如mov和MOV是一样的
汇编语言的种类
8086汇编(8086处理器是16bit的CPU)
Win32汇编
Win64汇编
AT&T汇编(Mac、iOS模拟器)
ARM汇编(嵌入式、iOS设备)
简单介绍下CPU
每一个CPU都有很多脚,这些脚里面是导线,每一个脚都与总线相连,CPU通过这些脚与外部进行交互;
CPU 中央处理器 central processing unit 是一个由数百万个晶体管组成的集成电路CI
从功能划分 主要由 寄存器 控制器 运算器 时钟构成
时钟负责CPU执行指令的执行信号
寄存器负责储存数据和指令
控制器负责把数据或指令从内存拷贝到寄存器
运算器负责预算在寄存器中的数据
寄存器大概有20~100个
按照功能分类:
1.程序计数器
2.标记寄存器
3.基址寄存器
4.变址寄存器
5.通用寄存器
6.指令寄存器
7.栈寄存器
8.累加寄存器
总线分为3类:
地址总线;数据总线;控制总线
地址总线
它的宽度决定了CPU的寻址能力
8086的地址总线宽度是20,所以寻址能力是1M( 2_20 )
数据总线
它的宽度决定了CPU的单次数据传送量,也就是数据传送速度
8086的数据总线宽度是16,所以单次最大传递2个字节的数据
控制总线
它的宽度决定了CPU对其他器件的控制能力、能有多少种控制
总线的宽度是指,可以控制的导线个数;如:
上图总线个数为 3.
寻址方式还有一个段地址和偏移地址的概念,此处不再深究。
1kb = 1024b
1b = 8位 这里8位的概念就是物理中的8根导线所记录的高电位或者低电位的任何组合方式之一
寄存器
谈起CPU就要讲一讲寄存器,下面是我的一些见解,可能并不充分,如需了解更多请参考汇编语言,计算机硬件,单片机等内容。
针对CS、IP进行简述
CS 代码寄存器,作用就是读取代码的.
IP指令寄存器,作用有点类似偏移地址的意思
CPU只要通电就会运转,里边有loop机制,熟悉opengl的人都知道,打开一个窗口是在执行while循环,否则,就会打开之后窗口一闪而过;于我而言,CPU应该也是属于这种类似的状态
指令的执行过程相对复杂,这里用一幅图来表示。
有兴趣可以参考这篇文章:https://www.jianshu.com/p/195440f7234c
针对DS进行简述
ax 或者 AX 不区分大小写,又可分为 al 和 ah,大小分别为8位
mov ax 0123h
mov bx 1122h
add al bh
此时的ax 为 0134, al对应 23 ;bh对应11
关于寄存器的内容,先到此为止,堆栈寄存器会在下一篇文章介绍。
高级语言与汇编语言的关系
你应该基础到过汇编语言,比如断点调试,崩溃日志对于mov ax 0123h 你可能不熟悉,但应该不陌生。
代码也是数据,对于内存来将没有什么特别的地方,只是CS:IP的处理使得他是作为代码去处理,而不是普通数据。所以应该有以下对应关系。
代码--》1000100111011000
这样的代码成为汇编语言,也就是mov ax, bx,然而这样写太累,于是乎有了高级语言;
ax = bx,所以有了如下对应关系
ax = bx -》mov ax, bx -》1000100111011000
不同的高级语言可能生成的汇编语言是一样的,同样的高级语言,不一样的数据可能生成的汇编语言是一样的。因此各种语言才可以大展身手,也因此反向汇编是一个难点,也就是说我们无法通过汇编语言得到确切的高级语言。
C语言:Hello world!#include
int main(void)
{
printf("Hello, world!\n");
return 0;
}
#include 编译预处理命令
C源程序->编译预处理->编译->优化程序->汇编程序->链接程序->可执行文件
常用:
指令 用途
# 空指令,无任何效果
#include 包含一个源代码文件
#define 定义宏
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
#elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码,其实就是else if的简写
#endif 结束一个#if……#else条件编译块
#error 停止编译并显示错误信息
数据寄存器、指针寄存器、变址寄存器 统称为通用寄存器
H、L分别表示高低位;Height Low
段寄存器
CS: 与 IP 组合使用,代码段
mov 不能用于设置 CS:IP的值 需要用到jmp
jmp 指令 用于代码段跳转执行,设置IP值:jmp 2AD0:0003
DS: 读写内存单元,8086不支持将数据直接放到段寄存器(数据段),所以需要一个通用寄存器作为中转站;
mov bx,1000h
mov ds,bx ;写
mov al [0] ;读
在汇编语言中 注释符号是“;”
同理:mov bx,1000h
mov ds,bx ;写
mov [0],al ;写
大小端的概念
寻址能力:
首先有个寻址的概念,不同位数的CPU寻址的大小不一样,比如16\32\64位的分别是:2^16,2^32,2^64;
2进制转16进制的方法:https://www.cnblogs.com/gaizai/p/4233780.html
由于8086 是段地址➕偏移地址,8086的寻址大小为:2^20, 也就是0x00000 ~ 0xFFFFF
大端模式:
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中(高低\低高) (Big Endian)
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中(高高\低低) (Little Endian)
例如存放一个0x1122数据
11 是高字节 22是低字节
小端模式:
0x0001 0x22
0x0002 0x11
大端模式:
0x0001 0x11
0x0002 0x22
引自百度百科:
目前Intel的80x86系列芯片是唯一还在坚持使用小端的芯片,
ARM芯片默认采用小端,但可以切换为大端;
而MIPS等芯片要么采用全部大端的方式储存,要么提供选项支持大端——可以在大小端之间切换。
另外,对于大小端的处理也和编译器的实现有关,
在C语言中,默认是小端(但在一些对于单片机的实现中却是基于大端,比如Keil 51C),
Java是平台无关的,默认是大端。
在网络上传输数据普遍采用的都是大端。
SS:
ss作为栈段地址 与SP组合使用,ss:sp任意时刻指向栈顶元素
压栈push 出栈pop 来操作栈顶数据
比如push ax是将ax的数据入栈,pop ax是将栈顶的数据送入ax
这里两张图表示这一过程:
push:
pop:
常见操作:
ES:
作为附加段,会保存一些基本信息