单片机原理与应用 C/C++在现代数字计算机上的实现.

Slides:



Advertisements
Similar presentations
主讲:王幸民 理学院计算机基础教学部.
Advertisements

C++语言程序设计教程 第5章 构造数据类型 第6章 C++程序的结构.
C语言程序设计 主讲教师 :张群燕 电话:
培养目标 1.建立基本的程序设计概念体系,掌握基础程序设计方法。
行程(process).
四資二甲 第三週作業 物件導向程式設計.
Memory Pool ACM Yanqing Peng.
第一章 C语言概述 计算机公共教学部.
编译原理上机实习
高级语言程序设计 C++程序设计教程(下) 2006年春季学期 与一些教材的区别 偏重理论,不去讨论某个系统的具体使用方法,但会涉及实现技术
第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置-(6) 4-2 鏈結串列的基礎-(7)
Linked List Operations
單向鏈結串列 Singly Linked Lists.
高级语言程序设计 主讲人:陈玉华.
第二十九章 DLL / LIB函式庫開發 當我們開發程式到一個階段之後,我們一定會希望各個Component的程式碼可以分開的越清楚越好。而這一章最主要就是要告訴各位讀者,我們常在Windows系統中看到的dll或是lib的檔案該怎麼實作?做出這樣的library我們又該如何運用?為什麼使用dll或是lib有利於我們開發程式?以上這些疑問都將會在這一章中得到解答。
补充内容 结构体 概述 定义结构体类型和定义结构体变量 结构体变量的引用 结构体变量的初始化 指针与结构体 用typedef定义类型的别名.
Scope & Lifetime 前言 Local Scope Global Functions & Objects
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
第六章 运行时存储空间的组织和管理 术语 本章内容 讨论一个活动记录中的数据布局 程序执行过程中,所有活动记录的组织方式 过程的活动
第12章 樹狀搜尋結構 (Search Trees)
Classes: A Deeper Look, Part 1
西安交通大学 计算机教学实验中心 大学C++程序设计教程 西安交通大学 计算机教学实验中心
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
Function.
程序设计期末复习 黎金宁
第三章 C++中的C 面向对象程序设计(C++).
第12章 從C到C++語言 12-1 C++語言的基礎 12-2 C++語言的輸出與輸入 12-3 C++語言的動態記憶體配置
2 C++ 的基本語法和使用環境 親自撰寫和執行程式是學好程式語言的不二法門。本章藉由兩個簡單的程式,介紹C++ 程式的基本結構和開發環境,讓初學者能逐漸建立使用C++ 的信心。
第3章 變數、常數與資料型態 3-1 C語言的識別字 3-2 變數的宣告與初值 3-3 指定敘述 3-4 C語言的資料型態
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
第3章 栈和队列(一).
第1章 概述 本章要点: C语言程序结构和特点 C语言程序的基本符号与关键字 C语言程序的编辑及运行 学习方法建议:
合泰半导体股份有限公司 技术讲座 - Holtek V3 C Compiler介绍 主讲人:王幼端 2017/06/15.
第4章 汇编语言程序格式  汇编程序功能  伪操作  汇编语言程序格式  汇编语言程序的上机过程.
第1讲 C语言基础 要求: (1) C程序的组成 (2) C语言的标识符是如何定义的。 (3) C语言有哪些基本数据类型?各种基本数
Speaker: Liu Yu-Jiun Date: 2009/4/29
第 二 章 数据类型、运算符与表达式.
第10讲 构造函数和析构函数 构造函数 析构函数 This 指针.
第四章 栈和队列 栈 ( Stack ) 队列 ( Queue ) 优先队列 (Priority Queue) 小结.
Chap 5 函数 5.1 计算圆柱体积 5.2 使用函数编写程序 5.3 变量与函数.
7.1 C程序的结构 7.2 作用域和作用域规则 7.3 存储属性和生存期 7.4 变量的初始化
第11章 從C到C++語言 11-1 C++語言的基礎 11-2 C++語言的資料型態與運算子 11-3 C++語言的輸出與輸入
開放電腦計劃 報告人:陳鍾誠 2011 年 8 月 20 日 台灣開源人年會 COSCUP 2011 – 中研院
第三章 数据抽象.
C程序设计.
C++语言程序设计教程 第2章 数据类型与表达式 第2章 数据类型与表达式 制作人:杨进才 沈显君.
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
第7章 程序的结构 四、生存期与存储属性 五、extern关键字与外部连接属性 六、static关键字与内部连接属性.
第一章 C语言概述 目录 什么是语言、程序 C语言的历史与发展 C语言的书写形式与程序结构 运行C语言的步骤与方法
C++程序设计 吉林大学计算机科学与技术(软件)学院.
本节内容 对象拷贝 视频提供:昆山爱达人信息技术有限公司.
C/C++基礎程式設計班 C++: 物件的使用、參考、重載函式 講師:林業峻 CSIE, NTU 3/28, 2015.
第 9 章 建構函式與解構函式.
计算机程序设计 小应用工具设计 1.
C程序设计.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第 3 章 类的基础部分 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置-(6) 4-2 鏈結串列的基礎-(7)
《数据结构与算法设计》第一部分 面向对象的C++程序设计基础.
大数据搜索挖掘实验室 第五章 子程序设计 张华平 副教授 博士 Website: 大数据搜索挖掘实验室
C/C++基礎程式設計班 C語言入門、變數、基本處理與輸入輸出 講師:林業峻 CSIE, NTU 3/7, 2015.
第9章 C++程序设计初步 9.1 C++的特点 9.2 最简单的C++程序 9.3 C++的输入输出 9.4 函数的重载
C++程序语言设计 Chapter 14: Templates.
第六章 复合数据类型 指针的声明与使用 数组的声明与使用 指针与数组的相互引用 字符串及相关库函数 new与delete
本节内容 指针类型 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
第11章 字符和内存处理 一、字符数组、指针和字符串的初始化作用 二、strlen函数确定字符串有效长度 三、strcpy函数拷贝字符串.
C语言基础学习 从外行到入门.
第十二章 C与C C转入C++时不需改变的内容 12.2 C转入C++的一些与类无关的 新特性
Presentation transcript:

