6. 基本句法

1.基本结构

  1. 顺序语句
  2. 分支语句
  3. 循环语句

1. if, switch 语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
using namespace std;

bool isLeapYear(unsigned int year){
if ((year % 4 == 0 && year % 100 !=0) || (year % 400 == 0)){
return true;
} else {
return false;
}
}

typedef enum _COLOR{
RED,
GREEN,
BLUE,
UNKNOWN
}color;

int main(){

cout << isLeapYear(2000) << endl;
cout << isLeapYear(2001) << endl;
cout << isLeapYear(2020) << endl;

color color0;
color0 = BLUE;
if (color0==RED){cout << "red" << endl;}
else if (color0==GREEN){cout << "green" << endl;}
else if (color0==BLUE){cout << "blue" << endl;}
else {cout << "unknown" << endl;}

color color1;
color1 = GREEN;
switch (color1) {
case RED: // case 后面接常数值
cout << "red" << endl;
break;
case GREEN:
cout << "green" << endl;
break;
case BLUE:
cout << "blue" << endl;
break;
default:
cout << "unknown" << endl;
break;

}
}

if 和switch 比较

  • 使用场景
    1. switch 只支持常量值固定相等的分支判断
    2. if还可用于区间判断
    3. 用switch能做的,用if都能做,反过来则不行
  • 性能比较
    1. 分支较少是,差别不是很多;分支多是,switch性能较高
    2. if开始处几个分支效率很高,之后递减
    3. switch所有case的速度几乎一样

2. 自定义结构——枚举enum

  • 使用#defineconst创建符号常量,使用enum不仅能够创建符号常量,还能定义新的数据类型。
1
2
3
4
5
6
7
8
9
10
int main(){
enum wT{Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
wT weekday;
weekday = Monday;
weekday = Wednesday;
// weekday = 1 非法 不能直接给int值,只能赋值成wT定义好的类型值 不能做左值
cout << weekday << endl;
return 0;

}

使用细节:

  1. 枚举值不可以做左值
  2. 非枚举变量不可以赋值给枚举变量
  3. 枚举变量可以赋值给非枚举变量

3. 自定义结构——结构体与联合体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;
int main(){
union Score{
double ds; // 8
char level; // 1 联合体使用同一个空间
};
struct Student{
char name[6]; // 6 * 1 bytes
int age;// 4 bytes
Score s;// 8 bytes
};
cout << sizeof(Score) << endl; // 8
cout << sizeof(Student) << endl; //24
}

结构体数据对齐问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct s1{
char x;//1byte 不满 4个字节, 在32位cpu中,直接补成4字节
int z;// 4
short y;//2 不满 4个字节, 在32位cpu中,直接补成4字节 共12个字节
};
struct s2{
char x;//1
short y;//2 先一个字节 加 2个字节凑成4字节
int z;//4 共8个字节

};
struct s3{
char x;//1
short y;//2
double z;//8 以最大的size考虑,上面1+2会补成8,共16bytes

};
cout << sizeof(s1) << endl;
cout << sizeof(s2) << endl;
cout << sizeof(s3) << endl;

对于32位CPU:

  • char: 任何地址
  • short:偶数地址
  • int: 4的整数倍地址
  • double: 8的整数倍地址
1
2
3
4
5
6
7
Student stu;

memcpy(stu.name, "lili", 6);
stu.age = 16;
stu.s.ds = 95.5;
stu.s.level = 'A';
cout << stu.name << endl; // lili

4. 循环语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
using namespace std;

int main(){
int sum = 0, index = 1;
while (index <= 100){
sum += index;
index ++;
}
cout << sum << endl;

// for循环
// index = 1;
sum = 0;
for (int index = 1; index <= 100; ++index){
sum += index;
}
cout << sum << endl;

//do-while语句
sum = 0, index = 1;
do{
sum += index;
index += 1;
}while (index <= 100);
cout << sum << endl;

return 0;
}

Q:输出所有形如aabb的四位完全平方数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <math.h>
using namespace std;

int main(){
// 实现 aabb的完全平方数
int num;
double m;
for (int a = 0; a < 10; ++a) {
for (int b = 0; b < 10; ++b) {
num = a * 1100 + b * 11;
m = sqrt(num);
if ((m - int(m)) < 0.000001){
cout << num << endl;
cout << m << endl;
}

}

}
return 0;
}

int main(){
int n = 0;
int high, low;
for (int index = 0;; ++index) {
n = index * index;
if (n < 1000){
continue; // 继续下一次循环,这一次不再往下继续
}
if (n > 9999){
break;//退出循环
}
high = n / 100; // 4567 / 100 = 45
low = n % 100; // 4567 % 100 = 67
if ((high / 10 == high % 10) && (low / 10 == low % 10)){
cout << n << endl;
cout << index << endl;
}
}
return 0;
}
___________________________
7744
88

5. 函数

一个C++程序是由若干个源程序文件构成,而一个源程序是由若干个函数构成,函数将一段逻辑封装起来,便于复用

从用户角度看,可以分为:

  • 库函数: 标准函数, 由c++系统提供
  • 用户自定义函数:需要用户定义后使用

从组成角度看:

  • 返回类型: 一个函数可以返回一个值
  • 函数名称:函数名和参数列表一起构成了函数签名
  • 参数: 参数列表包括函数参数的类型、顺序、数量。参数是可选的,函数可以不包含参数。

1. overload 函数重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int test(int a){
return a;
}
int test(double b){
return int(b);
}

int test(int a, double b){
return a + b;
}

int main(){
int (*p)(int);
p = test;// 函数指针
int result = (*p)(1);
cout << result << endl;
// int result = test(1);
// cout << result << endl;
result = test(2.0);
cout << result << endl;
result = test(1, 2.0);
cout << result << endl;
return 0;
}

2. 指数函数的指针与返回指针的函数

每一个函数都占用一段内存单元,它们有一个起始地址,指向函数入口地址的指针称为函数指针。

一般形式: 数据类型 (*指针变量名)(参数表);

int (*p)(int)

区别:

  • int (*p)(int);是指针,指向一个函数入口地址
  • int* p(int);是函数,返回值是一个指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
}

