Giter VIP home page Giter VIP logo

c_learn's Introduction

c-learn

linux c 一站式编程学习

第七章笔记

SICP 指出, 在学习一门编程语言要特别注意一下三个方面:

  1. 这门语言提供了哪些 Primitive, 比如基本类型, 比如基本运算符, 表达式和语句.
  2. 这门语言提供了哪些组合规则, 比如基本类型如何组成复合类型, 比如简单的表达式和语句如何组成复杂的表达式和语句.
  3. 这门语言提供了哪些抽象机制, 包括数据抽象和过程抽象 (Procedure Abstraction).

gdb 基本命令 1

命令 描述
backtrack (或者 bt) 查看各级函数调用及参数
finish 连续运行到当前函数返回为止, 然后停下来等待命令
frame (或 f) 帧编号 选择栈帧
info (或 i) locals 查看栈局部变量的值
list (或 l) 列出源代码, 接着上次的位置往下列, 每次列 10 行
list 行号 列出从第几行开始的源代码
list 函数名 列出某个函数的源代码
next (或 n) 执行下一行语句
print (或 p) 打印表达式, 通过表达式可以修改变量的值或者调用函数
quit (或 q) 推出 gdb 调试环境
set var 修改变量的值
start 开始执行程序, 停在 main 函数的第一行前面等待命令
step (或 s) 执行下一行语句, 如果有函数调用则进入到函数中

gdb 基本命令 2

命令 描述
break (或 b) 行号 在某一行设置断点
break 函数名 在某个函数开头设置断点
break ... if ... 设置条件断点
continue (或 c) 从当前位置开始连续运行程序
delete breakpoints 断点号 删除断点
display 变量名 跟踪查看某个变量, 每次停下来都显示它的值
disable breakpoints 断点号 禁用断点
enable 断点号 启用断点
info (或 i) breakpoints 查看当前设置了哪些断点
run (或 r) 从头开始连续运行程序
undisplay 跟踪显示号 取消跟踪显示

gdb 基本命令 3

命令 描述
watch 设置观察点
info (或 i) watchpoints 查看当前设置了哪些观察点
x part1 从某个位置开始打印储存单元的内容,
x part2 全部当作字节来看, 而不区分哪个字节属于哪个变量

整数的加减运算

2's complement 表示法

2's Complement 表示法规定:正数不变,负数先取反码再加 1. 如果 8 个 bit 采用 2's Complement 表示法,负数的取值范围是从 10000000 到 11111111(-128~-1) ,正数是从 00000000 到 01111111(0~127),也可以根据最高位判断一个数是正是负, 并且 0 的表示是唯一的,目前绝大多数计算机都采用这种表示法.

判断溢出

在相加过程中最高位产生的进位和次高位产生的进位如果相同则没有溢出, 如果不同则表示有溢出.逻辑电路的实现可以把这两个进位连接到一个异或门, 把异或门的输出连接到溢出标志位.链接

浮点数

模型由三部分组成:符号位,指数部分(表示 2 的多少次方)和尾数部分(小数点前面是 0,尾数部分只表示小数点后的数字)

指数部分的正负号

使用偏移的指数(Biased Exponent).规定一个偏移值,比如 16,实际的指数要加上这个偏移值再填写到指数部分, 这样比 16 大的就表示正指数,比 16 小的就表示负指数

正规化(Normalize)

规定尾数部分的最高位必须是 1,也就是说尾数必须以 0.1 开头,对指数做相应的调整,这称为正规化(Normalize). 由于尾数部分的最高位必须是 1,这个 1 就不必保存了,可以节省出一位来用于提高精度,我们说最高位的 1 是隐含的(Implied)

计算机体系结构基础

CPU

寄存器 Register

是 CPU 内部的高速存储器,像内存一样可以存取数据,但比访问内存快得多. x86 的寄存器 eax,esp,eip 等等,有些寄存器只能用于某种特定的用途, 比如 eip 用作程序计数器,这称为特殊寄存器(Special-purpose Register), 而另外一些寄存器可以用在各种运算和读写内存的指令中,比如 eax 寄存器, 这称为通用寄存器(General-purpose Register).

程序计数器 PC,Program Counter

是一种特殊寄存器,保存着 CPU 取下一条指令的地址, CPU 按程序计数器保存的地址去内存中取指令然后解释执行, 这时程序计数器保存的地址会自动加上该指令的长度,指向内存中的下一条指令。

指令译码器 Instruction Decoder