单片机原理与应用 C/C++在现代数字计算机上的实现

课程项目 安装并配置51单片机的软件开发环境 Code::Blocks+SDCC 实验报告模板有实验所需文件及详细实验步骤。从课程主页下载并逐步填写并提交。 严禁抄袭 提交截止:2015-10-20 23:59

C/C++的实现 C/C++在现代数字计算机上的实现 各种语法映射到实际硬件实现 高效地实现语言的各项特性 提供一定的资源管理能力 提供方便开发的工具、库

平坦地址空间模型 所有数据、代码共享一个地址空间 便于操作系统管理和分配内存 程序需要存储哪些信息? 如何实现这些功能和信息? 函数 数据 各个函数的指令 数据 全局变量,静态变量,局部(自动)变量 动态分配的存储空间,函数调用信息 如何实现这些功能和信息? 必须要清楚各自的功能和特点

局部(自动)变量 仅在定义变量的函数被调用时存在 当函数被递归调用时,可有多个不同变量 void main() { pow(0.5,3); } 局部(自动)变量 仅在定义变量的函数被调用时存在 当函数被递归调用时,可有多个不同变量 float pow(float x, int n) { if(n==1) return x; if(n==0) return 1; int t = n/2; return pow(x,t)*pow(x,n-t); } main() x=?? n=?? t=?? pow(0.5,3) x=0.5 n=3 t=1 x=0.5 n=1 t=?? x=0.5 n=2 t=1 pow(0.5,1) pow(0.5,2) pow(0.5,1)

