c++学习
README
项目开源来自gitee.com/ASUS_HACKED
有问题请询问科协-网络安全部HACKED(3255154997@qq.com)
想写这个教程其实从2025年就开始了,当时没什么动力去写,而且对程序设计并不是有一个系统性的学习。后面才发现程序设计越来越重要,学校的衔接的知识点太过于简单和跳跃性(跳了很多计的基础大,导致一时看不懂c++的代码)
不过现在是复习,同时也是我对C++的基础知识的理解,如果你遇到问题的话也可以来找我,或者出现问题需要反馈也可以联系我。希望你能对程序设计有一个全新的看法和感兴趣。
如果喜欢程序设计的话也可以加入科协的算法部门和项目部门,感兴趣的也可以咨询我
本教程的书写环境是VS2022,里面的代码文件均为.cpp格式,支持的编译器有dev-c++和code:block(太老太难用了)
首先我先默认你已经初步的学习了c的相关知识,并且学到了运算符一类的东西,如果没有的话请你先学习C语言的内容再回来看这部分的复习
推荐学习网站是:
https://www.runoob.com/cplusplus/cpp-tutorial.html
https://c.biancheng.net/view/2188.html
部分涉及到的知识点都会从上面出来,例子我会进行举例子
C++ 读作“C加加”,是“C Plus Plus”的简称。顾名思义,C++ 是在C语言的基础上增加新特性,玩出了新花样,所以叫“C Plus Plus”,就像 iPhone 7S 和 iPhone 7、Win10 和 Win7 的关系。
从语法上看,C语言是 C++ 的一部分,C语言代码几乎不用修改就能够以 C++ 的方式编译,这给很多初学者带来了不小的困惑,学习 C++ 之前到底要不要先学习C语言呢?
我对这个问题保持中立,但是初学者直接学习 C++ 会非常吃力,Hold 不住,尤其是对计算机内存不太理解的情况下,C++ 是学不懂的。C++ 是一门灵活多变、特性丰富的语言,同时也意味着比较复杂,不易掌握。
不过可以明确地说:学了C语言就相当于学了 C++ 的一半,从C语言转向 C++ 时,不需要再从头开始,接着C语言往下学就可以,所以我强烈建议先学C语言再学 C++。
C++和C语言的血缘关系
现在看来,C++ 和C语言虽然是两门独立的语言,但是它们却有着扯也扯不清的关系。
早期并没有“C++”这个名字,而是叫做“带类的C”。“带类的C”是作为C语言的一个扩展和补充出现的,它增加了很多新的语法,目的是提高开发效率,如果你有 Java Web 开发经验,那么你可以将它们的关系与 Servlet 和 JSP 的关系类比。
这个时期的 C++ 非常粗糙,仅支持简单的面向对象编程,也没有自己的编译器,而是通过一个预处理程序(名字叫 cfront),先将 C++ 代码”翻译“为C语言代码,再通过C语言编译器合成最终的程序。
随着 C++ 的流行,它的语法也越来越强大,已经能够很完善的支持面向过程编程、面向对象编程(OOP)和泛型编程,几乎成了一门独立的语言,拥有了自己的编译方式。
我们很难说 C++ 拥有独立的编译器,例如 Windows 下的微软编译器(cl.exe)、Linux 下的 GCC 编译器、Mac 下的 Clang 编译器(已经是 Xcode 默认编译器,雄心勃勃,立志超越 GCC),它们都同时支持C语言和 C++,统称为 C/C++ 编译器。对于C语言代码,它们按照C语言的方式来编译;对于 C++ 代码,就按照 C++ 的方式编译。
从表面上看,C、C++ 代码使用同一个编译器来编译,所以上面我们说“后期的 C++ 拥有了自己的编译方式”,而没有说“C++ 拥有了独立的编译器”。
再说C++教程
如果针对没有任何编程经验的读者写一本 C++ 的书,那将是一项不小的任务,写出来的书也会非常厚。即使这样,也仅仅是在讲语法。
更重要的是,这些知识你很难全部吸收,会严重打击你的信心,失去学习的兴趣。
1.快速了解c和c++的不同
1.头文件区别
还记得我们学习c语言写的第一段代码吗?
那就是hello,world
下面展示两段代码
#include<stdio.h>
int main()
{
printf("hello,world\n");
return 0;
}
#include<iostream>
int main()
{
std::cout<<"hello,world"<<std::endl;
return 0;
}
再一个手动输入打印字符的代码
include<stdio.h>
int main()
{
char a[100];
scanf("%s",a);//这里没写错哈,因为在c/c++语言中数组名就是数组首元素的地址
printf("%s\n",a);
return 0;
}
#include<iostream>
int main()
{
char a[100];
std::cin>>a;
std::cout<<a<<std::endl;
}
细心的你发现了,c和c++都有共同的特征,那就是分号!(这好像是一个废话了)
c的头文件是stdio.h,c++的头文件是iostream。学长你是不是写错了,怎么iostream不用写.h。(´⊙ω⊙`)
这也是c和c++的区别之一。
c语言中不管是什么类型的头文件(包括自己定义的头文件,都需要写.h)
但是c++中不同的是,只有标准库中没有的东西才需要写,也就是自己定义的头文件,类似于iostream这个头文件是本身这个编译器就有的(但凡是一个c++的编译器,他都会和c语言一样带了一个内置的头文件库)而在C++中,标准库的头文件通常不使用.h扩展名,而是采用了新的命名方式,去掉了扩展名前的stdio等前缀,并直接使用
2.变量和常量
变量和常量还记得是什么吧,这些是c的内容,c++完全一样的
| 对比项 | 变量(Variable) | 常量(Constant) |
|---|---|---|
| 定义 | 用来存储可改变的数据 | 用来存储不可改变的数据 |
| 值是否可变 | ✅ 可以在程序运行过程中修改 | ❌ 一旦定义后不能修改 |
| 使用目的 | 表示会变化的数据(计数、输入、状态等) | 表示固定不变的数据(π、配置值等) |
| 安全性 | 较低,可能被误修改 | 较高,防止被意外修改 |
| 内存占用 | 需要内存空间 | 需要内存空间 |
| 编程习惯 | 普通数据使用 | 能不变就尽量用常量 |
| 示例(C) | int a = 10; |
const int a = 10; |
3.定义和声明
相信很多人对这个都有疑问吧,这个算是扩展性内容,学校都不会讲de。
声明(Declaration):
👉 告诉编译器“有这么个东西”。
定义(Definition):
👉 告诉编译器“这个东西在这里,并且怎么实现”。
从“编译器视角”看区别(非常关键)
1️⃣ 编译器看到「声明」时
编译器只做三件事:
- 记录名字
- 记录类型/签名
- 检查用法是否合法
❗但是:
- 不分配内存
- 不生成实现代码
就像你跟编译器说:
“别急,这个变量/函数以后会有的。
2️⃣ 编译器看到「定义」时
编译器会:
- 分配内存空间(变量)
- 生成函数的机器码(函数)
- 建立符号和实体的一一对应关系
等于你对编译器说:
“东西就在这,用这个!"
变量:声明 vs 定义(重点)
1️⃣ 变量定义(Definition)
int a = 10;
这是 定义,因为:
- 指定了类型:
int - 指定了名字:
a - 分配了内存
- 还给了初始值
📌 即使没有初始值,也是定义:
int b;
2️⃣ 变量声明(Declaration)
extern int a;
这是 声明,因为:
- 只告诉编译器
a是一个int - 不分配内存
- 实际定义在别的文件中
📌 常见场景:多文件编程
// a.c
int a = 10;
// b.c
extern int a;
函数:声明 vs 定义
1️⃣ 函数声明(函数原型)
int add(int x, int y);
特点:
- 有返回值类型
- 有函数名
- 有参数列表
- 没有函数体
📌 这是声明,不是定义。
2️⃣ 函数定义
int add(int x, int y) {
return x + y;
}
特点:
- 包含函数体
{ } - 编译器会生成可执行代码
📌 函数定义本身也是一次声明
| 项目 | 声明(Declaration) | 定义(Definition) |
|---|---|---|
| 是否分配内存 | 否 | 是 |
| 是否生成代码 | 否 | 是 |
| 是否必须唯一 | 否,可多次 | 是,只能一次 |
| 是否提供实现 | 否 | 是 |
| 编译器作用 | 让编译通过 | 真正创建实体 |
| 常见位置 | 头文件(.h) | 源文件(.c) |
| 关键字 | extern | 无 / 具体实现 |
4.作用域
任何一种编程中,作用域是程序中定义的变量所存在的区域,超过该区域变量就不能被访问。C 语言中有三个地方可以声明变量:
1.在函数或块内部的局部变量。
2.在所有函数外部的全局变量。
3.在形式参数的函数参数定义中。
概念:
1.代码块作用域(block scope)
(1)含义:
代码块作用域指的是在程序代码中,由花括号 {} 包围的区域所定义的变量和函数的可见性和生命周期。
在C语言中,代码块可以是函数体、循环体、条件语句体等。
(2)特点:
局部性:变量只在声明它的代码块内有效。
生命周期:变量在代码块执行开始时创建,在代码块执行完毕后销毁。
隐藏性:变量不会在代码块外部被访问。
例子
function myFunction() {
var localVar = "我是局部变量";
console.log(localVar); // 输出: 我是局部变量
}
console.log(localVar); // 报错: localVar is not defined
在上面的例子中,localVar 是在 myFunction 函数内部定义的局部变量,只能在函数内部访问。
在函数外部尝试访问 localVar 会导致错误。
(3)与全局作用域的区别:
全局作用域:在函数外部定义的变量或函数属于全局作用域,可以在整个程序的任何地方访问。
函数作用域:在函数内部定义的变量或函数属于函数作用域,只能在函数内部访问。
2.文件作用域(file scope)
(1)含义:文件作用域指的是在文件(或模块)中定义的变量或函数,可以在整个文件中访问,但在其他文件中不可见。
这种作用域通常用于模块化编程,避免全局污染。
作用范围是从它们的声明位置开始,到文件的结尾处都是可以访问的。
另外,函数名也具有文件作用域,因为函数名本身也是在代码块之外
(2)特点:变量或函数在文件(或模块)的顶层定义。
可以在整个文件中访问,但在其他文件中不可见(除非显式导出)。
有助于模块化设计和代码组织
3.原型作用域(prototype scope)
(1)含义:原型作用域只适用于那些在函数原型中声明的参数名。函数在声明的时候可以不写参数的名字(但参数类型是必须要写上的).
多次尝试可以发现,函数原型的参数名还可以随便写一个名字,不必与形式参数相匹配(当然,这样做毫无意义)。允许你这么做,只是因为原型作用域起了作用。
(2)特点:
对象可以通过原型链访问其原型对象上的属性和方法。
如果对象本身没有某个属性或方法,会沿着原型链向上查找。
4.函数作用域(function scope)
(1)含义:函数作用域是指在函数内部定义的变量或函数,其可见性和生命周期仅限于该函数内部。
(2)特点:
局部变量:在函数内部声明的变量(使用 var、let 或 const)只能在函数内部访问,外部无法访问。
生命周期:函数执行完毕后,局部变量会被销毁,释放内存。
嵌套函数:在函数内部定义的函数(嵌套函数)可以访问外部函数的变量,但外部函数无法访问嵌套函数的变量。
函数作用域确保了变量的封装性,避免了命名冲突,并优化了内存管理
例子:
function myFunction() {
let x = 10; // 局部变量
console.log(x); // 可以访问
}
myFunction();
console.log(x); // 报错,x 未定义
解释(不想看长串的看这里,大白话解释)
核心概念:
作用域 = 变量或函数在哪一块代码里能被使用 / 谁能看见它
换句话说:写在哪里,能用多久,就决定了它的作用域
1️⃣ 代码块作用域(block scope)
- 通俗理解:
{}就像一个小房间,里面定义的变量只能在里面用,出了房间就“消失”。
#include <stdio.h>
int main() {
{
int a = 10; // 代码块作用域变量
printf("a = %d\n", a); // ✅ 可以访问
}
// printf("%d\n", a); // ❌ 错误,出了块外就访问不到
return 0;
}
- 口诀:
“出生在括号里,死在括号外”
2️⃣函数作用域(function scope)
-
通俗理解:
函数本身就是一个大房间,函数里的变量只能在函数里用,函数执行完就消失。
#include <stdio.h>
void myFunction() {
int x = 10; // 局部变量
printf("x = %d\n", x); // ✅ 可以访问
}
int main() {
myFunction();
// printf("%d\n", x); // ❌ 错误,函数外访问不到
return 0;
}
- 口诀:
“函数作用域 = 函数内部能用,外部不能用”
3️⃣ 全局 / 文件作用域(file scope)
- 通俗理解:
写在函数外面的变量,整个文件都能用。
#include <stdio.h>
int g = 100; // 文件作用域 / 全局变量
void printGlobal() {
printf("g = %d\n", g); // ✅ 可以访问
}
int main() {
printf("g = %d\n", g); // ✅ 可以访问
printGlobal();
return 0;
}
- 口诀:
“写在外面,大家都能用(当前文件内)”
4️⃣ 函数参数作用域
- 通俗理解:
函数参数就像函数里的局部变量,只能在函数内部用,函数执行完就消失。
#include <stdio.h>
void add(int a, int b) { // 函数参数
printf("sum = %d\n", a + b); // ✅ 只能在函数内部使用
}
int main() {
add(5, 3);
// printf("%d\n", a); // ❌ 错误,函数外访问不到
return 0;
}
- 口诀:
“函数参数 = 局部变量,函数内能用”
| 作用域类型 | 小白理解 | 生命周期 | 可见范围 |
|---|---|---|---|
| 代码块作用域 | {} 内定义的变量 |
进入块就创建,出块就销毁 | 只在块内可用 |
| 函数作用域 | 函数里的变量 | 函数开始创建,结束销毁 | 函数内部可用,外部不可用 |
| 全局 / 文件作用域 | 函数外定义的变量 | 程序开始到程序结束 | 当前文件中任何位置可用 |
| 函数参数 | 函数里的参数 | 函数开始到结束 | 只能在函数内用 |
5.局部变量和全局变量
| 特性 | 局部变量 | 全局变量 |
|---|---|---|
| 定义位置 | 定义在函数或代码块内部 | 定义在所有函数外部,通常在文件顶部 |
| 作用域 | 只能在定义它的函数或代码块内使用 | 在整个文件中(甚至跨文件通过 extern)都可以使用 |
| 生命周期 | 函数调用时创建,函数结束时销毁 | 程序运行时创建,程序结束时销毁 |
| 初始值 | 如果不初始化,值是 不确定的(垃圾值) | 如果不初始化,默认初始化为 0(整型)或NULL(指针) |
| 内存位置 | 栈(stack)<底层知识> | 数据段(data segment)或全局存储区<底层知识> |
| 命名冲突 | 不会影响其他函数的同名变量 | 全局变量容易与其他文件或函数中的变量冲突,需要小心命名 |
| 访问方式 | 仅在定义的函数内部访问 | 可以在整个程序中访问,如果使用 extern 可以跨文件访问 |
#include <stdio.h>
// 全局变量
int globalVar = 10;
void func() {
// 局部变量
int localVar = 5;
printf("局部变量 localVar = %d\n", localVar);
printf("全局变量 globalVar = %d\n", globalVar);
}
int main() {
func();
// printf("%d", localVar); // 错误,localVar 在这里不可见
printf("全局变量 main中访问 globalVar = %d\n", globalVar);
return 0;
}
局部变量短命、只在局部使用;全局变量长寿、可在整个程序中使用。
Q:为什么不要使用大量的全局变量?
因为大量使用全局变量会对程序结构产生不良的影响,而且可能导致程序中各个函数之间具有太多的数据联系(在模块化程序设计的指导下,我们应该尽量设计内聚性强,耦合性弱的模块。
也就是要求你函数的功能要尽量单一,与其他函数的相互影响尽可能地少,而大量使用全局变量恰好背道而驰)
6.c++类和对象到底是什么意思(重要!)
看这个文章比较好
大白话解释的话
1️⃣类(Class)——抽象的模板
类就是“蓝图”或“模板”,它定义了一类事物的 属性(成员变量) 和 行为(成员函数),但它本身不是具体的东西。
比喻:
- 类就像一份房子的设计图纸,画出了房子的结构(几间房、几扇窗、几层楼)。
- 设计图本身不能住人,它只是描述了房子应该长什么样。
C++ 示例:
#include <iostream>
using namespace std;
// 定义一个类——Car(汽车)
class Car {
public:
// 成员变量(属性)
string brand;
int year;
// 成员函数(行为)
void start() {
cout << brand << " 启动了!" << endl;
}
};
这里的 Car 就是一个 类,它描述了汽车有 brand、year,并且可以 start()。
注意:这个类只是“模板”,还没有一辆真正的车。
2️⃣ 对象(Object)——具体实例
对象就是类的“实例”或“实体”,它是根据类的蓝图创建出来的具体东西。每个对象都有自己的数据(属性),并且可以调用类的方法。
比喻:
- 如果类是设计图,那么对象就是盖好的房子。
- 你可以建很多房子(对象),它们都是根据同一张图(类)建的,但房子的家具、颜色可以不同。
C++ 示例:
int main() {
// 创建对象
Car car1;
Car car2;
// 给对象属性赋值
car1.brand = "Toyota";
car1.year = 2020;
car2.brand = "Honda";
car2.year = 2022;
// 调用对象的方法
car1.start(); // 输出:Toyota 启动了!
car2.start(); // 输出:Honda 启动了!
return 0;
}
✅ 这里:
car1和car2是 对象(具体的汽车)。- 它们的数据可以不同,但行为(
start())是同一个类定义的。
3️⃣ 核心总结
| 概念 | 含义 | 举例 |
|---|---|---|
| 类 | 描述事物的模板/蓝图 | 汽车的设计图 |
| 对象 | 类的具体实例/实体 | 根据设计图建的实际汽车 |
| 成员变量 | 对象的属性 | 汽车的品牌、年份 |
| 成员函数 | 对象的行为 | 启动汽车、刹车 |
一句话记忆:类是模板,对象是实体。
学长学长,之前的c和c++打印的内容,发现了c++在打印的时候都要写std::,有没有更加简单的方法呢
有的有的,兄弟有的。
这就是我们接下来要介绍的东西。
7.c++的命名空间
[C++命名空间(名字空间)详解 - C语言中文网][(https://c.biancheng.net/view/2192.html)
1.相关概念
1️⃣ 命名空间的概念
- 命名空间就是一个“容器”,用来 管理名字(变量名、函数名、类名),防止不同地方起了相同名字导致冲突。
- 作用:避免 命名冲突,尤其是当程序很大或者使用了很多库时。
- 可以理解为 “一个名字的文件夹”,名字相同的变量放在不同文件夹里不会冲突。
2️⃣ 语法
namespace MySpace { // 定义命名空间
int a = 10;
void printA() {
std::cout << "a = " << a << std::endl;
}
}
- 这里
MySpace是命名空间的名字。 - 里面的变量
a和函数printA()都属于MySpace。
3️⃣ 使用命名空间的方式
方法1:限定名访问
#include <iostream>
using namespace std;
namespace MySpace {
int a = 10;
void printA() {
cout << "a = " << a << endl;
}
}
int main() {
MySpace::printA(); // 用命名空间限定符访问
cout << "a = " << MySpace::a << endl;
return 0;
}
输出:
a = 10
a = 10
方法2:使用 using 声明
- 可以引入命名空间里的名字,直接使用,不用写
::。
using MySpace::a; // 只引入 a
using MySpace::printA; // 引入 printA
int main() {
printA(); // 直接使用
cout << a << endl; // 直接使用
return 0;
}
方法3:使用 using namespace
- 引入整个命名空间,所有名字都可以直接使用,但可能引入冲突。
using namespace MySpace;
int main() {
printA(); // 直接用
cout << a << endl;
return 0;
}
⚠️ 注意:大型项目一般 不要随意
using namespace std;,容易和自己定义的名字冲突。
4️⃣ 示例:避免命名冲突
#include <iostream>
using namespace std;
namespace Space1 {
int value = 100;
}
namespace Space2 {
int value = 200;
}
int main() {
cout << Space1::value << endl; // 100
cout << Space2::value << endl; // 200
return 0;
}
- 同名变量
value在不同命名空间里不冲突。 - 这就是命名空间的核心作用。
5️⃣ 总结
| 概念 | 说明 |
|---|---|
| namespace | 命名空间,用于管理名字,防止冲突 |
| 访问方式 | 命名空间名::名字 或 using 引入 |
| 作用 | 避免全局变量、函数、类重名,特别是大型项目或引用库时 |
| 注意事项 | 小心使用 using namespace std;,大项目容易冲突 |
🔹 类比:命名空间就像“文件夹”,文件夹里可以有同名文件,不同文件夹互不干扰。
命名空间 不是函数,它和函数是完全不同的概念。我们来区分一下:
2.命名空间和函数
1️⃣ 命名空间 vs 函数
| 特性 | 命名空间(namespace) | 函数(function) |
|---|---|---|
| 作用 | 用来管理名字,防止变量、函数、类等冲突 | 用来执行特定操作或计算,封装可重复使用的代码 |
| 是否执行 | 只是一个“容器”,不会被调用执行 | 会被调用执行,完成任务 |
| 语法 | namespace Name { ... } |
返回类型 函数名(参数) { ... } |
| 内容 | 变量、函数、类等都可以放进去 | 一般包含可执行语句(代码块) |
| 访问 | Name::变量名 或 using |
直接用 函数名() 调用 |
2️⃣ 核心区别
- 命名空间 = “名字的文件夹”,只是组织代码,不会运行。
- 函数 = “可执行的动作”,必须调用才能执行。
3️⃣ 举例对比
#include <iostream>
using namespace std;//这也是命名空间哈,不然就得写std::cout了
// 命名空间
namespace MySpace {
int a = 10; // 变量
void show() { // 函数也可以放在命名空间里
cout << "Hello from MySpace" << endl;
}
}
// 普通函数
void sayHello() {
cout << "Hello from function" << endl;
}
int main() {
// 调用命名空间里的函数
MySpace::show();
// 调用普通函数
sayHello();
// 访问命名空间里的变量
cout << MySpace::a << endl;
return 0;
}
输出:
Hello from MySpace
Hello from function
10
MySpace只是一个容器,它里面的函数和变量可以访问,但命名空间本身 不会被调用执行。sayHello()是函数,需要调用才会执行。
💡 比喻:
- 命名空间 = “房间”,房间里放家具(变量)、工具(函数)。
- 函数 = “机器人”,你按下开关(调用函数),它开始工作。
只需要留意using namespace std;,它声明了命名空间 std,后续如果有未指定命名空间的符号,那么默认使用 std,代码中的 string、cin、cout 都位于命名空间 std。
8.复习区域
1️⃣ 字符和字符串
概念
- 字符(char):存储单个字符,占1字节。
- 字符串:字符数组,以
\0结尾。 - C++中也可用
char[]或string类型。
#include <iostream>
using namespace std;
int main() {
// 单个字符
char ch = 'A';
cout << "ch = " << ch << endl;
// 字符串(字符数组)
char str[] = "Hello"; // 自动在末尾加 '\0'
cout << "str = " << str << endl;
// 修改字符串中的字符
str[0] = 'h';
cout << "修改后 str = " << str << endl;
return 0;
}
输出:
ch = A
str = Hello
修改后 str = hello
🔹 注:C++里推荐用
string类型,更方便:string是c++的特性,可以动态定义一个字符串,也就是说string可以根据输入的字符串分配内存空间
string s = "Hello";
s[0] = 'h';
cout << s << endl; // hello
2️⃣ 数组
概念
- 数组是一组相同类型数据的集合,连续存储。
- 下标从 0 开始。
- 可以是单维或多维。
#include <iostream>
using namespace std;
int main() {
// 单维数组
int arr[5] = {1, 2, 3, 4, 5};
cout << "arr[2] = " << arr[2] << endl; // 访问第3个元素
// 多维数组(2行3列)
int matrix[2][3] = {{1,2,3},{4,5,6}};
cout << "matrix[1][2] = " << matrix[1][2] << endl; // 输出6
return 0;
}
3️⃣ 循环
概念
- 循环用于重复执行一段代码。
- C/C++有三种基本循环:
for循环(已知循环次数)while循环(先判断条件)do-while循环(先执行一次再判断)
#include <iostream>
using namespace std;
int main() {
// for循环
cout << "for循环输出1~5: ";
for(int i=1; i<=5; i++)
cout << i << " ";
cout << endl;
// while循环
int j = 1;
cout << "while循环输出1~5: ";
while(j <= 5) {
cout << j << " ";
j++;
}
cout << endl;
// do-while循环
int k = 1;
cout << "do-while循环输出1~5: ";
do {
cout << k << " ";
k++;
} while(k <= 5);
cout << endl;
return 0;
}
输出:
for循环输出1~5: 1 2 3 4 5
while循环输出1~5: 1 2 3 4 5
do-while循环输出1~5: 1 2 3 4 5
4️⃣ 字符串 + 循环结合示例
- 遍历字符串
#include <iostream>
using namespace std;
int main() {
char str[] = "Hello";
cout << "遍历字符串: ";
for(int i=0; str[i] != '\0'; i++) { // 遍历直到 '\0'
cout << str[i] << " ";
}
cout << endl;
return 0;
}
输出:
遍历字符串: H e l l o
5️⃣ 总结概念
| 概念 | 说明 |
|---|---|
| char | 存储单个字符 |
| 字符串 | 字符数组,以 \0 结尾 |
| 数组 | 存储相同类型元素的连续内存 |
| for循环 | 已知循环次数,常用 |
| while循环 | 条件先判断,可能一次都不执行 |
| do-while循环 | 条件后判断,至少执行一次 |
| 遍历字符串 | 用循环 + \0 结束条件 |
2.指针
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:
type *var-name;
在这里,type 是指针的基类型,它必须是一个有效的 C++ 数据类型,var-name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:
int *ip; /* 一个整型的指针 */
double *dp; /* 一个 double 型的指针 */
float *fp; /* 一个浮点型的指针 */
char *ch; /* 一个字符型的指针*/
1️⃣ 生活类比:指针就像“房子的门牌号”
想象:
x是你家里的 房子(变量,里面存东西)p是 门牌号(指针,告诉你房子在哪)
int x = 42; // 房子里放着 42
int* p = &x; // p 是 x 的门牌号
x= 房子里的东西(42)&x= 房子的地址(门牌号)p= 你手里的门牌号*p= 打开门牌号对应的房子,看里面的东西(解引用)
2️⃣ 类比操作
*p = 100; // 打开门,把房子里的东西改成 100
生活类比:
你有门牌号,就可以打开房子改东西
不用直接站在房子旁边
3️⃣ 指针的动态性
你可以换门牌号,让它指向另一个房子:
int y = 200;
p = &y; // p 现在指向房子 y
*p = 300; // 改变 y 里的东西
类比:
p 不固定在一个房子上,可以随时指向别的房子
为什么需要指针?
一、指针的核心优势
1️⃣ 间接访问(Indirect Access)
指针可以让你通过地址访问变量,这在很多场景非常有用。
int x = 10;
int* p = &x;
*p = 20; // 间接修改 x
- 优点:函数可以修改外部变量
- 场景:函数参数传递中,如果不想拷贝大对象,可以传指针
2️⃣ 动态内存管理(Dynamic Memory)
指针可以指向动态分配的内存,大小在运行时确定。
int* arr = new int[n]; // n 运行时确定
...
delete[] arr;
- 优点:灵活使用内存,不受编译时限制
- 场景:大数据、可变数组、图结构等
3️⃣ 高效操作数组 / 字符串 / 缓冲区
int arr[100];
int* p = arr;
p += 10; // 直接跳到 arr[10]
- 指针可以直接算地址,速度比下标访问快
- 优点:高性能控制
- 场景:系统底层、网络缓冲区、文件读写
4️⃣ 支持复杂数据结构(链表 / 树 / 图)<后面会学数据结构的>
- 链表:
struct Node {
int val;
Node* next;
};
- 树 / 图也依赖指针或智能指针
- 优点:可以动态构建任意结构
5️⃣ 函数指针(Callback / 多态前身)
void (*func_ptr)(int) = &myFunction;
func_ptr(10);
- 指针可以指向函数
- 优点:实现回调、策略模式、插件机制
6️⃣ 节省内存拷贝
void process(std::vector<int>* v);
- 传指针而不是整个数组或对象
- 优点:避免大对象拷贝,提高性能
二、指针的本质优势总结
| 优点 | 说明 |
|---|---|
| 间接访问 | 可以通过地址修改或读取变量 |
| 动态内存分配 | 内存大小运行时确定 |
| 高效操作数组/缓冲区 | 直接操作内存地址,速度快 |
| 支持复杂数据结构 | 链表、树、图等结构依赖指针 |
| 函数指针 | 实现回调、策略模式 |
| 节省拷贝 | 传递指针避免复制大对象 |
示例代码
//比大小并按照从小到大的顺序进行交换
#include<iostream>
using namespace std;
{
int a, b, c, t;
int*pa, *pb, *pc;//指针
printf("请输入三个数:");
cin>>a>>b>>c;
pa = &a;//指针pa存放变量a的地址
pb = &b;
pc = &c;
if (a > b)
{
t = *pa;
*pa = *pb;
*pb = t;
}
if (a > c)
{
t = *pa;
*pa = *pc;
*pc = t;
}
if (b > c)
{
t = *pb;
*pb = *pc;
*pc = t;
}
printf("%d <= %d <= %d\n", *pa, *pb, *pc);
//cout << *pa << " <= " << *pb << " <= " << *pc;
}

浙公网安备 33010602011771号