C++ 入门基础详解:从第一个程序到核心语法特性

🎬 个人主页 :道尔柯南
❄专栏传送门 :《C语言》《C++》《Linux操作系统》

昙花一现,却等待了整个白昼;蝉鸣一夏,却蛰伏了好几个四季。 技术专栏:C++ 编程基础 · 适合初学者 · 关键词:命名空间、输入输出、函数重载、引用、内联函数、nullptr

一、C++ 简介与发展历程

1.1 什么是 C++?

C++ 是一种通用的编程语言,在 C 语言的基础上发展而来,并进一步提供了面向对象编程、泛型编程等能力。它兼顾了较强的运行效率和丰富的抽象能力,广泛应用于系统软件、游戏引擎、音视频处理、桌面应用和高性能服务等领域。

C++ 课程通常会涉及以下内容:

  • 基础语法: 变量、函数、控制流程、引用等。
  • 面向对象编程: 类、对象、封装、继承和多态。
  • STL 标准模板库: 常用容器、算法和迭代器。
  • 高阶数据结构与编程技术: 为复杂项目开发打下基础。

1.2 C++ 的发展历史

1979

起源阶段

Bjarne Stroustrup 在贝尔实验室开展相关研究,希望在保留 C 语言效率的同时,提升大型软件开发中的表达能力与可维护性。

1983

正式命名为 C++

语言逐步引入类、封装、继承等面向对象特性,形成 C++ 的早期形态。

1998

第一个 ISO C++ 国际标准

C++98 标准发布,语言及其标准库的发展进入更加规范的阶段。

持续演进

不断扩展语言能力

C++ 后续持续更新,逐步增加了新的语言特性和标准库功能,以适应现代软件开发需求。

其中,STL(Standard Template Library,标准模板库)是 C++ 学习过程中的重要组成部分。它提供了常用的数据结构与算法,能够帮助开发者减少重复实现基础功能的工作。

1.3 C++ 学习参考文档

学习 C++ 时,建议结合不同类型的参考资料:

需要注意的是,文档的更新范围和组织方式不同,实际使用时应根据所使用的 C++ 标准选择相应资料。

二、为什么要学习 C++?

C++ 不仅是一门基础编程语言,也是一项能够应用于多种技术方向的开发技能。

使用 buildroot 和 QEMU 调试 linux 内核 - kicey's blog

  1. 系统软件开发

操作系统、编译器、数据库及部分浏览器核心组件,都涉及 C++ 或其他系统级编程技术。

Where to Find the Suitable Crowd Cheering Audio and How to Use It

  1. 音视频处理

FFmpeg、WebRTC 等技术生态中广泛使用 C/C++,涉及编解码、媒体处理和实时通信。

Qt Design Studio 1.1 Beta released

  1. 桌面客户端开发

C++ 配合 Qt,可以开发跨平台图形用户界面(GUI)应用,例如工具软件、桌面管理系统等。

Game Development Using the Open-Source Godot Game Engine - Tero Salmela BACHELOR'S THESIS - Theseus

  1. 游戏与高性能服务

游戏引擎、游戏服务器、实时处理系统等场景,经常需要利用 C++ 的性能和底层控制能力。

Research & Science --- AI Labs | Scientific AI R&D

  1. 嵌入式与底层开发

智能设备、车载系统、摄像头和机器人等产品中,也可能使用 C++ 完成应用逻辑和设备控制。

因此,学习 C++ 的意义不仅在于掌握语法,更在于理解程序运行、内存管理、数据结构和软件工程的基础知识。

三、编写第一个 C++ 程序:Hello World

学习一门编程语言,通常从输出一条简单的信息开始。

3.1 使用 C 风格的输入输出

C++ 兼容 C 语言的许多语法,因此可以使用 printf() 输出信息。

复制代码
#include <stdio.h>

int main()
{
    printf("Hello World\n");
    return 0;
}

