A man provided with paper,pencil,and rubber and subject to strict discipline,is effect a universal Turing Machine. ——Alan Mathison Turing
1.图灵机与三种基本结构
三种基本的程序结构
- 顺序,分支和循环
2.if语句基础和例子
-
复合语句:
单一语句:在任何一个表达式后面加上分号(😉
复合语句:用一对花括号{}括起来的语句块,在语法上等效于一个单一的语句。 -
if语句:
if语句是最常见的一种分支语句,也称为条件语句。
如:
if(p != NULL)
{
cout<<*p<<endl;
}else{
;
}
3.switch分支基础
- switch的一般形式:
switch(表达式)
{ case 常数1:语句1;break;
case 常数2:语句2;break;
................
case 常数n:语句n;break;
default:语句n+1
}
4.switch和if的对比
- 使用场景:
- switch只支持常量值固定相等的分支判断;
- if还可以判断区间范围;
- 用switch能做的,用if都能做,但反过来则不行
- 性能比较:
- 分支少时,差别不是很大,分支多时,switch性能较高;
- fi开始处几个分支效果高,之后效率递减;
3.switch所有case的速度几乎一样;
5.自定义类型——枚举
- 使用#define和const创建符号常量,使用enum不仅能够创建符号常量,还能定义新的数据类型;
- 枚举类型enum(enumeration)的声明和定义:
例如:
enum WT{
Monday,
Tuesday,
Wedesday,
Thursday,
Friday,
Saturday,
Sunday
};
wT weekday;
weekday = Monday;
//weekday = 1; //错误,不能直接给int值,只能赋给成wT定义好的类型值
weekday = wT(1);//强制类型转化就可以
cout<<weekday<<endl;//1
//Monday = 0; //类型值不能做左值
int a = Wednesday;
cout<<a<<endl; //可以
- 使用细节:
- 枚举值不可以做左值;
- 非枚举变量不可以赋值给枚举变量;
- 枚举变量可以赋值给非枚举变量;
6.自定义类型——结构体与联合体
- 使用struct定义的是结构体:
例如:
struct Student
{
char name[6];
int age;
Score s;
}
- 使用union定义的是联合体:
例如:
union Score
{
double fs;
char level;
}
sizeof(Student); //24
sizeof(Score); //8
7.结构体的内存布局
结构体数据对齐问题
- 结构体的尺寸:
struct s1
{
char x;
int z;
short y;
}; //sizeof(s1) = 12
struct s2
{
char x;
short y;
int z;
}; //sizeof(s1) = 8
结构体的内存布局
32位CPU眼中的内存布局(32位系统中以4个字节为整体来看!)
将int改成double类型,上面是24,下面是16。面试经常考这个!!!!!
结构体数据对齐——缺省对齐原则
- 32位CPU
char:任何地址
short:偶数地址
int:4的整数倍地址
double:8的整数倍地址 - 修改默认编译选项
Visual C++:
#pragma pack(1) //以1位整数倍地址
g++:
__attribute__(aligned(n))
__attribute__(__packed__)
建议:如果没有编译选项,要考虑内存布局,要尽量将小的sizeof放在一起,这样可以凑够大的sizeof的整数倍,不需要两边都进行扩充。当然最好的方法还是修改编译选项。
8.三种循环的基本使用与比较
- while,do…while和for
- 三个循环的编写方式:
9.函数基础
int main()
{
return 0;
}
函数的所有组成部分:
- 返回类型:一个函数可以返回一个值;
- 函数名称:这是函数的实际名称,函数名和参数列表一起构成了函数签名;
- 参数:参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数;
10.函数重载overload与Name Mangling
- 函数重载overload与C++ Name Mangling:
int test(int a);
int test(double a);
int test(int a,double d);
11.指向函数的指针与返回指针的函数
- 每一个函数都占用一段内存单元,它们都有一个起始地址,指向函数入口地址的指针称为函数指针。
一般形式:数据类型(*指针变量名)(参数表);
举例:int(*p)(int)
; - 注意与返回指针的函数之间区别:
如:
int(*p)(int); //是指针,指向一个函数入口地址
int* p(int); //是函数,返回的值是一个指针
再如:
bool processNum(int i,int j,int(*p)(int a,int b));//int(*p)(int a,int b)回调函数
char *strcpy(char *dest,const char *src);
#include <iostream>
using namespace std;
int MaxValue(int x, int y)
{
return (x > y) ? x : y;
}
int MinValue(int x, int y)
{
return (x < y) ? x : y;
}
int Add(int x, int y)
{
return x+y;
}
bool ProcessNum(int x, int y, int(*p)(int a, int b))
{
cout << p(x, y) << endl;
return true;
}
int main()
{
int x = 10, y = 20;
cout << ProcessNum(x, y, MaxValue) << endl;
cout << ProcessNum(x, y, MinValue) << endl;
cout << ProcessNum(x, y, Add) << endl;
return 0;
}
12.命名空间
-
同一个班级,同一个公司,很容易出现同名的人;
在C++程序中也会出现这样的问题。 -
命名空间这个概念,可作为附加信息来区分不同库中相同名称的函数、类、变量等,命名空间即定义了上下文。
本质上,命名空间就是定义了一个范围。 -
关键词:
using
和namespace
的使用;(在之后做项目之中一定会用到)
注:如果有多个命名空间,不能再头部直接引入命名空间,则在每次使用时加入命名空间即可。
13.函数体的Hack过程
函数的所有组成部分:
4.函数主体:函数主体包含一组定义函数执行任务的语句;
int Fib(int n)
{
if(n==0)
{return 0;}
else if(n==1)
{return 1;}
else
{return Fib(n-1}+Fib(n-2);}
}
进程,线程,携程,(#^.^#)
查看汇编代码来分析C++的函数运行栈信息
14.内联函数
如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。
inline int MaxValue(int x,int y)//求最大数
{
return (x>y)
}
C/C+±>高级->调用约定:
_cdecl(/Gd):从右向左传参
C/C+±>高级->内联函数是否扩展:
只适用于__inline(Ob1)
注意:内联是空间换时间,有时候会失效。复杂的循环和递归不适合内联操作。
15.数学归纳法与递归
递归与数学归纳法
-
从数学归纳法说起
数学归纳法是证明当n等于任意一个自然数时某命题成立。
证明步骤分两步:
1.证明当n=1时命题成立;
2.假设n=m时命题成立,那么可以推导出在n=m+1时命题也成立(m代表任意自然数); -
证明世界上所有的人都是秃子:
我们知道:
1)0根头发的人是秃子,有1根头发的人也是秃子;
2)假设有n根头发的人是秃子,那么有n+1根头发的人也是秃子;
所以,所有人都是秃子; -
斐波那契数列:1,1,2,3,5,8,13,21,34,…
F i b ( n ) = { 1 , n = 1 , 2 F i b ( n − 1 ) + F i b ( n − 2 ) , n > 2 Fib(n)=\begin{cases} 1, n=1,2\\ Fib(n-1)+Fib(n-2),n>2\\ \end{cases} Fib(n)={1,n=1,2Fib(n−1)+Fib(n−2),n>2
程序实现:
见标题:函数体的Hack过程 -
斐波那契数列递归处理的问题:
重复运算,时间和空间大量浪费
递归(recursion)
- 递归的四个基本法则:
1)基准情形:无须递归就能解出;
2)不断推进:每一次递归调用都必须使求解状况朝接近基准情形的方向推进;
3)设计法则:假设所有的递归调用都能运行;
4)合成效益法则(compound interest rule):求解一个问题的同一个实例时,切勿在不同的递归调用中做重复性的工作;
由此可见,使用递归来计算诸如菲波那切数列并不是一个好主意;
-
递归是一种重要的编程思想:
1)很多重要的算法都包含递归的思想;
2)递归最大的缺陷:
A.空间上需要开辟大量的栈空间;
B.时间上可能需要有大量重复运算; -
递归的优化:
1)尾递归:所有递归形式的调用都出现在函数的末尾;
2)使用循环替代;
3)使用动态规划,空间换时间;
16.递归的特点和Hack过程
- 循环优化递归
int Fib(int n)
{
if(n <2)
{
return n;
}
int n0 = 0,n1 = 1;
int temp;
for(int i = 2;i <= n;++i)
{
temp = n0;
n0 = n1;
n1 = temp+n1;
}
return n1;
}
- 尾递归优化递归
(尾递归只需要保存一个递归信息,普通的递归需要保存两个函数的递归信息),尾递归迭代写法阔以!
int Fib(int n,int ret0,int ret1)
{
if(n == 0)
{
return ret0;
}else if(n == 1){
return ret1;
}
return Fib(n-1,ret1,ret0+ret1);
}
17.尾递归的优化
在编译器中C/C+±>优化->优化->使大小最小化(/O1)
C/C+±>代码生成->基本运行时检查->默认值
18.递归的动态规划思路
#include<assert.h>
int a[1000]; //全局数组
int Fib(int n)
{
a[0] = 0;
a[1] = 1;
for(int i <=2;i <= n;++i)
{
a[i] = a[i-1]+a[i-2];
}
return a[n];
}
assert(Fib(10)==55);