嵌入式开发学习 C++:day01
C++概述
C++诞生
1972年前后,计算机先驱丹尼斯·里奇开始设计C语言并用它来重写Unix系统,里奇的这个决定催生了计算机领域最石破天惊的两门重炮:Unix和C,这两者都是IT产业中鼻祖级的存在,Unix是现代苹果系统和Linux系统的最初来源,而C则是现代众多编程语言的思想源泉。后来随着IT产业的发展,C语言加入了诸如面向对象、泛型编程等新特性,在1983年前后,贝尔实验室的Biarne Strau-strup(本贾尼·斯特劳斯特卢普)推出了现代编程界中威力最猛的一门重炮:C++。
c++之父「Bjarne Strou-strup」
C++进一步扩充和完善C语言,成为一种多种编程范式的程序设计语言。从其名称可以推断,这种编程语言与C语言有密切的联系,实际上它们通常被合称为C/C++,它们的关系主要体现在以下两点:
- C++是C语言的超集, C语言是C++语言联邦的一部分。
- C++完全兼容C语言,并提供众多火力强大的现代编程范式。
C与C++
据统计,当今世界上不同的编程语言有几百种之多, C++是使用最广泛的编程语言之一,是名副其实的顶流。 C++强大的同时,也是一门比较复杂的语言,它还包括函数式编程、泛型编程等不同的编程范式,有人说C++不是一门编程语言,而是众多编程语言的合集。
C++能干什么
C++主要用在开发系统、算法核心、游戏引擎等对性能高度敏感的领域,除了性能出众, C++的高安全性也是首屈一指的。下面简单罗列C++在IT各开发领域所展露的强大生
命力。
游戏 C++非常适合开发游戏,它的超前设计在应对3D游戏的复杂性时游刃有余, C++对网络的良好支持使得开发大型多人同时在线游戏成为可能,更进一步,由于C++允许直接控制硬件,因此能极大释放CPU的潜能,这种高性能使得C++被广泛应用于不同的游戏和游戏引擎。
动画 许多动画软件都是用C++开发的, C++非常适合用于图像处理、视觉效果呈现等典型的资源消耗型软件,比如:三维动画、建模、仿真、渲染,尤其是虚拟现实VR的场景和角色创建。
浏览器 C++也适合于用来开发浏览器,比如微软著名浏览器IE,谷歌的 Chrome
和 Fire Fox
等,都是C++编写的, C++还是谷歌和Mozilla开源项目的核心呈现引擎的编程语
言。
数据库 C++在数据库软件开发中也占据非常重要的地位,最流行的数据库管理软件 MySQ L
就是其中一例, C++有助于节省时间、成本,提高业务系统的开发效率,还有大量基于
数据库软件访问的应用程序,主要都使用C++开发,这使得数据库访问快速、准确。
多媒体 C++当然可以用于创建媒体播放器,管理音视频文件等,它使得我们享受音乐,访
问和分享视频和音乐文件。相关的C++类库还具有艺术支持、音频和视频流媒体等功能。
甚至提供互联网无线电台的接入。
编译系统 大多数的编译器都使用C++开发,编译器是一种将计算机语言翻译成机器指令的特殊软件,比如C#、 Java等语言的编译器都是由C++进行翻译,然后才能交给CPU去运行。 C++在底层层面为各种各样软件的移植性提供支持。
从这方面理解, C和C++是编写其他语言的语言,是许多编程语言的母语言。
操作系统 开发编译系统和操作系统,从某种程度上讲是C/C++当初被设计出来的初衷,如今世界上通行的底层操作系统几乎全部都是C/C++开发的,比如微软的 Windows2000
和XP
, Unix/Linux
系统,安卓系统和苹果公司的部分操作系统。
应用开发 图形应用软件领域也大量广泛使用C++开发,比如被著名的图形设计软件 Phot oShop
、 Adobe
系列软件,微软的 Office
办公套件和Visual Studio
,还有大量应
用于人工智能领域的算法引擎。
小结
由于出生年代较早, C/C++并不是最易用、语法结构最浅显的语言,这主要是因为早期的计算机资源有限,计算机科学家在开发C/C++的时候,最在意的是最终代码运行的效率,而远非代码编写的难易度,这导致C/C++可以支持非常贴近底层硬件细节的语法,抓地力非常强,因而学习曲线要比一般的编程语言陡峭,也就是对学习者的要求更高。
但也正是因为以上原因, C/C++天生具有高性能的血统,在目前的IT产业中,凡是涉及底层系统、算法核心、游戏引擎等对性能敏感的领域, C/C++都扮演者着不可或缺的角色,因为这些部件哪怕1%的性能波动,都会对整个软件的体验带来本质的改变。 总体来讲, C/C++是一门难学易用的编程语言,是计算机编程领域的重装武器,一旦掌握,威力无比。
C++输入输出
概述
在C++ 中输入和输出操作可以通过标准库中的iostream
头文件来实现。这个头文件提供了类和对象,用于处理输入流(istream)和输出流(ostream)。最常见的来个对象是cin
(控制台输入)和cout
(控制台输出)。
包含头文件
为了使用输入输出功能必须包含以下头文件#include <iostream>
输出到控制台
使用cout
对象可以将数据输出到控制台。<<
运输符 (用于将数据插入到输出流中)。
(cout
相当于一个面向对象的结构体)
typedef struct
{int id;char *name;
}Student;
Student stu;// stu是学生对象
//stu.id 和 stu.name 是对象的成员
基本用法
// 如果不想写std :: 可以在文件顶部添加声明,using namespace std;
std ::cout << "hello World";// 开口方向是数据流方向
// 类似于C语言中的 printf("hello world \n");
输出多个值
int number = 10;
std:: count << "number" <<"number"; // 输出:number :10
从控制台输入
使用cin
对象可以从控制台读取用户输入。>>
运输符(用于从输入流中读取数据)
基本用法
int age;
std::cout << "请输入您的年龄";
std::cin >> "age";
std::cout << "您今年" << "age" << " 岁 " << " \n ";
处理字符串输入
对于字符串类型,建议使用getline
函数,因为他可以读取包含空格的整行文本。
#include <string>std::string name ;// 字符串类型的变量
std::cout << "请输入您的名字";
std::getline(std::cin,name); // 相当于C语言中的scanf("%s",&name)
std::cout << "你好", << "name" << "!\n";
格式化输出
可以使用操纵器来格式化输出,例如设置数字的显示方式、填充字符等。
设置精度
double pi = 3.1415926;
std:: cout.self(std:: ios::fixed);// 固定小数点表示法
std:: cout.precision(2); // 和上面代码配套使用,设置小数保留为2
std:: cout<< "PI 的近似值:" << pi << "\n" ;// 输出结果,PI 的近似值:3.14
对齐方式
//c++ 标准库中的一个头文件, 提供了多种用于控制输入输出格式的操纵器( manipulators) , 通过使用`<iomanip>`, 可以方便的格式化输入输出数据, 例如试着小数位数、 对齐方式、 填充字符等。
#include <iomanip>int num = 42;
std::cout.width(10); // 设置字段宽度为10, 也就是c语言的%m
std::cout.fill("*"); // 填充字符为*
std::cout << std:: left << num << "\n"; // 左对齐输出, 输出结果:
std::cout << std:: right << num << "\n"; // 右对齐输出, 输出结果:
完整的代码
/*************************************************************************> File Name: demo01.cpp> Author: 小刘> Description: 控制台的输入输出> Created Time: 2025-08-18 10:46:58************************************************************************/
#include <iostream>
#include <iomanip>
#include <string>// 声明使用命名空间
using namespace std;int main()
{// 声明一个字符串的变量string name;int age;cout << "请输入您的名字:";getline(cin,name);cout << "请输入您的年龄:";cin >> age ;double height ;cout << "请输入您的身高(米)";cin >> height;cout << " 个人信息 :" << endl; // cout << " 姓名 : " << name << " \n";cout << " 年龄 : " << age << endl;cout.setf(ios :: fixed);cout.precision(2);cout << " 身高: " << height << endl;return 0;
}
内存分区模型
C++ 程序执行时,将内存大方向划分为4个区域
-
代码区:存放函数体的二进制代码,由操作系统进行管理
-
全局区:存放全局变量和静态变量以及常量
-
堆区:由编译器自动分配释放,存放函数的形参值,局部变量等。
-
栈区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。
内存四区意义:
不同区域存放的数据,赋予不同的生命周期给我们大量的灵活编程。
程序运行前
在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域
代码区:
存放CPU执行的机器指令
代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令
全局区:
全局变量和静态变量存放在此
全局区还包含了常量区,字符串常量和其他常量也存放在此
该区域的数据在程序结束后由操作系统释放
案例:
/*************************************************************************> File Name: demo03.cpp> Author: 小刘> Description: 内存分区模型> Created Time: 2025-08-18 14:39:47************************************************************************/
#include <iostream>
#include <iomanip>// 使用std命名空间
using namespace std;// 全局变量:定义在函数外部,默认有初始值是0 \0 NULL
int g_a = 10;
int g_b = 10;// 全局常量:只能赋值一次,一般通过初始值赋值
const int c_g_a = 10;
const int c_g_b = 10;int main()
{// 局部常量:写在函数或者语句块的常量int a = 10;int b = 10;// 打印局部变量地址cout << "局部变量a 的地址:" << &a << endl;cout << "局部变量b 的地址:" << &b << endl;// 打印全局变量地址cout << "全局变量g_a 的地址:" << &g_a << endl;cout << "全局变量g_b 的地址:" << &g_b << endl;// 静态变量 存储在数据区static int s_a = 10;static int s_b = 10;// 打印静态变量地址cout << "静态变量s_a 的地址:" << &s_a << endl;cout << "静态变量s_b 的地址:" << &s_b << endl;cout << "全局常量c_g_a 的地址:" << &c_g_a << endl;cout << "全局常量c_g_b 的地址:" << &c_g_b << endl;cout << "字符串常量的地址:" << &"hello world" << endl;cout << "字符串常量的地址:" << &"hello world1" << endl;// 只读常量 | 局部常量const int c_l_a = 10;const int c_l_b = 10;cout << "局部常量c_l_a 的地址" << &c_l_a <<endl;cout << "局部常量c_l_b 的地址" << &c_l_b <<endl;return 0;
}
总结
- C ++中在程序运行前分为全局区和代码区
- 代码区特点是共享和只读
- 全局区中存放全局变量、静态变量、常量
- 常量区中存放const修饰的全局常量和字符串常量
程序执行后
栈区:
由编译器自动分配释放,存放函数的参数值,局部变量等
注意事项:不要返回局部变量的地址,栈区开辟的空间由编译器释放
示例:
int* func()
{int a = 10;return &a; // 返回a 的地址
}int main()
{int *p = func();cout << *p << endl; // 此时p是有值的,存放的就是func中a的地址,但是由于func整体的内存回收所以导致p地址对应的空间不存在,此时这种现象就属于悬挂指针(空悬指针),它是野指针的一种体现。//ststem("pause");return 0;
}
堆区:
由程序员分配释放,若程序员不释放,程序结束后由操作系统回收。
在C++主要利用new在堆区开辟内存。
int func()
{int *a = new int(10); // 等价于 int* a = malloc(sizeof(int));return 0;
}int main()
{int *p = func();cout << *p << endl;return 0;
}
总结:
堆区数据由程序员管理开辟和释放
堆区数据利用new关键字进行开辟内存,利用delete关键字释放内存
new操作符
C++中利用new 操作符在堆区开辟内存。
堆区开辟的内存,由程序员手动开辟,手动释放,释放利用操作符delete
语法:
new 数据类型;
delete 变量;
利用new创建的内存,会返回内存空间的指针。
示例1基本语法
/*************************************************************************> File Name: demo03.cpp> Author: 小刘> Description: new 操作符> Created Time: 2025-08-18 15:39:41************************************************************************/
#include <iostream>
#include <iomanip>using namespace std;int* func()
{int *a = new int(10); // 在堆区内存申请4字节的空间,存放一个初始数据10return a;
}
int main()
{int *p = func();cout << *p << endl; //10// 利用delete释放堆区数据delete p ;cout << *p << endl;// 报错,释放的空间不可访问return 0;
}
示例2:开辟数据组
/*************************************************************************> File Name: demo04.cpp> Author: 小刘> Description: new操作符,开辟数组> Created Time: 2025-08-18 15:45:51************************************************************************/
#include <iostream>
#include <iomanip>using namespace std;int main()
{int * arr = new int [10];// int(10) 初始化数据位10 ,int[10] 指定数组大小for(int i=0;i<10;i++){arr[i] = i + 100;}for(int i=0;i<10;i++){cout << arr[i] << endl;}delete[] arr;return 0;
}
面试题:new与 malloc的区别?
-
联系:
1.1 new和malloc都可以用来申请堆内存,new运算符内部实现仍然调用了malloc,
1.2 new和malloc作用于基础数据类型,两者没有区别;
-
区别:
2.1 性质上:new是运算符,malloc是函数
2.2内存申请上,malloc是要指明申请的内存大小,new会根据目标类型申请内存;
2.3返回数据类型,malloc只返回void*,new会返回目标类型的指针;
2.4从底层实现原理上,malloc只负责空间申请,new不仅要空间申请,还要负责对象构造。
涉及术语:自由存储区自由存储区:用new运算符申请的内存成为自由存储区C++标准库提供的new运算符内部调用了malloc所以此时的自由存储区就是堆区,但C++支持运算符重载,如果我们对new运算符进行了重载,可能在内存申请上就可以从堆区外的其他区域申请,此时的自由存储区就不是堆区了。
引用
引用的基本使用
**作用:**给变量起别名
语法:
数据类型 &别名 = 原名
示例:
/*************************************************************************> File Name: demo05.cpp> Author: 小刘> Description: 引用> Created Time: 2025-08-18 15:36:43************************************************************************/
#include <iostream>
#include <iomanip>using namespace std;int main()
{// 定义一个变量int a = 10;// 定义引用int &b = a; // 此时a和b的空间是共享的,也就是b的空间是它的引用的对象a的空间 真正的引用是b & 是一个操作符cout << "a = " << a << endl; // a = 10cout << "b = " << b << endl; // b = 10cout << "a 的地址 " << &a << endl; //a 的地址 0x7f5bdffba4cout << "b 的地址 " << &b << endl; //b 的地址 0x7f5bdffba4b = 100;cout << "a = " << a << endl; // a = 100cout << "b = " << b << endl; // b = 100int aa = 10;int *p = &aa;cout << "aa 的地址 " << &aa << endl; //aa 的地址 0x5c725ff750cout << "p 的地址 " << &p << endl; //p 的地址 0x5c725ff748return 0;
}
引用注意事项
- 引用必须初始化,否则没有意义
- 引用在初始化后,不可以改变
引用特点:
①引用不可以改变
②引用对象的值可以改变
示例:
/*************************************************************************> File Name: demo06.cpp> Author: 小刘> Description: 引用相关案例> Created Time: 2025-08-18 15:51:25************************************************************************/
#include <iostream>
#include <iomanip>using namespace std;int main()
{int a = 10;int b = 20;// int &c; 错误,引用必须初始化int &c = a;// 一旦初始化后,就不可以改变c = b; // 这是赋值操作,不是改变引用cout << "a = " << a << endl; // a = 20cout << "b = " << b << endl; // b = 20cout << "c = " << c << endl; // c = 20return 0;
}
引用做函数参数
**作用:**函数传参时,可以引用的技术让形参修饰实参
**优点:**可以简化指针修改实参
示例
/*************************************************************************> File Name: demo07.cpp> Author: 小刘> Description: 值传递、地址传递、引用传递> Created Time: 2025-08-18 15:57:19************************************************************************/
#include <iostream>
#include <iomanip>using namespace std;/*
* 1.值传递
*/
void mySwap01(int a, int b)
{int temp = a ;a = b;b = temp;
}/*
* 2.地址传递
*/
void mySwap02(int* a, int* b)
{int temp = *a ;*a = *b;*b = temp;
}/*
* 3.引用传递(C语言不支持)
*/
void mySwap03(int& a, int& b)
{int temp = a ;a = b;b = temp;
}int main()
{int a = 10 ,b = 20; mySwap01(a,b); // 普通的值传递,形参和实参空间是独立的cout << "a = " << a << ", b = " << b << endl;mySwap02(&a,&b); // 地址传递,形参和实参空间是共享的cout << "a = " << a << ", b = " << b << endl;mySwap02(&a,&b); // 引用传递,形参就是使用实参的空间cout << "a = " << a << ", b = " << b << endl;return 0;
}
总结:通过引用参数的效果同按地址传递一样的。引用的语法更清楚简单
引用做函数返回值
**作用:**引用是可以作为函数的返回值存在的。
**注意:**不要返回局部变量的引用。因为会产生野指针。
示例
/*************************************************************************> File Name: demo08.cpp> Author: 小刘> Description: > Created Time: 2025-08-18 16:12:13************************************************************************/
#include <iostream>
#include <iomanip>using namespace std;/*
* 返回局部变量引用
*/int& text01()
{
int a = 10;
return a;
}/*
* 返回局部变量引用
*/
int& text02()
{static int a = 20; // 局部变量cout << "a = " << a << endl; return a;
}int main()
{// 不能返回局部变量的引用//int& ref = text01();// cout << "ref = " << ref << endl; // 这种写法有问题,因为引用的空间已经提前释放了int& ref2 = text02();cout << "ref2= " << ref2 << endl;text02() = 1000;cout << "ref2= " << ref2 << endl;text02();return 0;
}
引用本质
本质:引用的本质在C++内部实现是一个指针常量
示例:
// 发现是引用, 转换为 int* const ref = &a;
void func(int& ref){
ref = 100; // ref是引用, 转换为*ref = 100
}
int main(){int a = 10;//自动转换为 int* const ref = &a; 指针常量是指针指向不可改, 也说明为什么引用不可更改int& ref = a;ref = 20; //内部发现ref是引用, 自动帮我们转换为: *ref = 20;cout << "a:" << a << endl;cout << "ref:" << ref << endl;func(a);return 0;
}
结论:C++推荐用应用技术,因为方便,引用本质是指针常量,但是所有的指针操作编译器都帮我们做了
常量引用
**作用:**常量引用主要用来修饰形参,放置误操作
在函数新参列表中可以加const 修饰形参
,防止形参改变实参
/*************************************************************************> File Name: demo09.cpp> Author: 小刘> Description: 常量引用> Created Time: 2025-08-18 16:55:03************************************************************************/
#include <iostream>
#include <iomanip>using namespace std;// 引用使用场景,通常来修饰形参
void showValue(const int& v)
{//v+= 10 ;等价于 v = v + 10;cout << v << endl;
}int main()
{// int& ref = 10; 引用本身需要一个合法的内存空间// 加入const就可以了, 编译器优化代码: int temp = 10; const int& ref = temp;const int& ref = 10;// ref = 100; // 加入coust 后不可以修改变量cout << ref <<endl; // 10// 函数中利用常量引用防止误操作修改实参int a =20;showValue(a);return 0;
}
思考题:
什么是零拷贝?
函数扩展
函数默认参数
在C++中,函数的形参列表中的形参可以有默认值。
语法:
返回值类型 函数名( 参数 = 默认值) {}
示例:
/*************************************************************************> File Name: demo10.cpp> Author: 小刘> Description: > Created Time: 2025-08-18 17:24:22************************************************************************/
#include <iostream>
#include <iomanip>using namespace std;int func(int a, int b = 10,int c = 10)// 如果有对应的实参赋值操作, 形参默认值失效
{return a + b + c;
}int main()
{cout << "rst = " << func(20,20) << endl;// ret = 50 如果有实参, 使用实参数据, 如果无实参, 使用形参默认数据cout << "rst = " << func(100) << endl;// ret = 120return 0;
}
函数占位参数
C++ 中函数的形参列表里有占位参数,用来做占位,调用函数时必须填补该位置
语法: 返回值类型 函数名 (数据类型){}
在现阶段函数的占位参数存在意义不大,但是后面的课程中会用到该技术
示例:
//函数占位参数 , 占位参数也可以有默认参数
void func(int a, int) {cout << "this is func" << endl;
}int main() {func(10,10); //占位参数必须填补system("pause");return 0;
}
函数重载
函数重载概述
**作用:**函数名可以相同,提高复用性
函数重载满足的条件
- 同一个作用域下(比如同一个文件中)
- 函数名称相同
- 函数参数类型不同或者参数个数不同或者顺序不同
**注意:**函数的返回值不可以作为函数重载的条件,也就是说函数重载跟返回值无关。
示例:
// 函数重载需要函数都在同一个作用域下, 比如在同一个文件中
void func()
{cout << "func 的调用! " << endl;
}
void func(int a)
{cout << "func (int a) 的调用! " << endl;
}
void func(double a)
{cout << "func (double a)的调用! " << endl;
}
void func(int a ,double b)
{cout << "func (int a ,double b) 的调用! " << endl;
}
void func(double a ,int b)
{cout << "func (double a ,int b)的调用! " << endl;
} //函数返回值不可以作为函数重载条件
//int func(double c, int d)
//{
// cout << "func (double c ,int d)的调用! " << endl;
//}int main() {func();func(10);func(3.14);func(10,3.14);func(3.14 , 10);system("pause");return 0;
}
函数重载注意事项
- 引用作为重载条件
- 函数重载碰到函数默认参数
示例:
/*************************************************************************> File Name: demo11.cpp> Author: 小刘> Description: 函数重载(函数重载注意事项)> Created Time: 2025-08-18 17:37:33************************************************************************/
#include <iostream>
#include <iomanip>using namespace std;/*
* 1.引用作为重载条件
*/
void func(int &a) // 引用不占用任何内存空间,引用的值可变
{cout << "func(int &a) 调用" << endl;
}void func(const int &a) //常量引用,引用的值不可变,一般只读操作
{cout << "func(const int &a) 调用" << endl;
}/*
* 2.函数重载碰到函数默认参数
*/
void func2(int a ,int b = 10)
{cout << "func(int a,int b = 10) 调用" << endl;
}void func2(int a)
{cout << "func(int a) 调用" << endl;
}int main()
{int a = 10;func(a); // 调用无constfunc(10); // 调用有constfunc2(10,20); // func2(10); // 碰到默认参数产生歧义,需要避免return 0;
}