CPU 取上来的指令由若干个字节组成,这些字节中有些位表示内存地址,有些位表示寄存器编号, 有些位表示这种指令做什么操作,是加减乘除还是读写内存,指令译码器负责解释这条指令的含义, 然后调动相应的执行单元去执行它.

算术逻辑单元 ALU,Arithmetic and Logic Unit

如果译码器将一条指令解释为运算指令,就调动算术逻辑单元去做运算, 比如加减乘除,位运算,逻辑运算.指令中会指示运算结果保存到哪里, 可能保存到寄存器中,也可能保存到内存中.

地址和数据总线 Bus

CPU 和内存之间用地址总线,数据总线和控制线连接起来,每条线上有 1 和 0 两种状态.

x86 汇编程序基础

最简单的汇编程序

代码

 .section .data

 .section .text
 .global _start
_start:
 mov $1, %eax
 movl $4, %ebx

 int $0x80

编译成可执行文件

  1. 把这个程序保存成文件 hello.s(汇编程序通常以.s 作为文件名后缀),用汇编器(Assembler) as 把汇编程序中的助记符翻译成机器指令,生成目标文件 hello.o: as hello.s -o hello.o
  2. 然后用链接器(Linker,或 Link Editor) ld 把目标文件 hello.o 链接成可执行文件 hello: ld hello.o -o hello
  3. 为什么用汇编器翻译成机器指令了还不行,还要有一个链接的步骤呢? 链接主要有两个作用,一是修改目标文件中的信息,对地址做重定位, 二是把多个目标文件合并成一个可执行文件,这个例子虽然只有一个目标文件, 但也需要经过链接才能成为可执行文件.

执行

现在执行这个程序,它只做了一件事就是退出,退出状态是 4, 在 Shell 中可以用特殊变量 $?得到上一条命令的退出状态. 所以这段汇编代码相当于在 C 程序的 main 函数中 return 4.

分析

  • .section .data 汇编程序中以.开头的名称并不是指令的助记符,不会被翻译成机器指令, 而是给汇编器一些特殊指示,称为汇编指示(Assembler Directive)或伪操作(Pseudo-operation), 由于它不是真正的指令所以加个"伪"字..section指示把代码划分成若干个段(Section), 程序被操作系统加载执行时,每个段被加载到不同的地址, 操作系统对不同的页面设置不同的读,写,执行权限..data段保存程序的数据,是可读可写的, 相当于 C 程序的全局变量.本程序中没有定义数据,所以.data段是空的.

  • .section .text .text段保存代码,是只读和可执行的,后面那些指令都属于.text 段

  • .global _start _start是一个符号(Symbol), 符号在汇编程序中代表一个地址, 可以用在指令中, 汇编程序经过汇编器的处理之后,所有的符号都被替换成它所代表的地址值. 在 C 语言中我们通过变量名访问一个变量,其实就是读写某个地址的内存单元, 我们通过函数名调用一个函数,其实就是跳转到该函数第一条指令所在的地址, 所以变量名和函数名都是符号,本质上是代表内存地址的. .globl指示告诉汇编器,_start这个符号要被链接器用到, 所以要在目标文件的符号表中标记它是一个全局符号. _start就像 C 程序的 main 函数一样特殊,是整个程序的入口, 链接器在链接时会查找目标文件中的_start符号代表的地址,把它设置为整个程序的入口地址, 所以每个汇编程序都要提供一个_start符号并且用.globl声明. 如果一个符号没有用.globl声明,就表示这个符号不会被链接器用到.

  • _start 这里定义了 _start 符号,汇编器在翻译汇编程序时会计算每个数据对象和每条指令的地址, 当看到这样一个符号定义时,就把它后面一条指令的地址作为这个符号所代表的地址. 而 _start 这个符号又比较特殊,它所代表的地址是整个程序的入口地址,所以下一条指令 movl $1, %eax 就成了程序中第一条被执行的指令。

  • movl $1, %eax 这是一条数据传送指令, 这条指令要求 CPU 内部产生一个数字 1 并保存到 eax 寄存器中. mov 的后缀 l 表示 long, 说明是 32 位的传送指令. 这条指令不要求 CPU 读内存, 1 这个数是在 CPU 内部产生的, 称为立即数 (Immediate). 在汇编程序中, 立即数前面要加$,寄存器名前面要加%, 以便跟符号名区分开. 以后我们会看到 mov 指令还有另外几种形式, 但数据传送方向都是一样的, 第一个操作数总是源操作数, 第二个操作数总是目标操作数.

  • movl $4, %ebx 和上一条指令类似, 生成一个立即数 4 并保存到 ebx 寄存器中.

  • int $0x80 前两条指令都是为这条指令做准备的, 执行这条指令时发生以下动作:

  1. int 指令称为软中断指令, 可以用这条指令故意产生一个异常, 上一章讲过, 异常的处理和中断类似, CPU 从用户模式切换到特权模式, 然后跳转到内核代码中执行异常处理程序.

  2. int 指令中的立即数 0x80 是一个参数, 在异常处理程序中要根据这个参数决定如何处理, 在 Linux 内核中 int $0x80这种异常称为系统调用 (System Call). 内核提供了很多系统服务供用户程序使用, 但这些系统服务不能像库函数 (比如 printf) 那样调用, 因为在执行用户程序时 CPU 处于用户模式,不能直接调用内核函数,所以需要通过系统调用切换 CPU 模式, 经由异常处理程序进入内核, 用户程序只能通过寄存器传几个参数, 之后就要按内核设计好的代码路线走, 而不能由用户程序随心所欲, 想调哪个内核函数就调哪个内核函数, 这样可以保证系统服务被安全地调用. 在调用结束之后, CPU 再切换回用户模式, 继续执行 int $0x80 的下一条指令, 在用户程序看来就像函数调用和返回一样.

  3. eax 和 ebx 的值是传递给系统调用的两个参数. eax 的值是系统调用号, Linux 的各种系统调用都是由 int $0x80 指令引发的, 内核需要通过 eax 判断用户要调哪个系统调用, _exit 的系统调用号是 1. ebx 的值是传给 _exit 的参数, 表示退出状态. 大多数系统调用完成之后会返回用户空间继续执行后面的指令, 而_exit 系统调用比较特殊, 它会终止掉当前进程, 而不是返回用户空间继续执行.

