跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 入门基础知识详解

介绍 C++ 入门核心概念,包括第一个程序编写、命名空间的作用与定义、标准输入输出流的使用、缺省参数规则、函数重载机制、引用的概念与特性、内联函数的优化原理以及 nullptr 空指针的安全使用。通过代码示例讲解了 namespace 隔离冲突、引用传参效率提升及 const 引用限制等内容,帮助初学者建立 C++ 基础语法认知。

云朵棉花糖发布于 2026/3/25更新于 2026/10/2694 浏览

1. C++ 的第一个程序

C++ 兼容 C 语言绝大多数的语法,所以 C 语言实现的 hello world 依旧可以运行。C++ 中需要将定义文件代码后缀改为 .cpp,编译器看到是.cpp 就会调用 C++ 编译器编译。

// C 语言版本
#include <stdio.h>
int main() {
    printf("hello world\n");
    return 0;
}
// C++ 版本
#include <iostream>
using namespace std;
int main() {
    cout << "hello world\n" << endl;
    return 0;
}

2. 命名空间

2.1 Namespace 的价值

在 C/C++ 中,变量、函数和类的名称都存在于全局作用域中,可能会导致冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染。namespace关键字的出现就是针对这种问题的。

在 C 语言项目中,类似下面程序这样的命名冲突是普遍存在的问题:

#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main() {
    // 编译报错:error C2365: 'rand': 重定义;以前的定义是'函数'
    printf("%d\n", rand);
    return 0;
}

2.2 Namespace 的定义

定义命名空间需要使用 namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。命名空间中可以定义变量/函数/类型等。

namespace 本质是定义出一个域,这个域跟全局域各自独立,不同的域可以定义同名变量。C++ 中域有函数局部域、全局域、命名空间域、类域。所有有了域隔离,名字冲突就解决了。

以下代码展示了 namespace 的一些定义的使用:

#include <stdio.h>
#include <stdlib.h>

// 1. 正常的命名空间定义
namespace bit {
    int rand = 10;
    int Add(int left, int right) {
        return left + right;
    }
    struct Node {
        struct Node* next;
        int val;
    };
}

int main() {
    // 这里指定 bit 命名空间中的 rand
    printf("%d\n", bit::rand);
    return 0;
}

// 2. 命名空间可以嵌套
namespace bit {
    namespace pg {
        int rand = 1;
        int Add(int left, int right) {
            return left + right;
        }
    }
    namespace hg {
        int rand = 2;
        int Add(int left, int right) {
            return (left + right)*10;
        }
    }
}

int main() {
    printf("%d\n", bit::pg::rand);
    printf("%d\n", bit::hg::rand);
    printf("%d\n", bit::pg::Add(1, 2));
    printf("%d\n", bit::hg::Add(1, 2));
    return 0;
}

// 多文件中定义的同名 namespace 会认为是同一个 namespace,会自动合并
// Stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
namespace bit {
    typedef int STDataType;
    typedef struct Stack {
        STDataType* a;
        int top;
        int capacity;
    }ST;
    void STInit(ST* ps, int n);
    void STPush(ST* ps, STDataType x);
}

// Stack.cpp
#include"Stack.h"
namespace bit {
    void STInit(ST* ps, int n) {
        assert(ps);
        ps->a = (STDataType*)malloc(n * sizeof(STDataType));
        ps->top = 0;
        ps->capacity = n;
    }
    void STPush(ST* ps, STDataType x) {
        assert(ps);
        if (ps->top == ps->capacity) {
            int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
            STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
            if (tmp == NULL) { perror("realloc fail"); return; }
            ps->a = tmp;
            ps->capacity = newcapacity;
        }
        ps->a[ps->top] = x;
        ps->top++;
    }
}

// test.cpp
#include"Queue.h"
#include"Stack.h"

int main() {
    // 调用 bit namespace 的
    bit::ST st2;
    bit::STInit(&st2);
    bit::STPush(&st2, 1);
    return 0;
}

2.3 命名空间的使用

编译查找一个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。使用命名空间中的变量/函数可以使用以下三种方式:

  • 指定命名空间访问(推荐)。
  • using 将命名空间中的某个成员展开(推荐用于无冲突成员)。
  • 展开命名空间中的全部成员(不推荐,易产生冲突)。
#include<stdio.h>
namespace N {
    int a = 0;
    int b = 1;
}

int main() {
    // 指定命名空间访问
    printf("%d\n", N::a);

    // using 将命名空间中某个成员展开
    using N::b;
    printf("%d\n", b);

    // 展开命名空间中全部成员
    using namespace N;
    printf("%d\n", a);
    return 0;
}