3. 命名空间

  • 命名空间,可以作为附加信息来区分不同库中名称的函数、类、变量等,命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。
  • 关键字:using 和namespace。 std::name

4. 函数体

函数主体包含一组定义函数执行任务的语句。

5.内联函数 inline function

  • 如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方
  • 引入内联函数的目的是为了解决程序中函数调用的效率问题
  • 注意:内联函数内部不能有太复杂的逻辑,编译器有时会有自己的优化策略,所以内联不一定起作用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>

inline int Fib(int n){
if ( n == 0 ){
return 0;
}
else if ( n == 1 )
{
return 1;
}
else
{
return Fib(n-1) + Fib(n-2);
}
}
// 有可能没用

int main(){
int n = 10;
std::cout << Fib(n) << std::endl;
return 0;
}

6. 数学归纳法

递归的四个基本法则:

  1. 基准情形:无须递归就能解出;
  2. 不断推进:每一次递归调用都必须是求解状况朝接近基准情形的方向推进
  3. 设计法则:假设所有递归调用都能运行
  4. 合成效益法则——compound interest rule:求解一个问题的同一个实例是,切勿在不同的递归调用中做重复性的工作。

递归recursion的缺陷:

  1. 递归是一种重要的编程思想:
    • 很多重要的算法都包含递归的思想
    • 递归最大的缺陷:
      • 空间删需要开辟大量的栈空间1
      • 时间上可能需要有大量重复运算
  2. 递归的优化
    • 尾递归: 所有递归形式的调用都出现在函数的末尾
    • 使用循环替代
    • 使用动态规划:时间换空间

分别使用递归,循环,尾递归、动态规划来实现斐波那契数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>
#include <assert.h>

int g_a[1000];//全局数组,记录fib计算的值

int Fib(int n){
if ( n == 0 ){
return 0;
}
else if ( n == 1 )
{
return 1;
}
else
{
return Fib(n-1) + Fib(n-2);
}
}
int Fibloop(int n){
if (n < 2){
return n;
}
int n0=0, n1=1;
int tmp;
for (int i = 2; i<=n; i++){
tmp = n0;
n0 = n1;
n1 = n0 + tmp;
}
return n1;
}
// 尾递归
int Fiblast(int n, int ret0, int ret1){
if(n==0)
{return 0;}
else if (n == 1)
{
return ret1;
}
return Fiblast(n-1, ret1, ret0 + ret1);

}

// 动态规划
int Fibd(int n){
g_a[0] = 0;
g_a[1] = 1;
for (int i=2; i<=n; i++){
if (g_a[i]==0){
g_a[i] = g_a[i-1] + g_a[i-2];
}
}
return g_a[n];
}

int main(){
int n = 10;
std::cout << Fib(n) << std::endl;
std::cout << Fibloop(n) << std::endl;
std::cout << Fiblast(n, 0, 1) << std::endl;
std::cout << Fibd(n) << std::endl;
assert(Fib(10)==55);
return 0;
}