代码说明:

  • #include <stdio.h>:引入标准输入输出相关声明。
  • int main():程序入口函数。
  • printf():向标准输出打印文本。
  • \n:表示换行。
  • return 0:表示程序正常结束。

3.2 使用 C++ 风格的输入输出

C++ 提供了自己的流式输入输出机制。

复制代码
#include <iostream>

int main()
{
    std::cout << "Hello World" << std::endl;
    return 0;
}

这里有三个需要掌握的知识点:

语法 作用
iostream 提供标准输入输出流相关功能
std::cout 标准输出对象
std::endl 插入换行并刷新输出流

其中,<< 在这里是流插入运算符,用于把数据送入输出流。

C++ 程序通常使用 .cpp 作为源文件后缀。使用 GCC 工具链时,可以通过 g++ 编译:

复制代码
g++ test.cpp -o test

在 Linux 环境中运行:

复制代码
./test

学习建议: 初学阶段可以先学会运行示例程序,再逐步理解每一条语句的作用,不必在第一次接触时就完全理解标准库的内部实现。

四、命名空间 namespace:解决名称冲突

4.1 为什么需要命名空间?

在大型项目中,不同模块可能定义相同名称的变量或函数。

例如,某个模块定义了一个名为 rand 的变量,而程序中又使用了标准库提供的 rand() 函数。如果没有合理的名称组织方式,就容易发生名称冲突。

C++ 引入了 namespace,用于把相关标识符组织在独立的命名空间中。

4.2 命名空间的定义

基本语法如下:

复制代码
namespace bit
{
    int value = 10;

    int Add(int left, int right)
    {
        return left + right;
    }
}

上述代码定义了名为 bit 的命名空间,其中包含一个变量和一个函数。

使用作用域解析运算符 ::,可以明确指定访问哪个命名空间的成员:

复制代码
#include <iostream>

namespace bit
{
    int value = 10;
}

int main()
{
    std::cout << bit::value << std::endl;
    return 0;
}

输出结果:

复制代码
10

这里的 bit::value 表示访问 bit 命名空间中的 value。

4.3 命名空间可以嵌套

命名空间支持嵌套定义,用于进一步组织代码。

复制代码
namespace company
{
    namespace developer
    {
        int level = 1;
    }
}

int main()
{
    int n = company::developer::level;
    return 0;
}

通过 company::developer::level,可以明确地访问嵌套命名空间中的成员。

在多个源文件中,只要命名空间名称相同,它们的定义可以共同组成同一个命名空间。这种方式适合将同一模块的声明与实现拆分到不同文件中。

4.4 命名空间的三种使用方式

方式一:推荐用于项目代码

使用完整限定名称

复制代码
std::cout << "Hello" << std::endl;

优点是名称明确,可以有效减少冲突。

方式二:按需引入

使用 using 声明单个成员

复制代码
using std::cout;
using std::endl;

cout << "Hello" << endl;

适合经常使用、且不容易与其他名称冲突的成员。

方式三:谨慎使用

引入整个命名空间

复制代码
using namespace std;

cout << "Hello" << endl;

写小型练习程序时比较方便,但大型项目中可能增加名称冲突风险。

需要特别注意:namespace 本身不会创建一个新的独立变量生命周期。它主要用于组织名称与控制名称查找,而变量的生命周期取决于其存储期等因素。

五、C++ 输入与输出:cin、cout 和 endl

C++ 的流式输入输出可以根据数据类型选择相应的处理方式,使用起来比较方便。

5.1 基本输入输出示例

复制代码
#include <iostream>

int main()
{
    int age = 0;
    double height = 0.0;

    std::cout << "请输入年龄和身高:" << std::endl;
    std::cin >> age >> height;

    std::cout << "年龄:" << age << std::endl;
    std::cout << "身高:" << height << std::endl;

    return 0;
}

假设输入:

复制代码
20 1.75

输出结果:

复制代码
请输入年龄和身高:
年龄:20
身高:1.75

5.2 常用流操作符

符号 含义
std::cin 标准输入流对象
std::cout 标准输出流对象
>> 流提取运算符,常用于输入
<< 流插入运算符,常用于输出
std::endl 换行并刷新输出流

与 scanf()、printf() 相比,C++ 流式输入输出可以通过类型相关的重载机制处理不同类型的数据,减少手动指定格式符的需要。

5.3 endl 与换行符的区别

复制代码
std::cout << "Hello\n";

和:

复制代码
std::cout << "Hello" << std::endl;

都可以输出换行,但 std::endl 还会刷新输出流。频繁刷新可能影响输出效率,因此不需要立即刷新时,可以优先考虑使用 \n。

对于大量输入输出的程序,介绍以下优化方式:

复制代码
#include <iostream>

int main()
{
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    // 后续进行输入输出操作
    return 0;
}

这些设置可以在合适的场景下提高 C++ 流式输入输出的效率。不过,关闭同步后应避免随意混用 C 与 C++ 的标准输入输出,以免产生输出顺序等问题。

六、缺省参数:让函数调用更加灵活

6.1 什么是缺省参数?

缺省参数,也称默认参数,是指在声明函数时,为部分形参指定默认值。

当调用函数时,如果没有提供相应实参,就会使用默认值。

复制代码
#include <iostream>

void Func(int value = 10)
{
    std::cout << value << std::endl;
}

int main()
{
    Func();      // 使用默认值 10
    Func(100);   // 使用传入的值 100

    return 0;
}

输出结果:

复制代码
10
100

6.2 全缺省与半缺省参数

全缺省参数:

所有参数都设置默认值。

复制代码
void Print(int a = 10, int b = 20, int c = 30);

调用时可以提供不同数量的参数:

复制代码
Print();
Print(1);
Print(1, 2);
Print(1, 2, 3);

半缺省参数:

只为部分参数设置默认值。

复制代码
void Print(int a, int b = 10, int c = 20);

此时可以这样调用:

复制代码
Print(100);
Print(100, 200);
Print(100, 200, 300);

6.3 缺省参数的注意事项

  1. 半缺省参数必须从右向左连续设置默认值。
  2. 调用函数时,实参按照从左到右的顺序传递,不能跳过中间参数而直接指定后面的参数。
  3. 当函数声明与定义分离时,默认参数通常写在函数声明中,不应在声明和定义中重复指定。

例如,在栈初始化函数中,可以把默认容量设置为 4:

复制代码
// Stack.h
void STInit(ST* ps, int n = 4);

// Stack.cpp
void STInit(ST* ps, int n)
{
    // 初始化栈
}

这样,普通场景下可以直接调用 STInit(&stack);如果明确知道需要较大的初始容量,也可以调用 STInit(&stack, 1000)。

缺省参数能够减少重复代码,让函数接口更加简洁。

七、函数重载:同名函数实现不同功能

7.1 什么是函数重载?

C++ 允许在同一作用域中定义多个同名函数,只要这些函数的参数列表能够形成有效区分,就可以构成函数重载。

重载常见的区分方式包括:

  • 参数类型不同。
  • 参数个数不同。
  • 参数类型的顺序不同。

例如:

复制代码
#include <iostream>

// 参数类型不同
int Add(int a, int b)
{
    return a + b;
}

double Add(double a, double b)
{
    return a + b;
}

// 参数个数不同
void Print()
{
    std::cout << "无参数版本" << std::endl;
}

void Print(int value)
{
    std::cout << "整数:" << value << std::endl;
}

int main()
{
    std::cout << Add(10, 20) << std::endl;
    std::cout << Add(1.5, 2.5) << std::endl;

    Print();
    Print(100);

    return 0;
}

输出结果:

复制代码
30
4
无参数版本
整数:100

编译器会根据调用时提供的实参,选择匹配的函数。