同步地把Root换成this(多线程安全) volatile: 如一个变量可能被多个线程修改,应定义为volatile this: C++成员函数中指向当前对象的指针 全局变量 程序的整个生存周期都存在 常量 全局资源的管理数据 const char *FlagToName[]= { “create”, “open”, “append” }; 同步地把Root换成this(多线程安全)

静态变量 程序的整个生存周期内存在 当函数被递归调用时,也仅有一个变量 大多数静态变量可以用类变量代替 void main() { pow(0.5,3); } 静态变量 程序的整个生存周期内存在 当函数被递归调用时,也仅有一个变量 float pow(float x, int n) { static int entryCount=0; entryCount ++; …… main() entryCount=0 pow(0.5,3) entryCount=1 entryCount=2 entryCount=3 pow(0.5,1) pow(0.5,2) 大多数静态变量可以用类变量代替 pow(0.5,1)

程序的其它信息 动态分配的存储空间 函数调用信息(返回地址等) 函数 生存周期由程序员控制 与局部变量相同 程序的整个生存周期内存在 可能跨越函数,也可能仅跨越几条语句 函数调用信息(返回地址等) 与局部变量相同 函数 程序的整个生存周期内存在 与各种数据都很不相同 与常量有一定相似性

程序的各种信息总结 程序的整个生存周期内存在 仅在定义变量的函数被调用时存在 由程序员控制 各部分应分别管理,放置在地址空间的不同部位 函数,全局变量,静态变量 仅在定义变量的函数被调用时存在 局部变量,函数调用信息 由程序员控制 动态分配的存储空间 各部分应分别管理,放置在地址空间的不同部位

程序在内存中的映象 全局和静态数据的实现没有区别 防止空指针错误 00000000 未映射空间 文件头 函数 函数1 数据 开始执行位置 __startup 堆 函数在内存的顺序并不重要 call _main ~ main() call _exit 栈 函数n 操作系统的 代码、数据 FFFFFFFF

栈 管理函数调用和返回,分配局部变量 为什么用栈? 栈指针 栈框架 SP 函数可以递归调用 指向栈顶部的指针 未分配空间 SP printf() SF 管理函数调用和返回,分配局部变量 为什么用栈? 函数可以递归调用 栈指针 指向栈顶部的指针 栈框架 一个函数的调用信息、局部变量等所有的信息 Go() SF Test() SF main() SF 已分配空间 不能返回指向局部变量的指针或引用!

栈 SP SP ptr y (3) printf SP main main SA int * ptr () { int y=3; return &y; }; main () { int *SA,content; SA = ptr(); content = *SA; printf("%d", content); /*??*/ content = *SA; printf("%d", content); /*??*/ }; SP SP ptr y (3) printf SP main SA(??) main SA main SA See 3 at stackAddress, its equal to 3

栈 空间比较小:32位平台约1MB左右 如果函数调用太多,或者自动变量太多? Stack Overflow: 栈溢出

堆 堆:程序员主动管理的内存 返回值:地址(?) 如何使用? 叫啥名? size new char[16*1024*1024];

指针(pointer) 存储地址的变量 char *photo = new char[16*1024*1024]; 堆 00230002 00230003 00230004 栈 ffff0008 ffff000c ffff0010 ffff0014 photo 0023004

指针 指针存储的是地址 指针和整数的区别:没有! 一种情况是从堆分配的内存 合法的指针:(char*)12345 还有别的情况 指针和整数的区别:没有! 合法的指针:(char*)12345 指针的大小通常和整数的大小相同 32位平台,通常是32位,或4字节 与指针所指向的内容的大小无关

指针 指针是变量 指针的值可以被改变 变的是什么? char *photo=new char[16*1024*1024]; …… 0011004 也没变! 变了! 0023004 没变!

指针 访问指针指向的内容 指针可以如数组名一样使用 堆中的内存块就是数组 char *photo=new char[size]; for(int i=0;i<size;i++) photo[i] = 0; photo[eyePos] = …… faceColor = photo[facePos];

指针 易犯的错误 编译没有问题 typedef struct {…}TMyStruct; TMyStruct *sptr = (TMyStruct *)malloc(n*sizeof(TMyStruct *)); C++ C 这个*是不能有的 struct TMyStruct{…}; TMyStruct *sptr = new TMyStruct *; 编译报错

指针 易犯的错误 假设变量大小 尽量用sizeof计算变量的大小 int *dynArray = (int*)malloc(mySize*4); 尽量用sizeof计算变量的大小 特别是结构的大小 结构的大小在不同的编译选项下面可能有差异

指针 指针的作用域 指针指向的内容呢? 如其它的变量 void ShowPhoto(const char *FileName) { …… char *photo=new char[fileSize]; } 指针指向的内容呢?

指针 指针指向内容的作用域 由程序员控制 不再使用的时候,要释放 void ShowPhoto(const char *FileName) { …… char *photo=new char[fileSize]; delete []photo; }

指针 如果忘了释放? 0023004 还在! 内存泄漏(memory leak)!

指针 如果尚未分配内存就访问其指向的内容? 非法指针访问! char *photo; photo[facePos] = clPink;  别的变量  00132357 非法指针访问!

指针 如果访问的单元超出了分配的范围? 指针越界! char *name=new char[5]; name[5] = 0; Name[-1] = ‘L’; 00230002 00230003 00230004 00230005 00230006 00230007 00230008 00230009  00230004 指针越界!

指针 如何防止这些错误 小心? 开发工具? 良好的书写风格 人总是有犯错误的时候! Borland C++ Builder 6以上版本可查这些错误 运行缓慢,需要很长时间调试 良好的书写风格 可以避免一部分错误

指针 良好的书写风格 总是把指针初始化成空 NULL 可以通过调试检测非法指针访问错误 空:通常定义为(void*)0 地址为0的内存单元一般被定义为不能被访问,如果有对这个单元的访问,则一定是错误 一般操作系统把最前和最后64KB地址都设为不能访问 可以通过调试检测非法指针访问错误 如何做? char *photo = NULL;

指针 空指针错误

指针 空指针错误 实验代码:NULL.7z(从课程主页下载)

指针 用C++类防止内存泄漏 构造函数:在定义变量的一开始被自动调用 析构函数:在变量被释放前被自动调用 均由编译器在适当的位置自动插入指令完成 不需要程序员管理 可以“忘掉”这些事情! 可以方便地管理内存分配与释放

指针 class TCharArray { public: char *img; int len; TCharArray() 要管理的内存 img = NULL; len = 0; } …… 要管理的内存 分配的内存大小 构造函数 初始化为空状态

指针 ~TCharArray() { 析构函数 Close(); } 调用内存释放函数(很重要!) void Close() ……//接上页 ~TCharArray() { Close(); } void Close() if(img!=NULL) delete []img; img = NULL; len = 0; …… 析构函数 调用内存释放函数(很重要!) 内存释放函数 测试是否已经分配 如是,则释放,并恢复成空状态

指针 void Alloc(int size) { Close(); img = new char[size]; len = size; ……//接上页 void Alloc(int size) { Close(); img = new char[size]; len = size; } …… }; 内存分配函数 调用内存释放函数(很重要!) 分配内存,设置成合法状态 实现其它功能

指针 如何使用? 定义变量,分配内存 如何访问其指向的内容? TCharArray photo; faceColor = photo.img[facePos]; 书写很麻烦 希望:… = photo[facePos]; TCharArray photo; photo.Alloc(fileSize);

指针 操作符重载 重新定义操作符的含义 class TCharArray { …… char operator[](int inx) return img[inx]; } }; 前面的内容 重载下标操作符[]

指针 指针越界? … = photo[-1]; … = photo[fileSize]; class TCharArray { …… char operator[](int inx) return img[inx]; } }; if ( (inx<0) || (inx>=len) ) { ……//出错处理 }

指针 什么样的出错处理比较合适? 不知道!(程序逻辑上已经不可恢复) 用assert #include <_assert.h> class TCharArray { …… char operator[](int inx) assert( (i>=0) && (i<len) ); return img[inx]; } }; 两处都需要!

指针 如何对其内容赋值? photo[facePos] = clPink; Compiler Error: LValue Required! photo.img[facePos] = clPink; 麻烦!

指针 解决办法:返回引用 唯一的变化 char &operator[](int inx) { …… char &operator[](int inx) { assert( (i>=0) && (i<len) ); return img[inx]; } }; TCharArray photo; photo.Alloc(fileSize); …… photo[facePos] = clPink; 

指针 返回引用和返回普通变量的区别 char &operator[](int inx) { assert( (i>=0) && (i<len) ); return img[inx]; } char operator[](int inx) { assert( (i>=0) && (i<len) ); return img[inx]; } 再返回 直接返回该变量的访问方法 复制一份 char tmp=img[inx]

指针 多种数据类型,为每个类型写一个类? class TFloatArray class TIntArray class TDoubleArray class TLongArray class TShortArray class TCharArray

模板 模板:管你是什么类型 TArray<int> IntArray; template<class T> class TArray { public: T *Data; …… void Alloc(int n) { Close(); Data = new T[n];} T &operator[](int n) { return Data[n];} TArray<int> IntArray; TArray<float> FloatArray; TArray<TMyStruct> SA; TArray<TArray<int> > AA;

资源泄漏 用类机制防止其它资源泄漏 与上面过程类似 class TMyFile { public: FILE *MyFile; ……

函数 高级语言写的函数必须翻译成机器语言 需要翻译的内容 可执行文件中函数即是机器语言指令串 参数传递 函数调用 局部变量 函数内在功能(算法) 返回

函数 调用函数需要进行的操作 SP SP SP SP ret_add var1 fmt count …… 第一步:参数传递(先不管顺序) void main() { int count = 10; printf(“%d times hello”,count); …… SP var1 SP fmt SP count …… 第一步:参数传递(先不管顺序) 第二步:记录返回值,转到函数入口

(ret_add到fmt之间的长度不定) 函数 调用函数需要进行的操作 SP i SP ret_add int printf(const char *fmt,...) { for(int i=0;fmt[i]!=0;i++) if(fmt[i]==‘%’) …… var1 fmt 从右到左传递参数 var1 fmt count …… 第三步:分配局部变量 函数如何知道fmt在哪里? (ret_add到fmt之间的长度不定)

函数 函数的参数传递 __cdecl, __stdcall: 从右到左顺序压栈 __pascal:从左到右顺序压栈 还有其它差别 __pascal:从左到右顺序压栈 __fastcall: 利用寄存器传递参数 速度稍快

函数 函数返回 SP SP SP i ret_add 第一步:清除局部变量 fmt 第二步:返回 var1 count 谁清除调用参数? …… __cdecl, __pascal : 调用函数 __stdcall: 被调用函数 __fastcall: 基本不用清除

函数 函数的参数传递与清除 __cdecl: 从右到左,调用函数清除 __stdcall: 从右到左,被调用函数清除 可以方便地支持可变参数个数 在参数表末尾添加不必要的参数不会导致问题 __stdcall: 从右到左,被调用函数清除 __pascal: 从左到右,调用函数清除 __fastcall: 利用寄存器传递参数

函数 函数重载的实现 函数名转换 int OpenFile(const char *FileName) { …… int OpenFile(const wchar_t *FileName) @@OpenFile$qpxc proc near …… @@OpenFile$qpxb proc near

函数 函数名转换 调用方式的区别 大小写 名修饰(name mangling, name decoration) __cdecl, __stdcall, __fastcall:区分 __pascal:不区分 名修饰(name mangling, name decoration) C:不修饰(但可选择头上加下划线) extern “C” {…} C++:修饰(可以重载) extern “C++” {…}

函数 函数名转换 不同编译器的名修饰是不同的! 不同编译器编译的C++库不能通用 C++DLL不能被不同编译器编译的执行文件调用 编写DLL应使用extern “C” {…}以避免名修饰 名字前面的下划线? Ansi C/C++:缺省加 VC:缺省不加(且加一个尾巴!) 如果DLL链接出错,看看是否相应选项正确

函数 函数的地址和函数指针 CPU不知道名字 编译器必须把所有名字翻译成地址 ...... E8 ???? ...... void main() { int count = 10; printf(“%d times hello”,count); …… ...... …… call ???? E8 ???? ......

函数 函数的地址和函数指针 编译器不知道别的模块的名字对应的地址 把需要地址的地方记录下来 ...... E8 00000000 0x1234abcd ...... 0x1234abcd: _printf 引入符号表

函数 函数的地址和函数指针 编译器知道本模块的名字的地址 要把这些名字的地址告诉后续工具 main() Usage() push ebp ...... ret push ebp ... 0x00000000 ...... 0x00001234 ...... 0x00000000: _main 0x00001234: _Usage ...... 引出符号表

函数 函数的地址和函数指针 编译器的功能 但是 如何让程序执行起来? .c/.cpp ----> .obj 高级语言函数“翻译”成机器语言函数 但是 .obj不能执行 尚未解决的地址 如何让程序执行起来?

函数 函数的地址和函数指针 连接器 一个程序要调用多个函数 编译器生成多个独立的函数 连接器把函数合并到一起成为程序

函数 函数的地址和函数指针 连接器 0x00010000 ? __startup ? __startup 0x00001234: _main 0x00005678: _exit

函数 函数的地址和函数指针 连接器 0x00010000  __startup  0x0001abcd _main ? 0x0001ef01 _exit _main 0x0000abcd: _printf _exit

函数 函数的地址和函数指针 连接器 0x00010000  __startup  0x0001abcd _main  _printf …… 0x0001ef01 _exit _printf ? ……

函数 ?库文件一定叫.lib? 函数的地址和函数指针 经常使用的函数 库:收集经常使用的函数到一起 每次都重新编译? 放到哪里? _printf _scanf ...... Dictionary _printf: ...... _scanf: ...... ...... ?库文件一定叫.lib?

函数 函数的地址和函数指针 生成程序的全过程 中间临时输出路径 .c .cpp .pas .f .asm 编译器 汇编器 .h .hpp .inc ...... .obj .lib tlink.exe .exe tlib.exe 输出路径 库文件路径 源文件路径 头文件路径

函数 函数的地址和函数指针 函数地址 函数的执行入口在内存中的地址 函数指针 理论上:执行文件被加载到内存后才能确定函数地址 实际上:系统均有很强的约定,大多数函数地址可以在连接时确定 函数指针 指向函数的入口的指针 有什么用?

函数 函数指针 作用:调用所指向的函数 修改函数的代码? 获得函数的代码? 一般情况下不作这些用处 理论上可行 为什么用指针? 不用名?

函数 函数指针 当编译的时候无法确定函数名,则不能用名调用函数,只能用指针 例1:排序算法,编译时不能确定需要排序的数组的具体类型,就无法确定比较函数 例2:虚函数,编译时无法确定指向基类的指针的实际类型,就无法确定该用哪个函数 虚函数在编译器中是用函数指针实现的

函数 函数指针 定义和使用函数指针类型的语法 typedef int (*TCmp)(const void *,const void *); 可以不加名字 差异所在 基本上是函数的声明 函数指针可以如一般函数名一样使用 函数指针类型可以如一般变量类型使用 void qsort(void *base, size_t nelem, size_t width, TCmp cmp) {…… int cmpResult = cmp( (void*)(((int)base)+width*…);