从C到CPP过渡,你需要了解这些知识

目录

1.内存划分

2.namespace的引入

3.namespace的定义

4.namespace的使用

二、CPP的输入与输出

三、缺省参数(默认参数)

1.定义

2.使用规则 

四、函数重载 

五、引用&

1.定义

2.const修饰引用

​3.指针和引用的关系

六、内联函数

七、 nullptr


1.内存划分

  1. 栈区(Stack)

    由编译器自动管理,采用LIFO(last in first out)结构存储函数调用信息:

    • 存储局部变量、函数参数、返回地址
    • 分配/回收通过移动栈指针实现
    • 典型大小1-8MB,超过会导致栈溢出(如递归过深时)
    • 特点:存取效率高,空间固定不可扩展
  2. 堆区(Heap)

    动态内存管理区域:

    • 通过malloc/new手动申请,free/delete释放
    • 空间仅受物理内存限制(理论上可达数GB)
    • 容易产生内存泄漏(未释放)和碎片问题
  3. 全局/静态存储区

    包含两个子区域:

    • .data段:已初始化的全局/静态变量
    • .bss段:未初始化的全局/静态变量(程序加载时清零)
    • 生命周期与程序周期相同
  4. 常量存储区

    存放不可修改数据:

    • 字符串常量(如"hello")
    • const修饰的全局常量
    • 试图修改会触发段错误(如char* ptr = "abc"; ptr[0]='d')
  5. 代码区(Text Segment)

    存储可执行指令:

    • 包含编译后的机器码
    • 具有只读属性,防止意外修改
    • 可能包含编译器优化的内联代码
  6. 内存映射区(Memory Mapping)

    操作系统级管理区域:

    • 用于加载动态链接库
    • 实现文件映射I/O(mmap系统调用)
    • 用户可创建匿名映射区

2.namespace的引入

看段代码:

加了这个头文件,为什么报错了?理论上全局变量和局部变量可以同名,同名就遵循局部优先原则,但是rand是stdlib库里的函数名,我们命名与这个函数同名,引发错误

而namespace的价值就体现于此:

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

3.namespace的定义

  • 命名空间是一个声明性区域,为其内部的标识符(类型、函数和变量等的名称)提供一个范围
  • namespace本质是定义出一个域,这个域跟全局域各自独立,不同的域可以定义同名变量,所以下面的rand不在冲突了
  • namespace只能定义在全局,并且可以嵌套定义
  • 项目工程中多文件中定义的同名namespace会认为是同一个namespace,不会冲突
  • C++标准库都放在⼀个叫std(standard)的命名空间中
//定义
namespace name{
// fuction type variable
}

// 例如
namespace vect{

	int rand = 10;
	double d = 1.1;

	struct ListNode {
		int val;
		struct ListNode* next;
	};

	void Swap(int* pa, int* pb);
}

int main() {

	// 默认访问函数指针的地址
	printf("%p\\n", rand);
	// 访问vect中的rand
	printf("%d\\n", vect::rand);

	return 0;
}

// 多文件命名空间同名,默认就是同一个命名空间
#include "head1.h"
#include "head2.h"

// test.cpp
int main() {
	MyLib::funcA();
	MyLib::funcB();
	return 0;
}
// head1.h
namespace MyLib {
    void funcA() { cout << "Function A\\n"; }
}

// head2.h 
namespace MyLib {  // 合并到同一个命名空间
    void funcB() { cout << "Function B\\n"; }
}

输出结果:

4.namespace的使用

编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。

我们可以将局部域想象成自己家的菜地,全局域是野地,而命名空间则是别人家的菜地

我们只能采摘自己家的菜地或者人人都可采摘的野地,别人家的菜地未经允许是不可采摘的

我们要使⽤命名空间中定义的变量/函数,有三种方式:

  • 指定命名空间访问,项目中推荐这种方式
  • using将命名空间中某个成员展开,项目中经常访问的不存在冲突的成员推荐这种方式
  • 展开命名空间中全部成员,项目不推荐,冲突风险很大,OJ练习中为了方便通常全部展开

这里引入域作用限定符“::

“::”的左边为空,默认搜查全局域

// 指定特定成员访问
namespace bit {
	int val = 1;
}

int val = 10;

void fuc() {
	int val = 11;
	// 访问函数内部变量
	printf("%d\n", val);
	// 访问全局变量
	printf("%d\n", ::val);
}
int main() {

	int val = 9;
	// 访问局部变量
	printf("%d\n", val);
	// 访问全局变量
	printf("%d\n", ::val);
	// 访问命名空间变量
	printf("%d\n", bit::val);
	printf("\n");
	fuc();
	return 0;
}

输出结果:

// using将命名空间部分成员展开访问
namespace bit {
	int val = 1;
	int num = 30;
}
using bit::num;

int main() {

	printf("%d\n", num);
	printf("%d\n", bit::val);
	return 0;
}