7.2 为什么不能只根据返回值重载?

例如,下面两个函数不能仅凭返回类型构成有效的重载:

复制代码
int Func()
{
    return 1;
}

// 错误:不能仅通过返回类型区分
// double Func()
// {
//     return 1.0;
// }

当代码调用 Func() 时,编译器无法仅根据返回值来决定应该调用哪个版本。

7.3 缺省参数与函数重载的组合风险

下面的代码看起来定义了两个不同的函数:

复制代码
void Func()
{
}

void Func(int a = 10)
{
}

但是调用 Func() 时,两个函数都可能匹配,造成调用歧义。

学习重点: 函数重载依据参数列表区分,而默认参数可能扩大某个函数的可调用范围。设计接口时,应尽量避免产生歧义。

八、引用:C++ 中的重要概念

引用是 C++ 相比 C 语言的一个重要语法特性,也是理解后续类、对象和数据结构实现的基础。

8.1 引用的基本概念

引用可以理解为一个已经存在的变量的别名。

基本语法:

复制代码
类型& 引用名 = 变量名;

例如:

复制代码
#include <iostream>

int main()
{
    int a = 10;
    int& b = a;

    b = 20;

    std::cout << a << std::endl;
    std::cout << b << std::endl;

    return 0;
}

输出结果:

复制代码
20
20

这是因为 b 是 a 的引用,对 b 进行赋值,实际修改的就是 a。

8.2 引用的三个重要特性

第一,引用定义时必须初始化。

复制代码
int a = 10;
int& b = a;

不能直接定义一个没有绑定对象的普通引用。

第二,一个变量可以有多个引用。

复制代码
int a = 10;

int& b = a;
int& c = a;

此时 a、b、c 都指向同一个变量所代表的对象。

第三,引用一旦绑定,就不能重新绑定到其他对象。

复制代码
int a = 10;
int b = 20;

int& ref = a;
ref = b;

最后一行并不是让 ref 改为引用 b,而是把 b 的值赋给 a。执行后,a 和 ref 的值都为 20,而 ref 仍然引用 a。

8.3 引用传参:简化函数调用

引用最常见的用途之一,就是让函数直接操作调用者传入的变量。

例如,使用引用实现交换函数:

复制代码
#include <iostream>

void Swap(int& x, int& y)
{
    int temp = x;
    x = y;
    y = temp;
}

int main()
{
    int a = 10;
    int b = 20;

    Swap(a, b);

    std::cout << a << " " << b << std::endl;

    return 0;
}

输出结果:

复制代码
20 10

如果使用普通值传递,函数拿到的是参数的副本,交换副本不会直接改变外部变量。引用传参则可以直接操作原对象。

8.4 const 引用:只读访问与临时对象

当函数只需要读取一个对象、不需要修改它时,可以考虑使用 const 引用。

复制代码
#include <iostream>

void Print(const int& value)
{
    std::cout << value << std::endl;
}

int main()
{
    int a = 10;

    Print(a);
    Print(100);

    return 0;
}

这里的 const int& 有两个重要作用:

  • 可以引用普通的 int 对象,但通过这个引用不能修改对象。
  • 可以绑定到合适的临时对象,例如示例中的整数常量 100。

再看一个权限控制的例子:

复制代码
const int a = 10;

// 正确:只读引用
const int& ref = a;

// 错误:不能通过非 const 引用修改 const 对象
// int& ref2 = a;

理解 const 引用的关键是:允许缩小访问权限,但不能通过引用扩大原对象允许的访问权限。

8.5 引用返回值

引用还可以作为函数返回类型,让调用者通过函数返回值直接访问原对象。

例如:

复制代码
int& GetElement(int* array, int index)
{
    return array[index];
}

如果 array 指向的数组仍然有效,那么返回数组元素的引用后,可以通过它修改对应元素:

复制代码
int a[] = {10, 20, 30};