3. C++ 输入&输出

<iostream>是 Input Output Stream 的缩写,定义了标准的输入、输出对象。std::cin是 istream 类的对象,std::cout是 ostream 类的对象。<<是流插入运算符,>>是流提取运算符。

C++ 的输入输出可以自动识别变量类型(本质是通过函数重载实现),IO 流涉及类和对象、运算符重载等面向对象的知识。 C++ 标准库都放在一个叫 std(standard) 的命名空间中,一般日常练习中可以使用 using namespace std,但在实际项目开发中不建议这样使用。

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;

int main() {
    int a = 0;
    double b = 0.1;
    char c = 'x';
    
    cout << a << " " << b << " " << c << endl;
    std::cout << a << " " << b << " " << c << std::endl;
    
    cin >> a;
    cin >> b >> c;
    cout << a << endl;
    cout << b << " " << c << endl;
    return 0;
}

// 在 IO 需求比较高的地方,如部分大量输入的竞赛题中,加上以下 3 行代码可以提高 C++IO 效率
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);

4. 缺省参数

缺省参数是声明或定义函数时为函数的参数指定一个缺省值。分为全缺省或半缺省参数。C++ 规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。带缺省参数的函数调用,必须从左向右依次给实参,不能跳跃给实参。函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须在函数声明给缺省值。

#include <iostream>
#include <assert.h>
using namespace std;

void Func(int a = 0) {
    cout << a << endl;
}

int main() {
    Func();       // 没有传参时,使用参数的默认值
    Func(10);     // 传参时,使用指定的实参
    return 0;
}

// 全缺省
void Func1(int a = 10, int b = 20, int c = 30) {
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl;
}

// 半缺省
void Func2(int a, int b = 10, int c = 20) {
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl;
}

int main() {
    Func1();
    Func1(1);
    Func1(1, 2);
    Func1(1, 2, 3);
    Func2(100);
    Func2(100, 200);
    Func2(100, 200, 300);
    return 0;
}

// Stack.h
#include <iostream>
#include <assert.h>
using namespace std;
typedef int STDataType;
typedef struct Stack {
    STDataType* a;
    int top;
    int capacity;
}ST;
void STInit(ST* ps, int n = 4);

// Stack.cpp
#include"Stack.h"
void STInit(ST* ps, int n) {
    assert(ps && n > 0);
    ps->a = (STDataType*)malloc(n * sizeof(STDataType));
    ps->top = 0;
    ps->capacity = n;
}

// test.cpp
#include"Stack.h"
int main() {
    ST s1;
    STInit(&s1);          // 确定知道要插入 1000 个数据,初始化时一把开好,避免扩容
    ST s2;
    STInit(&s2, 1000);
    return 0;
}

5. 函数重载

C++ 支持在同一作用域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同。返回值不同不能作为重载条件。

#include<iostream>
using namespace std;

// 1、参数类型不同
int Add(int left, int right) {
    cout << "int Add(int left, int right)" << endl;
    return left + right;
}
double Add(double left, double right) {
    cout << "double Add(double left, double right)" << endl;
    return left + right;
}

// 2、参数个数不同
void f() {
    cout << "f()" << endl;
}
void f(int a) {
    cout << "f(int a)" << endl;
}

// 3、参数类型顺序不同
void f(int a, char b) {
    cout << "f(int a,char b)" << endl;
}
void f(char b, int a) {
    cout << "f(char b, int a)" << endl;
}

int main() {
    Add(10, 20);
    Add(10.1, 20.2);
    f();
    f(10);
    f(10, 'a');
    f('a', 10);
    return 0;
}

6. 引用

6.1 引用的概念和定义

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

用法:类型& 引用别名=引用对象;

#include<iostream>
using namespace std;
int main() {
    int a = 0;
    // 引用:b 和 c 是 a 的别名
    int& b = a;
    int& c = a;
    // 也可以给别名 b 取别名,d 相当于还是 a 的别名
    int& d = b;
    ++d;
    cout << &a << endl;
    cout << &b << endl;
    cout << &c << endl;
    cout << &d << endl;
    return 0;
}

6.2 引用的特性

引用在定义时必须进行初始化,即必须指明是对哪个变量进行引用。一个变量可以有多个引用。引用一旦绑定到一个实体,就无法再重新绑定到另一个实体。这与指针不同,指针可以随时指向不同的内存地址。

