1、从c到c++
采用c语言的方式来写一个求周长面积的工程,如下所示:
#include<iostream>
using namespace std;
void main(void)
{
double r, girth, area;
const double Pi = 3.1415926;
cout << "input number\n"<< endl;
cin >> r; //输入
girth = 2 * Pi * r;
area = Pi * r * r;
cout << "radius = " << r << endl;
cout << "girth = " << girth << endl;
cout << "area = " << area << endl;
}
结果如下:
下面改用c++的写法,代码如下:
#include<iostream>
using namespace std;
class Circle
{
double radius;
public:
void Set_Radius(double r)
{
radius = r;
}
double Get_Radius()
{
return radius;
}
double Get_Girth()
{
return 2*3.14f*radius;
}
double Get_Area()
{
return 3.14f*radius*radius;
}
};
void main()
{
Circle A,B;
A.Set_Radius(6.23);
cout << "半径=" << A.Get_Radius() << endl;
cout << "周长=" << A.Get_Girth() << endl;
cout << "面积=" << A.Get_Area() << endl;
B.Set_Radius(5.26);
cout << "半径=" << B.Get_Radius() << endl;
cout << "周长=" << B.Get_Girth() << endl;
cout << "面积=" << B.Get_Area() << endl;
}
运行结果如下所示:
可以看到这就是c和c++的区别所在了,c的话还是比较结构化的编程方式,但是c++已经开始跟python有点像了,已经是对一个个对象进行操作了,新建对象,对对象进行赋值,调用对象里面的函数也就是方法来进行运算。
下面是我从菜鸟教程里面截取的c++的类的说明:
2、一些需要注意的地方
但是要注意的一点就是c++的对象里面最好要写一下方法,不然会出问题,下面的例子很好的说明了这个情况,代码如下:
运行结果如下所示,可以看到结果是一个乱码的情况,这当然不是我们希望看到的
那么到底是什么原因造成了这种情况呢,因为我们的对象里面其实只有定义,包括最后那个计算面积的其实也是定义,所以他其实就只是一个初始值的运算,就是在对象初始化之后这个结果就出来了,我们后面的输入对于结果没有影响,如下所示:
这里我们可以给相关变量赋一个初值看看是啥情况
3、c++中的指针
1、指针的基本了解
这里首先要明确一点就是c++是完全兼容c的,也就是说,c的语法c++全部适用,但是反过来不行,所以这里c++的指针和c的基本上是一样的,在c语言中,指针就是指向变量的地址,c++中也是一样的,下面是指针指向地址的描述:(指针记录的就是地址)
下面来使用指针:(解引用)
2、指针的一些小知识
- 指针占用的内存空间
在32位系统下,一般都是占用4个字节的空间(64位下就是8个字节)
编写函数进行输出,可以看到是四个字节
同时无论是整数还是浮点数都是四个字节,因为这里只是存一个变量地址,所以就不像变量那个大小会改变的。
- 空指针和野指针
首先看一下概念:
空指针:指针变量指向内存中编号为0的空间,一般用于初始化指针变量,不可以被访问,如果访问空指针就会报错(空指针是系统占用的,一般用于这个变量前期不用,后面需要修改的时候使用)
野指针:指针指向非法的内存空间,程序中要避免野指针,野指针也会引发程序报错等异常现象
- const修饰指针,指针常量和常量指针
主要用常量指针和指针常量,下面是常量指针,常量指针指针指向的值不可以改,但是指针指向可以修改
下面是指针常量
下面是同时进行修饰的情况,简单总结下就是const修饰谁,谁就不可以被修改,所以真实情况下就看const离谁更近一点了
3、指针和数组结合
这里需要注意的就是数组是一串地址,并且数组名是这一串地址的开头,如下所示
如果要看看其他元素的话,这里注意指针变量+1其实就是偏移4个字节。
下面来分别使用数组的基本方法和指针来进行遍历查看效果,如下所示:
4、指针和函数
这里主要就是值传递和址传递相关的知识,值传递不会改变值,址传递才会改变,在一些交换的函数中比较常见
但是地址传递可以改变,如下所示:
5、指针数组函数进行联合
这里实现了一个冒泡排序,代码如下:
int arr[10] = { 4,5,8,6,7,4,88,5,5,4 };
void maopao(int *arr,int len)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main()
{
int len = sizeof(arr) / sizeof(arr[0]);//获取数组的长度
maopao(arr, len);
for (int i = 0; i < len; i++)
{
cout << *(arr + i) << endl;
}
system("pause");
}
运行结果如下:
冒泡排序是很经典的排序方式了,很多人认为是最简单的排序方式,其基本原理为,先第一个数逐个和后面的数进行比较,如果大于后面的就进行置换,反正就不变,这样第一轮选出最大的一个数,然后第二轮一样的,只是不和最后的一个数进行比较,因为已经知道了最后的一个数是最大的数字了,这样一直下去就可以了,如果要进行逆序排列,还是根据这个思路对代码进行简单修改,只要修改判断条件为小于一个数就行了,最终代码如下所示:
4、结构体
c++的结构体和c的结构体基本是一样的,这里就不机械记录了,直接写结构体数组函数相关的一些内容,首先是结构体数组,代码如下:
struct MyStruct//定义一个学生结构体
{
string name;
int age;
int score;
};
int main()
{
struct MyStruct stuarr[4] =
{
{"11",15,85},
{"12",25,75},
{"13",63,96},
{"13",63,96}
};
stuarr[3].name = "dfsd";
stuarr[3].age = 56;
stuarr[3].score = 74;
for (int i = 0; i < 4; i++)
{
cout << "姓名" << stuarr[i].name
<< "年龄" << stuarr[i].age
<< "分数" << stuarr[i].score << endl;
}
system("pause");
}
运行结果如下所示:
这里注意如果是老版本的vs的话要加上引用,因为用到了字符型变量(新版的忽略,没有这个问题,我这里是vs2017是需要加上的)
下面是结构体指针,利用指针访问地址来访问结构体的成员:
struct MyStruct//定义一个学生结构体
{
string name;
int age;
int score;
};
int main()
{
MyStruct stu = { "dsfsd",18,45 };
MyStruct *p = &stu; //定义一个结构体指针指向我们准备好的结构体
cout << "姓名" << p->name << "年龄" << p->score << "分数" << p->age << endl;
system("pause");
}
运行结果如下:
下面是结构体之间进行嵌套,代码如下所示:
struct student//定义一个学生结构体
{
string name;
int age;
int score;
};
struct MyStruct//定义一个学生结构体
{
int id;
string name;
int age;
struct student stu; //嵌套的学生结构体
};
int main()
{
MyStruct teacher;
teacher.age = 45;
teacher.id = 85;
teacher.name = "fsfds";
teacher.stu.age = 52;
teacher.stu.name = 48;
teacher.stu.score = 85;
cout << "学生的考试分数" << teacher.stu.score << endl;
system("pause");
}
运行结果如下:
下面是结构体作为函数参数,代码如下所示:
struct student//定义一个学生结构体
{
string name;
int age;
int score;
};
void printf_stu(struct student s) //结构体值传递
{
cout << "考试分数" << s.score << endl;
}
void printf_stu01(struct student *s) //结构体址传递
{
cout << "考试分数" << s->score << endl;
}
int main()
{
struct student s;
s.name = "fsfdfs";
s.age = 20;
s.score = 45;
printf_stu(s);
printf_stu01(&s);
system("pause");
}
下面是是值传递的修改状况
下面是址传递带来的修改
这里要注意就是址传递和值函数的改变的变化仍然存在!
5、内存分区
之前在单片机部分也提到过,没想到学c++的时候又看到了:堆栈记录(stm32为例)
c++在程序执行时,会将内存大方向划分为四个区域:(其中代码区和全局区是在代码执行前就有,堆栈是在代码执行之后才会产生的。)
- 代码区:存放函数代码
这里的代码是二进制代码,就是cpu的机器指令,是经过编译器编译出来的,代码区是共享的,也是只读的,是不可以被修改的。 - 全局区:全局变量,静态变量还有常量
存放了上面说的一些变量,这块区域的数据在程序结束之后会由操作系统进行释放。 - 栈区:编译器自动分配释放,存放函数的参数值和局部变量
- 堆区:程序员分配和释放,程序员如果没有分配和释放会在程序结束之后操作系统进行回收
使用栈来操作,要注意用了一次之后会被释放掉,这里编译器帮保存了一下,但是也只会保存一下:
使用堆来进行操作,利用new关键字,可以将数据开辟到堆区
这里指针本质上也是局部变量,因此函数中我们定义的指针放在栈上面,指针保存的数据放在了堆区。
上面提到了new关键字,这里new是我们向系统申请内存,这路继续介绍下一下相关的操作符,主要是new和delete操作符
- new的基本语法
这里我们使用new重复下上面的测试,可以看到他可以一直都在
使用delete可以删除掉它,这样就不会再出现了,再次输出会显示异常
- 下面我们尝试在堆区来开辟数组
测试代码如下:
同时,这里释放数组这样写