GetElement(a, 1) = 99;

执行后,数组第二个元素变为 99。

不过,返回引用时必须确保被引用对象的生命周期足够长。不要返回局部变量的引用,因为局部变量在函数结束后通常就不再有效。

九、指针与引用的区别

指针和引用在使用上有一定相似性,但概念并不相同。

对比维度 指针 引用
基本概念 保存对象的地址 已有对象的别名
定义时初始化 语法上不强制,但建议初始化 必须初始化
是否可以改变指向 可以改变指向 不能重新绑定
访问对象 通常通过解引用 * 可以直接访问
空值表达 可以表示为空指针 普通引用必须绑定有效对象
使用风险 需要注意空指针、悬空指针等问题 使用更简洁,但同样要注意对象生命周期

例如:

复制代码
int a = 10;

int* ptr = &a;
int& ref = a;

*ptr = 20;
ref = 30;

执行后,a 的值为 30。

需要注意,引用并不意味着绝对安全。如果引用绑定的对象已经失效,继续访问它仍然可能导致未定义行为。

十、inline 内联函数

10.1 什么是内联函数?

在 C++ 中,可以使用 inline 关键字声明内联函数。

复制代码
inline int Add(int a, int b)
{
    return a + b;
}

调用:

复制代码
int result = Add(10, 20);

传统意义上,内联函数允许编译器考虑将函数体展开到调用位置,以减少部分普通函数调用的开销。

不过,inline 只是向编译器表达一种意图,不保证编译器一定进行内联展开。实际优化取决于编译器、编译选项和函数本身等因素。

10.2 为什么需要内联函数?

与直接使用宏定义相比,内联函数具有更清晰的函数语法和类型检查能力。

例如,下面是一个简单的宏:

复制代码
#define ADD(a, b) ((a) + (b))

它可以完成基本的加法,但宏属于预处理阶段的文本替换,复杂时容易出现运算优先级和重复求值等问题。

使用普通函数或合适的内联函数,通常更容易理解、调试和维护。

10.3 使用 inline 时需要注意什么?

  1. inline 并不保证一定提升性能。
  2. 代码量较大或结构复杂的函数,不一定适合内联。
  3. 现代编译器会根据优化情况决定是否展开函数。
  4. 内联函数的定义通常应放在头文件中,便于需要它的源文件看到完整定义。

这里需要特别区分:inline 不只是性能提示,它还会影响函数定义在多个翻译单元中出现时的链接规则。因此,在多文件项目中,应正确组织内联函数的声明和定义。

十一、nullptr:C++11 引入的空指针字面量

11.1 为什么需要 nullptr?

在传统 C/C++ 代码中,NULL 经常被用于表示空指针。但在 C++ 中,NULL 可能被定义为整数常量 0,从而在函数重载时引发歧义。

例如:

复制代码
#include <iostream>

void Func(int)
{
    std::cout << "整数版本" << std::endl;
}

void Func(int*)
{
    std::cout << "指针版本" << std::endl;
}

int main()
{
    Func(0);
    Func(nullptr);

    return 0;
}

输出结果:

复制代码
整数版本
指针版本

Func(0) 匹配整数参数版本,而 Func(nullptr) 匹配指针参数版本。

11.2 nullptr 的优势

nullptr 是 C++11 引入的专用空指针字面量,可以转换为指针类型,但不会像整数 0 那样被当作普通整数实参进行重载匹配。

因此,推荐使用:

复制代码
int* ptr = nullptr;

而不是依赖整数 0 来表示空指针。

需要注意,nullptr 并不是一个普通的指针变量,而是具有专门类型 std::nullptr_t 的字面量。

十二、C++ 入门知识点总结

学完这部分内容后,可以用下面的知识结构来复习。

C++ 入门基础

建立语言基础与代码组织能力

程序基础

main、编译、输入输出

代码组织

namespace、作用域

函数特性