输出结果:

// 展开命名空间全部成员
namespace bit {
	int val = 1;
	int num = 30;
}
using namespace bit;

int main() {

	printf("%d\n", num);
	printf("%d\n", val);
	return 0;
}

输出结果:

总结一下,现在的访问顺序:

  1. 当前局部域                          // 自家菜地
  2. 全局域                                 // 野地
  3. 命名空间展开域                   // 别人菜地但公告允许他人采摘了

二、CPP的输入与输出

  • iostreamInputOutputStream的缩写,是标准的输入、输出流库,定义了标准的输入、输出对象
  • std::cin istream类的对象,它主要面向窄字符(narrow characters)的标准输入流
  • std::coutostream类的对象,它主要面向窄字符的标准输出流
  • std::endl 是一个函数,流插入输出时,相当于插入⼀个换行字符加刷新缓冲区
  • <<是流输出符,>>是流提取符 
  • 使用CPP输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动动指定格式,CPP的输入输出可以自动识别变量类型
  • cout/cin/endl等都属于CPP标准库,CPP标准库都放在⼀个叫std(standard)的命名空间中,所以要通过命名空间的使用方式去用他们
  • 一般日常练习中我们可以using namespace std,但实际项目开发中不建议

第一个CPP程序:

// 第一个CPP程序 
#include <iostream> // io流
using namespace std;// 展开std标准库

int main() {
	cout << "HELLO,CS!" << endl; // << 输出符 可自动匹配类型
	return 0;
}

// 输入几个数据并打印

int main() {
	int a = 0;
	double b = 0;
	char c = 'm';
	string s = "hello";

// 可以自动识别类型
	cin >> a;
	cin >> b;
	cin >> c >> s;

	cout << a << "\n" << b << "\n" << c << "  " << s;

	return 0;

}

 运行结果:

三、缺省参数(默认参数)

1.定义

在函数参数设计中,给形参赋一个值,这个值就称为缺省参数(默认参数),在函数调用时,如果实参不传参数,则使用形参的默认参数

例如:

// 缺省参数
void fuc(int a = 1) {
	cout << a << endl;
}

int main() {
	fuc(); // 实参未传递实际的值 缺省参数 默认a == 1
	fuc(9);// 实参传递实际的值9 非缺省参数 a == 9

	return 0;
}

 输出结果:

2.使用规则 

  • 缺省参数分为全缺省和半缺省参数,全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值,半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值
  • 带缺省参数的函数调用,必须从左到右依次给实参,不能跳跃给实参
  • 函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值

例如:

// 全缺省
void display1(int x = 9, string msg = "Hello", double ratio = 1.5) {
    cout << "x=" << x
         << ", msg=" << msg
         << ", ratio=" << ratio << endl;
}
// 半缺省 定义缺省参数 从右往左定义 不可以跳跃
void display2(int x, string msg, double ratio = 1.5) {
    cout << "x=" << x
        << ", msg=" << msg
        << ", ratio=" << ratio << endl;
}
int main() {
    // 传缺省参数 从左往右
    display1();
    display1(99);
    display1(99,"cs");
    display1(99,"cs",8.9);


    display2(1,"hahahaha");
    display2(1,"hahahaha",8.2);

    return 0;

}

输出结果:

 函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值:

// 头文件声明
void init(int timeout = 1000);

// 源文件定义(错误示例)
// void init(int timeout = 1000) { ... } 

// 缺省参数必须在函数声明时给

 报错了

将定义中的缺省参数删除:

函数定义不接受缺省参数 

将定义中的缺省参数删除后:

没有重定义缺省参数的错误了,我在主函数中没有初始化变量,这里忽略,只是演示缺省参数在函数定义中不能用,只能在函数声明中使用缺省参数

四、函数重载 

函数重载:在CPP中,支持函数定义重名,但是要保证形参的类型、顺序或者个数不同

  • 函数形参的名字不能作为判定条件
  • 函数的返回值不能作为判定条件

代码示例:

#include <iostream>
using namespace std;
// 函数重载:在CPP中,支持函数定义重名,但是要保证形参的类型、顺序或者个数不同
// 函数形参的名字不能作为判定条件
// 函数的返回值不能作为判定条件,调用时编译器也无法识别到底是哪个函数

// 类型不同
int Add(int a, int b) {
	return a + b;
	cout << "Add(int a, int b)" << endl;
}
double Add(double a, double b) {
	cout << "Add(double a, double b)" << endl;
	return a + b;
}

// 顺序不同
void fuc(int x, double y) {
	cout << "fuc(int x, double y)" << endl;
}
void fuc(double y, int x) {
	cout << "fuc(double y, int x)" << endl;

}

// 个数不同
void Print(char c, int x) {
	cout << "Print(char c, int x)" << endl;
}
void Print(char c) {
	cout << "Print(char c)" << endl;
}