x86 的寄存器

x86 的通用寄存器有 eax,ebx,ecx,edx,edi,esi. 这些寄存器在大多数指令中是可以任意选用的, 比如 movl 指令可以把一个立即数传送到 eax 中,也可传送到 ebx 中. 但也有一些指令规定只能用其中某个寄存器做某种用途, 例如除法指令 idivl 要求被除数在 eax 寄存器中, edx 寄存器必须是 0, 而除数可以在任意寄存器中, 计算结果的商数保存在 eax 寄存器中 (覆盖原来的被除数), 余数保存在 edx 寄存器中. 也就是说, 通用寄存器对于某些特殊指令来说也不是通用的.

x86 的特殊寄存器有 ebp、esp、eip、eflags. eip 是程序计数器, eflags 保存着计算过程中产生的标志位, 其中包括进位标志, 溢出标志,零标志和负数标志, 在 intel 的手册中这几个标志位分别称为 CF、OF、ZF、SF. ebp 和 esp 用于维护函数调用的栈帧.

寄存器 用途
eax, ebx, ecx, edi, esi 通用
eax(被除数), edx(0), eax(商, 覆盖被除数), edx(余数) 除法指令 idivl
ebp、esp、eip、eflags 特殊
eip 程序计数器
eflags 进位标志, 溢出标志,零标志和负数标志
ebp, esp 维护函数调用的栈帧

寻址方式

内存寻址在指令中可以表示成如下的通用格式: ADDRESS_OR_OFFSET(%BASE_OR_OFFSET,%INDEX,MULTIPLIER) 它所表示的地址可以这样计算出来: FINAL ADDRESS = ADDRESS_OR_OFFSET + BASE_OR_OFFSET + MULTIPLIER \* INDEX

Makefile 基础

规则

-n 选项只打印要执行的命令,而不会真的执行命令,这个选项有助于我们检查 Makefile 写得是否正确, 由于 Makefile 不是顺序执行的,用这个选项可以先看看命令的执行顺序,确认无误了再真正执行命令.

-C 选项可以切换到另一个目录执行那个目录下的 Makefile.

make 命令行也可以用=或:=定义变量 make CFLAGS=-g

target ... : prerequisites ...
command1
command2
...

特殊变量

符号 表示
$@ 表示规则中的目标
$< 表示规则中的第一个条件
$? 表示规则中所有比目标新的条件,组成一个列表,以空格分隔
$^ 表示规则中的所有条件,组成一个列表,以空格分隔

c_learn's People

Contributors

shun-sfoo avatar

Stargazers

Nicholas avatar

Watchers

James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.