缺省参数、函数重载

对象访问

引用、指针、const

编译优化

inline 与函数调用

安全表达

nullptr、生命周期

建议重点掌握以下内容:

  • 能够独立编写、编译和运行 C++ 程序。
  • 理解命名空间的作用,知道如何使用 std:: 和 ::。
  • 掌握 cin、cout 等基本输入输出方式。
  • 理解缺省参数和函数重载的匹配规则。
  • 熟练区分引用与指针,理解引用传参和 const 引用。
  • 了解内联函数的作用及其局限。
  • 理解 nullptr 为什么比使用整数 0 表示空指针更明确。

十三、学习建议:从看懂代码到独立编程

对于刚开始学习 C++ 的同学,建议按照以下步骤练习。

  1. 先运行,再修改。 把课件中的每个示例输入开发环境,观察输出结果,再尝试修改变量、参数和函数名称。
  2. 针对每个知识点编写小程序。 例如,使用命名空间定义两个同名函数,使用引用实现交换,使用函数重载实现整数与浮点数加法。
  3. 主动制造错误。 尝试删除引用初始化、创建有歧义的重载,或者错误地使用 NULL,通过编译器报错理解语法规则。
  4. 整理个人笔记。 每个知识点记录定义、核心语法、代码示例、易错点和实际应用场景。
  5. 逐步结合数据结构。 学习栈、队列、顺序表和链表时,思考引用、指针与命名空间如何帮助组织代码。

结语

C++ 入门阶段的重点,并不是记住多少语法,而是建立正确的编程思维。

从第一个 Hello World 程序开始,到理解命名空间、函数重载、引用、内联函数和 nullptr,这些知识共同构成了进一步学习 C++ 的基础。

当你能够解释代码为什么这样写、预测程序的运行结果,并且独立修改和编写示例时,就真正迈出了从语法学习走向实际开发的重要一步。

后续可以继续学习类与对象、构造函数、拷贝控制、继承、多态,以及 STL 容器与算法,逐渐形成完整的 C++ 知识体系。


结尾

uu们,本文的内容到这里就全部结束了,道尔在这里再次感谢您的阅读!

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ### 道尔柯南 C/C++ & Linux 底层探索者 | 一个正在努力学习的技术博主 *** ** * ** *** 👀 【关注】 跟随我一起深耕技术领域,见证每一次成长。 ❤️ 【点赞】 让优质内容被更多人看见,让知识传递更有力量。 ⭐ 【收藏】 把核心知识点存好,在需要时随时查、随时用。 💬 【评论】 分享你的经验或疑问,评论区一起交流避坑! 不要忘记给博主"一键四连"哦! "今日目标达成!" |

结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!

相关推荐
知识分享小能手1 小时前
C学习教程,从入门到精通,C语言输入和输出(4)
c语言·开发语言·学习
盛世宏博智慧档案1 小时前
Modbus TCP+PoE以太网温湿度传感器项目部署全流程,附网络调试实操
开发语言·tcp/ip·php·以太网·传感器·温湿度
bkspiderx1 小时前
在 MFC 中添加自定义消息:从定义到使用的完整指南
c++·mfc·postmessage·sendmessage·mfc 中添加自定义消息·wm_app·on_message
ACP广源盛139246256731 小时前
Type-C 扩展坞方案选型笔记|国产 DP1.2 转 HDMI2.0 桥接芯片 GSV2201D@ACP 评估
开发语言·笔记·硬件架构·硬件工程·hdmi·国产芯片
纪念 2291 小时前
C++ string(三)
android·c++
纪念 2291 小时前
c++ string(最终篇)
android·c++
yuniko-n1 小时前
【JUC】线程间通信
java·开发语言
yuniko-n2 小时前
【JUC】关于 Java 线程介绍
java·开发语言
geovindu2 小时前
rust: Borg Pattern(续)
开发语言·后端·设计模式·rust·博格模式