// 是函数重载,但是编译器不知道匹配谁 存在歧义
void f() {
	cout << "f()" << endl;
}
void f(int a = 2) {
	cout << "f(int a)" << endl;
}
int main() {
	Add(6, 7);
	Add(1.3, 8.1);

	fuc(7, 8.6);
	fuc(8.6, 7);

	Print('a',8);
	Print('a');

	f();
	f(1);

	return 0;

}

为什么C不支持函数重载而CPP支持?

 CPP编译链接时会对函数名进行编码,将参数类型信息融入最终符号名,会将函数改名修饰

C语言编译链接时不会将函数改名修饰

五、引用&

1.定义

为了简化指针的操作,cpp引入引用(&)

引用是给变量起一个别名,比如说你自己,有自己的大名,小名,还有朋友对你的称呼,每个名字都代表你,因此,引用在语法上不开辟新的内存空间

引用有如下规则:

  • 引用在定义时必须初始化
  • 一个变量可以有多个引用
  • 引用一旦引用一个实体,再不能引用其他实体

代码示例:

// &引用

// 相当于给变量起别
int main() {
	int a = 0;
	int& b = a; 
	int& c = a;
	int& d = c;

	d++;
	cout << "&a" << endl;
	cout << "&b" << endl;
	cout << "&c" << endl;
	cout << "&d" << endl;

	return 0;

 

  • 引用一旦引用一个实体,再不能引用其他实体:
int main() {
	int a = 0;
	int& b = a; 
	int& c = a;
	int& d = c;

	d++;

	//指针变量起别名
	int* pa = &a;
	int*& pb = pa;
	pb = NULL;

	//引用一旦引用一个实体,再不能引用其他实体
	int x = 7;
	int& y = x;
	int z = 0;
	// 把z的值拷贝给y
	y = z;
	cout << z << " " << x << " " << y;
	return 0;
}

2.const修饰引用

权限只能平移和缩小,但不能放大!!!!!!!!

类型转换和表达式运算都会产生临时变量 总得有个值来存结果

代码示例:

// const修饰引用
int main() {

	const int a = 10;
	// 权限放大,不可以 a只读 而b是可修改的
	/*int& b = a;*/

	// 权限平移
	const int& b = a;

	int x = 5;
	//权限缩小 可以
	const int& y = x;

	int num = 45;
	const int* pnum = &num;
	// 权限平移
	const int*& pr = pnum;
	// 权限放大 不可以
	/*int*& pq = pnum;*/

	int* qnum = &num;
	// 权限平移
	int*& anum = qnum;
	// 权限缩小 但是引用的 int* 与const int* 二者类型不匹配 不可以
	/*const int*& bnum = qnum;*/
	// CPP允许通过隐式转换创建新指针实现权限缩小
	const int* bnum = qnum;

	// 类型转换和表达式运算都会产生临时变量 总得有个值来存结果

	double val1 = 1.2, val2 = 2.4;

	const double& sum = val1 + val2;

	int num1 = 5;
	float f = num1;

	const float& ff = f;

}

 3.指针和引用的关系

  • 在语法上指针需要开辟新的空间,引用给变量起别名无需开辟空间

  • 引用必须初始化,指针建议初始化(防止野指针),但不是必须初始化
  • 引用在初始化引用一个对象之后,就不能引用其他对象了,指针存储一个变量的地址,可以改变指向,指向其他变量
  • 引用可以直接访问引用对象,指针需要解引用才能访问目标对象
  • sizeof中含义不同,引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节,64位下是8字节)

六、内联函数

常规的函数调用:

压栈函数参数->跳转到函数体->执行函数体->返回结果->弹栈。这一系列步骤增加了函数调用的开销,尤其是在小函数中,这种开销可能比函数体本身还要大

所以,在需要频繁调用小函数时,CPP引进了inline,将函数修饰为内联函数,用于提示编译器在函数调用时将函数体直接嵌入到调用点展开,而不是通过常规的函数调用机制调用函数,来提高程序运行效率

注意:

这里的inlin只是建议,编译器也可以忽略

inline适用于频繁调用的短小函数,对于递归函数,代码相对多⼀些的函数,加上inline也会被编译器忽略

使用内联函数还需要注意两点:

  • 内联函数的声明与定义不能多文件分开,建议在同一文件下定义和声明,因为内联函数是将函数体直接展开,并没有保存地址,如果在多文件体系下,链接时找不到函数定义的地址,这个函数就失效了
  • vs编译器下,dbug版本默认不展开内联函数,这里需要调整两个地方:

七、 nullptr

NULL实际是⼀个宏

 

CPP中用关键字nullptr代表空指针

完结撒花~

看到这里,求一个点赞支持~

希望这篇文章对大家CPP过渡有所帮助,如有错误,欢迎评论区指正 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vect.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值