#include <iostream>
using namespace std;
int main() {
    int a = 10;
    int b = 20;
    int& ref = a; // ref 现在是 a 的别名
    cout << "ref: " << ref << endl; // 输出 10
    ref = b; // 注意:这里并不是让 ref 引用 b,而是将 b 的值赋给 a
    cout << "a: " << a << endl;      // 输出 20
    cout << "ref: " << ref << endl;  // 输出 20
    return 0;
}

6.3 引用的使用

引用在实践中主要是用引用传参和引用做返回值可以减少拷贝,提高效率,而且在改变引用对象时同时还可以改变被引用对象。引用传参和指针传参的功能是类似的,但引用传参相对会更简单一点。

void Swap(int& rx, int& ry) {
    int tmp = rx;
    rx = ry;
    ry = tmp;
}

int main() {
    int x = 0, y = 1;
    cout << x << " " << y << endl;
    Swap(x, y);
    cout << x << " " << y << endl;
    return 0;
}

#include<iostream>
using namespace std;
typedef int STDataType;
typedef struct Stack {
    STDataType* a;
    int top;
    int capacity;
}ST;

void STInit(ST& rs, int n = 4) {
    rs.a = (STDataType*)malloc(n * sizeof(STDataType));
    rs.top = 0;
    rs.capacity = n;
}

void STPush(ST& rs, STDataType x) {
    assert(rs);
    if (rs.top == rs.capacity) {
        int newcapacity = rs.capacity == 0 ? 4 : rs.capacity * 2;
        STDataType* tmp = (STDataType*)realloc(rs.a, newcapacity * sizeof(STDataType));
        if (tmp == NULL) { perror("realloc fail"); return; }
        rs.a = tmp;
        rs.capacity = newcapacity;
    }
    rs.a[rs.top] = x;
    rs.top++;
}

int& STTop(ST& rs) {
    assert(rs.top > 0);
    return rs.a[rs.top];
}

int main() {
    ST st1;
    STInit(st1);
    STPush(st1, 1);
    STPush(st1, 2);
    cout << STTop(st1) << endl;
    STTop(st1) += 10;
    cout << STTop(st1) << endl;
    return 0;
}

#include<iostream>
using namespace std;
typedef struct ListNode {
    int val;
    struct ListNode* next;
}LTNode, *PNode;

// 指针变量也可以取别名,这里 LTNode*& phead 就是给指针变量取别名
// 这样就不需要用二级指针了,相对而言简化了程序
void ListPushBack(PNode& phead, int x) {
    PNode newnode = (PNode)malloc(sizeof(LTNode));
    newnode->val = x;
    newnode->next = NULL;
    if (phead == NULL) {
        phead = newnode;
    } else {
        // ...
    }
}

int main() {
    PNode plist = NULL;
    ListPushBack(plist, 1);
    return 0;
}

6.4 const 引用

可以引用一个 const 对象,但是必须用 const 引用。const 引用也可以引用普通对象,因为对象的访问权限在引用过程中可以缩小,但是不能对访问权限进行放大。

int main() {
    const int a = 10;
    // 这里的引用是对 a 访问权限的放大
    // int& ra = a; // 编译报错
    const int& ra = a; // 正确
    
    int b = 20;
    const int& rb = b; // 正确,访问权限缩小
    // rb++; // 编译报错
    return 0;
}

#include<iostream>
using namespace std;
int main() {
    int a = 10;
    const int& rb = a*3; // 表达式计算结果保存在临时对象中,需 const 引用
    double d = 12.34;
    const int& rd = d;   // 类型转换场景,需 const 引用
    return 0;
}

6.5 引用和指针的关系

C++ 中的指针和引用在实践中相辅相成,功能具有重叠性,但又各自有特点,互相不可替代。

  • 语法概念上引用是一个变量的取别名不开空间,指针是存储一个变量的地址,要开空间。
  • 引用在定义时必须进行初始化,指针建议进行初始化。
  • 引用在初始化后就不能再引用其他对象,而指针可以不断改变指向。
  • 引用可以直接访问指向对象,指针需要解引用。
  • sizeof 含义不同,引用结果为引用类型的大小,指针始终是地址空间所占字节数。
  • 指针容易出现空指针和野指针,引用很少出现,使用更安全。
#include <iostream>
using namespace std;
int main() {
    double x = 3.14;
    double& ref = x; // 引用绑定到 x
    double* ptr = &x; // 指针指向 x
    
    cout << "sizeof(x): " << sizeof(x) << endl; // 8 字节
    cout << "sizeof(ref): " << sizeof(ref) << endl; // 8 字节
    cout << "sizeof(ptr): " << sizeof(ptr) << endl; // 4 或 8 字节
    return 0;
}

