跳到主要内容
C++ 核心基础:从程序结构到内存模型 | 极客日志
C++ 算法
C++ 核心基础:从程序结构到内存模型 C++ 基础涵盖命名空间、输入输出流、缺省参数、函数重载、引用、内联函数及 nullptr 关键字等核心概念。通过代码示例解析了各特性的定义、使用规则及注意事项,重点对比了指针与引用的区别,强调了 nullptr 在类型安全上的优势,并提供了优化 IO 效率的建议,适合初学者快速建立 C++ 知识框架。
GitMaster 发布于 2026/3/28 更新于 2026/4/25 2 浏览C++ 入门基础
1. C++ 的第一个程序
#include <iostream>
using namespace std;
int main () {
cout << "Hello World" << endl;
return 0 ;
}
2. 命名空间
2.1 namespace 的价值
在 C/C++ 中,变量、函数和类的名称都存在于全局作用域中,数量庞大时极易产生冲突。命名空间的目的是对标识符进行本地化,避免命名冲突。namespace 关键字正是为了解决这一问题而设计的。
2.2 namespace 的定义
定义命名空间需使用 namespace 关键字,后跟名字及一对 {},成员包括变量、函数、类型等。
命名空间本质是定义一个独立的域,与全局域隔离。不同域可定义同名变量而不冲突。
C++ 中的域包括函数局部域、全局域、命名空间域和类域。域主要影响编译时的语法查找逻辑,从而解决名字冲突。注意,局部域和全局域会影响变量生命周期,而命名空间域和类域通常不影响。
命名空间只能定义在全局,支持嵌套定义。
多文件中定义的同名命名空间会被视为同一个,不会冲突。
C++ 标准库都放在 std (standard) 命名空间中。
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
namespace abc {
int rand = 10 ;
int Add (int left, int right) {
return left + right;
}
struct Node {
struct Node * next;
val;
};
}
{
( , rand);
( , abc::rand);
;
}
int
int main ()
printf
"%p\n"
printf
"%d\n"
return
0
namespace asd {
namespace aaa {
int rand = 15 ;
}
namespace bbb {
int rand = 20 ;
}
}
int main () {
printf ("%d\n" , asd::aaa::rand);
printf ("%d\n" , asd::bbb::rand);
return 0 ;
}
2.3 命名空间使用 编译查找变量声明/定义时,默认只在局部或全局查找,不会自动进入命名空间。因此直接使用命名空间内的成员会报错。有三种使用方式:
指定命名空间 :项目中推荐这种方式,清晰明确。
using 展开某个成员 :针对经常访问且无冲突的成员。
展开整个命名空间 :项目不推荐,冲突风险大,日常练习为了方便可使用。
namespace N {
int a = 0 ;
int b = 0 ;
}
int main () {
printf ("%d\n" , N::a);
return 0 ;
}
using N::b;
int main () {
printf ("%d\n" , N::a);
printf ("%d\n" , b);
return 0 ;
}
using namespace N;
int main () {
printf ("%d\n" , a);
printf ("%d\n" , b);
return 0 ;
}
3. C++ 输入 & 输出
IO 是 Input Output Stream 的缩写,定义了标准的输入、输出流对象。
std::cin 是 istream 类的对象,面向窄字符的标准输入流。
std::cout 是 ostream 类的对象,面向窄字符的标准输出流。
std::endl 是一个函数,流插入输出时相当于换行符加刷新缓冲区。
<< 是流插入运算符,>> 是流提取运算符。
C++ 输入输出不需要像 printf/scanf 那样指定格式,可以自动识别变量类型。
cout/cin/endl 等属于 C++ 标准库,位于 std 命名空间中,使用时需注意命名空间处理。
一般练习中可以使用 using namespace std,实际项目开发中不建议。
某些编译器(如 VS)包含 <iostream> 可能间接包含 <stdio.h>,但其他编译器可能报错,建议显式包含。
#include <iostream>
#include <stdio.h>
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;
scanf ("%d%lf" , &a, &b);
printf ("%d %lf\n" , a, b);
return 0 ;
}
提示:在 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 ;
}
#include <iostream>
using namespace std;
void Func1 (int a = 10 , int b = 20 , int c = 30 ) {
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl << endl;
}
void Func2 (int a, int b = 10 , int c = 20 ) {
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl << endl;
}
int main () {
Func1 ();
Func1 (1 );
Func1 (1 , 2 );
Func1 (1 , 2 , 3 );
Func2 (100 );
Func2 (100 , 200 );
Func2 (100 , 200 , 300 );
return 0 ;
}
5. 函数重载 C++ 支持在同一作用域中出现同名函数,要求这些同名函数的形参不同(个数或类型)。这体现了多态行为,使调用更灵活。C 语言不支持同一作用域中出现同名函数。
#include <iostream>
using namespace std;
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;
}
void f () {
cout << "f()" << endl;
}
void f (int a) {
cout << "f(int a)" << endl;
}
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;
}
void f1 () {
cout << "f()" << endl;
}
void f1 (int a = 10 ) {
cout << "f(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 ;
int & b = a;
int & c = 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 = a;
int c = 20 ;
b = c;
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
return 0 ;
}
6.3 引用的使用
引用在实践中主要用于引用传参和引用做返回值,以减少拷贝提高效率,或在改变引用对象时同时改变被引用对象。
引用传参跟指针传参功能类似,但引用传参相对更方便一些。
引用和指针在实践中相辅相成,各有特点,互相不可替代。C++ 的引用跟其他语言的引用有很大区别,除了用法,最大的点是 C++ 引用定义后不能改变指向。
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 ;
}
注:指针变量也可以取别名,例如 LTNode*& phead 就是给指针变量取别名,这样就不需要用二级指针了,相对而言简化了程序。
6.4 const 引用
可以引用一个 const 对象,但必须用 const 引用。const 引用也可以引用普通对象,因为对象的访问权限在引用过程中可以缩小,不能放大。
注意类似 int& rb = a * 3; 或 int& rd = d; 的场景,表达式结果保存在临时对象中,临时对象具有常性,所以必须用常引用。
所谓临时对象就是编译器暂存表达式求值结果创建的未命名对象。
#include <iostream>
using namespace std;
int main () {
const int a = 10 ;
const int & ra = a;
int b = 20 ;
const int & rb = b;
return 0 ;
}
int main () {
int a = 10 ;
const int & ra = 30 ;
const int & rb = a * 3 ;
double d = 12.34 ;
const int & rd = d;
return 0 ;
}
6.5 指针和引用的关系 C++ 中指针和引用就像两个性格迥异的兄弟,指针是哥哥,引用是弟弟。实践中他们相辅相成,功能有重叠性,但各自有特点,互相不可替代。
语法概念 :引用是一个变量的取别名不开空间,指针是存储一个变量地址,要开空间。
初始化 :引用在定义时必须初始化,指针建议初始化,但语法上不是必须的。
指向改变 :引用在初始化时引用一个对象后,就不能再引用其他对象;而指针可以在不断地改变指向对象。
访问方式 :引用可以直接访问指向对象,指针需要解引用才是访问指向对象。
sizeof :含义不同,引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32 位平台下 4byte,64 位下 8byte)。
安全性 :指针很容易出现空指针和野指针的问题,引用很少出现,引用使用起来相对更安全一些。
7. inline
inline 修饰的函数叫做内联函数。编译时 C++ 编译器会在调用的地方展开内联函数,这样调用内联函数就不需要建立栈帧了,可以提高效率。
inline 对于编译器而言只是一个建议。加了 inline 编译器也可以选择在调用的地方不展开。不同编译器关于 inline 什么情况展开各不相同,因为 C++ 标准没有规定这个。
inline 适用于频繁调用的短小函数。对于递归函数、代码相对多一些的函数,加上 inline 也会被编译器忽略。
C 语言实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂容易出错,且不方便调试。C++ 设计了 inline 目的就是替代 C 的宏函数。
VS 编译器 debug 版本下面默认是不展开 inline 的,这样方便调试。debug 版本想展开需要设置一下相关选项。
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 ;
}
#include <iostream>
using namespace std;
#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 ;
}
#include <iostream>
using namespace std;
inline void f (int i) ;
#include "F.h"
void f (int i) {
cout << i << endl;
}
#include "F.h"
int main () {
f (10 );
f (10 );
return 0 ;
}
8. nullptr C++ 中 NULL 可能被定义为字面常量 0,C 语言中为 (void*)。
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 );
f (nullptr );
void * p1 = nullptr ;
int * p2 = p1;
return 0 ;
}
相关免费在线工具 加密/解密文本 使用加密算法(如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