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++头文件和std命名空间(精辟) - C语言中文网

还记得我们学习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等前缀,并直接使用等形式。这种命名方式主要是为了区别于C语言的标准库头文件,并且是为了避免命名冲突。

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++类和对象到底是什么意思(重要!)

C++类和对象到底是什么意思? - C语言中文网

看这个文章比较好

大白话解释的话

1️⃣类(Class)——抽象的模板

类就是“蓝图”或“模板”,它定义了一类事物的 属性(成员变量)行为(成员函数),但它本身不是具体的东西。

比喻:

  • 类就像一份房子的设计图纸,画出了房子的结构(几间房、几扇窗、几层楼)。
  • 设计图本身不能住人,它只是描述了房子应该长什么样。

C++ 示例:

#include <iostream>
using namespace std;

// 定义一个类——Car(汽车)
class Car {
public:
    // 成员变量(属性)
    string brand;
    int year;

    // 成员函数(行为)
    void start() {
        cout << brand << " 启动了!" << endl;
    }
};

这里的 Car 就是一个 ,它描述了汽车有 brandyear,并且可以 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;
}

✅ 这里:

  • car1car2对象(具体的汽车)。
  • 它们的数据可以不同,但行为(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++有三种基本循环:
    1. for 循环(已知循环次数)
    2. while 循环(先判断条件)
    3. 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;
}

3.指针和数组

posted @ 2026-01-24 16:49  ssot_HACKED  阅读(5)  评论(0)    收藏  举报