7. Inline

用 inline 修饰的函数叫做内联函数,编译时 C++ 编译器会在调用的地方展开内联函数,这样调用内联函数就不需要建立栈帧了,可以提高效率。inline 对于编译器而言只是一个建议。

inline 适用于频繁调用的短小函数,对于递归函数或者代码相对多一些的函数,就算加上 inline 也会被编译器忽略。C++ 设计 inline 目的就是代替 C 语言的宏函数。

inline 不建议声明和定义分离到两个文件,分离会导致链接错误。因为 inline 被展开就没有函数地址,链接时会出现报错。

#include<iostream>
using namespace std;

inline int Add(int x, int y) {
    int ret = x + y;
    ret += 1;
    ret += 1;
    ret += 1;
    return ret;
}

int main() {
    int ret = Add(1, 2);
    cout << Add(1, 2) * 5 << endl;
    return 0;
}

// 正确的宏实现
#define ADD(a, b) ((a) + (b))

int main() {
    int ret = ADD(1, 2);
    cout << ADD(1, 2) << endl;
    cout << ADD(1, 2)*5 << endl;
    int x = 1, y = 2;
    ADD(x & y, x | y);
    return 0;
}

// F.h
#include <iostream>
using namespace std;
inline void f(int i);

// F.cpp
#include "F.h"
void f(int i) {
    cout << i << endl;
}

// main.cpp
#include "F.h"
int main() {
    f(10);
    return 0;
}

8. nullptr

NULL 实际上是一个宏,在传统的 C 语言的头文件中,可以看到如下代码:

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif

C++ 中 NULL 可能被定义为字面常量 0,或者 C 中被定义为无类型指针的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦。C++11 中引入 nullptr,nullptr 是一种特殊类型的字面量,它可以转换成任意其他类型的指针类型。使用 nullptr 定义空指针可以避免类型转换的问题。

#include<iostream>
using namespace std;

void f(int x) {
    cout << "f(int x)" << endl;
}
void f(int* ptr) {
    cout << "f(int* ptr)" << endl;
}

int main() {
    f(0);
    f(NULL);           // 由于 NULL 被定义成 0,调用了 f(int x)
    f((int*)NULL);     // 编译报错
    f(nullptr);        // 正确调用 f(int* ptr)
    return 0;
}

目录

  1. 1. C++ 的第一个程序
  2. 2. 命名空间
  3. 2.1 Namespace 的价值
  4. 2.2 Namespace 的定义
  5. 2.3 命名空间的使用
  6. 3. C++ 输入&输出
  7. 4. 缺省参数
  8. 5. 函数重载
  9. 6. 引用
  10. 6.1 引用的概念和定义
  11. 6.2 引用的特性
  12. 6.3 引用的使用
  13. 6.4 const 引用
  14. 6.5 引用和指针的关系
  15. 7. Inline
  16. 8. nullptr

更多推荐文章

查看全部
  • NVIDIA DGX Spark 部署 Stable Diffusion 3.5 与 ComfyUI
  • Python 金融数据分析:yfinance 库实战指南
  • Python 打造 AI 三剑客:文档总结、代码生成与资料检索
  • JavaScript 中 var、let、const 的核心区别与实战应用
  • 利用 AI 自动生成符合 xxxxxl19d18–19 规范的 Python 项目结构
  • ClaudeCode 与 Figma-MCP 实现前端 UI 代码 1:1 还原指南
  • 开源本地 AI 助手 OpenClaw 安装与配置指南
  • OpenClaw 对接 Stable Diffusion:免费 AI 绘画方案
  • MySQL 到 KingbaseES 数据库迁移最佳实践指南
  • Flutter 三方库 webkit_inspection_protocol 在鸿蒙系统的适配与调试
  • 西门子 S7-1200FC PLC 与松下机器人 Profinet 通信及外部自动控制实战
  • FPGA 与 STM32 的 FMC 通信协议实现
  • C++ Elasticsearch 第三方库 elasticlient 详解
  • C++ 类和对象:默认成员函数详解
  • MCP 协议实战:Browser Tools 插件集成指南
  • VS Code 前端开发 AI 工具对比:Copilot、通义灵码、iFlyCode 与 Trae
  • OpenAI 发布 GPT-5.3 Instant:幻觉率降低 26.8% 及 2026 全球 AI 模型排行
  • MySQL 数据库核心操作详解:创建、修改与备份
  • C++ 二叉排序树原理与实现
  • 安路 FPGA 下载器驱动安装与测试指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online