IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++ 函数学习笔记 -> 正文阅读

[C++知识库]C++ 函数学习笔记

前言:主要是自己学习过程的积累笔记,所以跳跃性比较强,建议先自学后拿来作为复习用。

1 函数基础

典型的函数定义:返回类型 函数名字 圆括号(由0个或多个用逗号隔开形参列表) {函数体},比如:

int function(int parameter1, int parameter2, ...)
{
	...
	return XXX;
}

典型的函数调用:函数名 圆括号(由0个或多个用逗号隔开的实参列表),比如:

int a = function(argument1, argument2, ...);

函数调用完成两项工作:

  • 用实参初始化函数对应的形参;
  • 将控制权转移给被调函数。

此时,主调函数的执行被暂时中断,被调函数开始执行。

函数执行完后,return 语句完成两项工作:

  • 返回 return 语句中的值(如果有的话);
  • 将控制权从被调函数转移回主调函数。

函数的调用规定实参数量应与形参数量一致,且实参的类型必须与形参类型相匹配,所以形参一定会被初始化。函数的返回类型不能是数组或者函数,但可以是指向数组或函数的指针。

在形参和函数体内部定义的变量统称为局部变量,仅在函数的作用域内可用,程序从函数中退出时即被销毁;在所有函数体之外定义的对象称为全局变量,它们在程序启动时创建,在程序结束时销毁。若要令局部变量的生命周期贯穿函数调用及之后的时间(即在程序结束时才被销毁),可以将其定义为局部静态变量。如下所示:

int func()
{
	static int cnt = 0; // 函数调用结束后,这个值仍然有效
	return cnt++;
}

有一点要注意的是,若循环调用上面的函数,cnt 只有在程序第一次调用 func 函数时会被创建并分配空间,在此后的每次调用都不会重复创建,实际生效的语句是 cnt++,也就是说 cnt 的值在循环调用之后会越来越大。但是,在 func 函数之外的其他任何地方都用不了 cnt 变量,仅仅在 func 函数内 cnt 的值是一直有效的。

对于大的工程项目,在头文件中声明函数和变量,在源文件中进行定义是一个比较合理且容易管理项目的方法。

2 参数传递

当形参是引用类型时,我们说它对应的实参被引用传递或者函数被传引用调用,此时,引用形参是实参的别名;当形参为非引用类型时,形参和实参是两个相互独立的对象,此时称实参被值传递或者函数被传值调用

2.1 const 形参和实参

当形参有顶层 const 时,传给它常量对象或者是非常量对象都是可以的。例如:

void function(const int i) { /* fcn 能够读取 i,但是不能改变 i 的值 */}

调用 function 函数时,既可以传入 int,也可以传入 const int。实参初始化形参时会忽略掉顶层 const,这时就会出现一种情况:C++ 中的重载函数要求形参列表不一致,但由于顶层 const 被忽略了,所以下面的两条语句会被视作同一个函数从而报错:

void function(const int i) { /* fcn 能够读取 i,但是不能改变 i 的值 */}
void function(int i) { /*...*/ } // 错误,重复定义了 fcn(int i)

关于 const 的相关知识可以参考博客:C++ const 限定符学习笔记,写的非常详细。

2.2 引用形参

不推荐把函数中不会改变的形参定义成普通的引用(即非常量引用)。使用非常量引用也会极大地限制函数所能接受的实参类型,因为常量引用可以接受各种类型的实参,但非常量引用不能接受 const 对象、字面值或者需要类型转换的对象。

2.3 数组形参

数组的两个性质:不允许拷贝数组以及使用数组时通常会将其转换成指针使得我们无法以值传递的方式使用数组形参,当我们为函数传递一个数组时,实际上传递的是指向数组首元素的指针

可以把形参写成类似数组的形式:

// 下面三个函数是等价的
void fcn(const int*)
void fcn(const int[])
void fcn(const int[10])

有三种方式管理数组的指针形参防止越界:

  1. 使用标记指定数组长度

该方法要求数组本身就包含一个结束标记,比如 C 语言字符串的末尾有一个空字符 ‘\0’。这种方法适用于那些有明显结束标记且该标记不会与普通数据混淆的情况。

  1. 使用标准库规范

该方法向函数传递数组首元素和尾后元素的指针,如下面的函数:

void print(const int *beg, const int *end)
{
	// 输出 beg 到 end 之间(不含 end)的所有元素
	while (beg != end)
		cout << *beg++ << endl; // 输出当前元素并将指针向前移动一个位置
}

为了调用这个函数就需要传入两个指针,一个指向首元素,另一个指向为尾元素的下一个位置:

int sample[2] = {0, 1};
print(begin(sample), end(sample));
  1. 传递数组大小的形参

在函数的形参列表定义一个表示数组大小的形参。因此上面的 print 函数可改写成如下形式:

// const int *a 等价于 const int a[]
void print(const int a[], int size) { /*...*/ }

同理,如果函数不需要改变数组元素时,指针形参应写成指向常量的指针。

2.4 数组引用形参

见下面的例子:

// 正确:形参是数组的引用,维度是类型的一部分
void print(int (&arr)[10])
{
	for (auto elem : arr)
		cout << elem << endl;
}

注意:&arr 两端的括号必不可少

void print(int &arr[10])		// 错误:将 arr 声明成了10个引用构成的数组
void print(int (&arr)[10])	// 正确:arr 是具有10个整数的整型数组的形参

该写法的缺陷在于它无形中限制了函数的可用性,比如上述的 print 函数就只能作用于大小为10的数组。

2.5 多维数组形参

C++ 中没有真正的多维数组,只有数组的数组。将多维数组传递给函数时,传递的其实是指向数组首元素的指针。因为处理的是数组的数组,所以首元素本身就是一个数组,指针就是一个指向数组的指针。

// matrix 指向数组的首元素,该数组的元素是由10个整数构成的数组
void print(int (*matrix)[10], int rowSize) { /*...*/ }
// 等价定义
void print(int matrix[][10], int rowSize) { /*...*/ }

*matrix 两端的括号必不可少

void print(int *matrix[10])		// 错误:将 matrix 声明成了10个指针构成的数组
void print(int (*matrix)[10])	// 正确:matrix 是指向含有10个整数的数组的指针

2.6 可变形参

要编写能处理不同数量实参的函数,主要有两种方法。

  • initializer_list 形参

如果函数的实参数量未知全部实参的类型都相同,就可以使用 initializer_list 类型的形参。它是一种标准库类型,主要提供以下操作:

#include<initializer_list> // initializer_list 类型的头文件

initializer_list<T> lst;				默认初始化;T类型元素的空列表
initializer_list<T> lst{a, b, c...};	lst 的元素数量和初始值一样多;lst 的元素是对应初始值的副本;列表中的元素是 const
lst2(lst)			拷贝或赋值一个 initializer_list 对象不会拷贝列表中的元素;拷贝后,原始列表和副本共享元素
lst2 = lst			等价于上句
lst.size()			返回列表中的元素数量
lst.begin()			返回指向 lst 中首元素的指针
lst.end()			返回指向 lst 中尾元素下一位置的指针

类似于 vector,initializer_list 也是一种模板类型。定义 initializer_list 对象时必须说明列表中的元素类型:

initializer_list<string> ls;	// initializer_list 的元素类型是 string
initializer_list<int> li;		// initializer_list 的元素类型是 int

不同于 vector 的地方在于 initializer_list 中的元素都是常量值,无法改变。

因此要给函数传递未知数量的实参时,可以将函数改写成如下的形式:

void function(initializer_list<int> il) { /*...*/ }

若想向 initializer_list 形参传递一个值的序列,则需要把序列放在花括号中:

function({1, 2, 3});
  • 省略符形参

省略符形参是为了便于 C++ 访问某些特殊的 C 代码而设置的,它们使用了名为 varargs 的 C 标准库功能。省略符形参只能出现在形参列表的最后面,有以下两种形式:

void function(parameter_list, ...);
void function(...);

前者指定了部分形参的类型,只有这部分形参需要进行类型检查(编译器要检查实参和形参的类型是否一致),而省略符形参对应的实参无需类型检查。

3 返回类型和 return 语句

return 语句的两种类型:

return;				// 第一种形式
return expression;	// 第二种形式

3.1 无返回值

第一种形式只能用在返回类型是 void 的函数,但返回 void 的函数不要求非得有 return 语句。即使没有的话,它们也会在函数体的最后一句隐式的执行 return。而 return 用在返回 void 的函数中的主要作用是退出函数。所以也可以将其用在函数执行过程中来提前退出函数。

但返回 void 的函数也不是不能用第二种形式,此时 expression 必须是另一个返回 void 的函数,返回其他类型则会报错。

3.2 有返回值

有返回值的函数必须使用第二种形式,返回值的类型必须与函数的返回类型相同,或者能隐式地转换成函数的返回类型。

不要返回局部对象的引用或指针,因为函数一旦完成,它所占用的内存空间也随之被释放掉,也意味着局部对象的引用或指针将引用一个不再存在的对象或指向不再有效的内存区域。

3.3 返回引用

调用一个返回引用的函数得到的是左值,其他的返回类型则是右值。因此可以让一个函数返回引用。特别地,可以为返回类型是非常量引用的函数的结果赋值(如果返回类型是常量引用就不能给调用的结果赋值):

char &function(string &str, int index)
{
	return str[index];		// 假定传入 function 函数的索引值都是有效的
}
int main()
{
	string s("a value"); 	// 等价于 string s = "a value";
	cout << s << endl;
	function(s, 0) = 'A';
	cout << s << endl;

	return 0;
}

注意到 function 前有个 &,表明函数返回的是 char & 类型,因此 function(s, 0) 返回的实际上是 string 对象 s 的0号位置上字符的引用,将其作为左值修改为 A,从而输出的结果如下:
在这里插入图片描述

3.4 列表初始化返回值

函数可以返回花括号包围的值的列表。类似于其他返回结果,此处的列表也用来对变量进行初始化。如果返回的列表是空,临时量就执行值初始化;否则,返回的值由函数的返回类型决定。如下:

vector<string> process()
{
	if (expression1)
		return {}; // 返回一个空 vector 对象
	else if (expression2)
		return {"1", "111"}; // 返回列表初始化的 vector 对象 
	else
		return {"1", "111", "11111"};
}

(确实是没想到 vector 也能作为返回类型,但仔细一想想好像也没什么不可以的)如果函数返回的是内置类型,则花括号包围的列表最多包含一个值,而且该值所占空间不应该大于目标类型的空间。如果返回的是类类型,由类本身定义初始值如何使用。

3.5 递归

如果一个函数调用了它自身,不管这种调用是直接的还是间接的,都称该函数为递归函数。

3.6 返回数组指针

因为数组不能像 vector 那样拷贝,所以函数不能返回数组,但是可以返回数组的指针或引用。但直接定义一个返回数组指针或引用的函数很烦琐,最直接的方法就是使用类型别名:

typedef int arr[10];	// arr 是一个类型别名,它表示的类型是含有10个整数的数组
using arr = int[10];	// 等价声明
// 用别名来声明函数:function 返回一个指向含有10个整数的数组的指针
arr* function(int i);

上面第二条语句可以参考C++ 标准库类型学习笔记(一)(vector、string 篇)4.5 类型别名那一块。

3.6.1 声明一个返回数组指针的函数

要想在声明 function 时不使用类型别名,就必须牢记被定义的名字后面数组的维度:

int arr[10];	// arr 是一个含有10个整数的数组
int *p1[10];	// p1 是一个含有10个指针的数组
int (*p2)[10];	// p2 是一个指针,它指向含有10个整数的数组

返回数组指针的函数声明形式如下:

Type (*function(parameter_list))[dimension]

Type 表示元素的类型,function 是函数名,parameter_list 是形参列表,dimension 表示数组的大小。用一个具体的例子来解释:

int (*func(int i))[10];

可以按照由内到外的顺序来理解该声明:

  • func(int i) 表示调用 func 函数时需要一个 int 类型的实参。
  • (*func(int i)) 意味着可以对函数调用的结果执行解引用。
  • (*func(int i))[10] 表示解引用 func 的结果将得到一个大小为10的数组。
  • int (*func(int i))[10] 表示数组中的元素是 int 类型。

3.6.2 使用尾置返回类型

使用尾置返回类型可以简化上述 func 声明。任何函数都能使用尾置返回,但是这种形式对于返回类型比较复杂的函数最有效,比如返回类型是数组的指针或引用。尾置返回类型跟在形参列表后面并以一个 “->” 符号开头。然后在本应该出现返回类型的地方放一个 auto 表示函数真正的返回类型跟在形参列表之后。

// func 接受一个 int 类型的实参,返回一个指针,该指针指向含有10个整数的数组
auto func(int i) -> int(*)[10]

3.6.3 使用 decltype

如果知道函数返回的指针将指向哪个数组,就可以使用 decltype 关键字声明返回类型:

int odd[] = {1, 3, 5, 7, 9};
int even[] = {0, 2, 4, 6, 8};
// 返回一个指针,该指针指向含有5个整数的数组
decltype(odd) *arrPtr(int i)
{
	// 根据参数 i 的奇偶返回指向其中一个数组的指针
	return (i % 2) ? &odd : &even; // 返回一个指向数组的指针
}

decltype 并不负责把数组类型转换成对应的指针,所以 decltype 的结果是个数组,要想表示 arrPtr 返回指向数组的指针还必须在函数声明时加一个 * 符号。

练习

编写一个函数的声明,使其返回数组的引用并且该数组包含10个 string 对象。要求分别使用直接声明、类型别名、尾置返回类型以及 decltype。

我估计大多数读者都会疑惑,要求是数组(也就意味着不是 vector),还要求元素对象是 string,那该如何定义呢?我去百度查阅了才知道,原来 string 是可以定义成数组的…比如下面这种:

string s[] = {"111", "222", "333"};
cout << s[0] << ' ' << s[1] << ' ' << s[2] << endl;

输出结果如下:
在这里插入图片描述
所以函数声明可以写成如下四种形式:

// 直接声明
string (&func())[10]; { /*...*/ } // func() 表明调用函数不需要任何参数
// 类型别名
typedef string arr[10]; // arr 是一个类型别名,它表示的是含有10个 string 对象的数组
using arr = string[10]; // arr 的等价声明
arr &func();			// func 返回一个含有10个 string 对象的数组的引用
// 尾置返回类型
auto func() -> string (&)[10];
// decltype
string s[10]; 			// 定义一个 string 数组
decltype(s) &func();	// 返回一个含有10个 string 对象的数组的引用

4 函数重载

同一个作用域下,几个函数名字相同但形参列表不同,称之为重载函数。目的是减轻程序员起名字、记名字的负担。对于重载的函数来说,必须在形参的数量或者类型上有所不同。但有一种情况也不能算做重载函数,即2.1中提到的,实参初始化形参时会忽略掉顶层 const,一个拥有顶层 const 的形参无法和另一个没有顶层 const 形参区分开来:

int func(int i);
int func(const int i);	// 错误,重复声明了函数 int func(int i)
int func(int *i);
int func(int *const i);	// 错误,重复声明了 int func(int *i)

但是,如果形参是指针或者引用,那么通过区分其指向(引用)的是常量对象还是非常量对象就可以实现函数重载,因为此时的 const 是底层的:

int func(int*);			// 函数接受指向 int 型的指针作为形参
int func(const int*);	// 新函数,接受指向 const int* 型的指针作为形参
int func(int&);			// 新函数,接受 int 型的引用作为形参
int func(const int&);	// 新函数,接受 const int 型的引用作为形参

因为 const 不能转换成其他类型,所以只能把 const 对象传递给 const 形参;相反,因为非常量可以转换成 const,所以上面的四个函数都能作用于非常量对象或者指向非常量对象的指针。但是传递一个非常量对象或者指向非常量对象的指针时,编译器会优先选用非常量的版本。

5 特殊用途语言特性

5.1 默认实参

有时候,我们希望函数的某些形参在每次调用中都被赋予一个相同的值,这样我们在调用时就不需要每次都传值进去;但偶尔我们也希望自己重新传一个新的值进去计算,从而得到一个其他计算结果。此时就需要用到默认实参。比如我们想声明一个函数,它用来计算高度相等,半径不等的圆柱体的体积:每次计算,我们都只需传入圆的半径即可;也可以传入新的高度进行计算;或者如果需要更精确的结果,就传入精度更高的 π 值:

double cal_Volume(double radius, double hight = 10, double pi = 3.14);
// 普通调用:想使用默认形参,调用函数时省略它即可
cal_Volume(2);
// 传入新的高度进行计算
cal_Volume(2, 8)
// 需要更精确的结果时
cal_Volume(2, 10, 3.1415926);

有一点需要注意,一旦某个形参被赋予了默认值,在它右边的所有形参都必须有默认值。在给定的作用域中一个形参只能被赋予一次默认实参,函数的后续声明只能为之前那些没有默认值的形参添加默认实参,并且该形参右侧的所有形参都必须有默认值。

实参在初始化形参时是按照位置来的,如上面的 Cal_Volume 函数:如果我们不想改变高度,只想传入精度更高的 π,我们也必须填补上高度值,否则 Cal_Volume(2, 3.1415926) 的 3.1415926 会被用来初始化 hight。所以我们要尽量让不怎么使用默认值的形参出现在前面,让经常使用默认值的形参出现在后面。

5.2 内联函数和 constexpr 函数

5.2.1 内联函数

将函数指定为内联函数,通常就是将它在每个调用点上展开。将函数定义为内联函数需要在返回类型前加上 inline 关键字,比如:

inline int compare(int a, int b) // 函数返回较小值
{
	return a < b ? a : b;
}

那么下面的语句在调用 compare 函数,在编译过程中会展开成后者的形式:

cout << compare(1, 2) << endl;
cout << a < b ? a : b << endl;

这样做的目的是消除 compare 函数运行时的开销(涉及到中断的概念了)。一般来说,内联机制用于优化规模较小、流程直接、频繁调用的函数。而且很多编译器也不支持内联递归函数。

5.2.2 constexpr 函数

constexpr 函数是指能用于常量表达式的函数。定义 constexpr 函数要遵循几项约定:函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条 return 语句:

常量表达式 是指值不会改变且在编译过程就能得到计算结果的表达式,比如字面值、用 const 初始化的对象等。

constexpr int func() { return 52; }
constexpr int a = func(); // 正确:a 是一个常量表达式

func 函数返回的是常量表达式,所以能用它来初始化 constexpr 类型的变量 foo。在执行初始化时,编译器会把对 constexpr 函数的调用替换成结果值,也就是说,constexpr 函数被隐式地指定为内联函数。当然也可允许 constexpr 函数的返回值并非一个常量:

constexpr int func(int cnt) { return 52 * cnt}

当调用 func 函数传入的实参是常量表达式时,比如 func(2),它的返回值也是常量表达式。但如果传入的是非常量表达式,比如 func(i),就不能够拿来初始化其他的常量表达式

编译器要想展开内联函数,仅仅有声明是不够的,还必须要有函数的定义。多次定义都必须一致,因此通常将内联函数和 constexpr 函数定义在头文件中。

6 函数指针

函数指针指向的是函数而非对象。函数指针指向某种特定类型,而函数的类型由它的返回类型形参类型共同决定:

// 声明一个 function 函数
bool function(const string &, const string &);
// 声明一个指向函数的指针
bool (*pf)(const string &, const string &);

从声明的名字开始观察,pf 前面有个 * 表明 pf 是指针;右侧的形参列表表明 pf 指向的是函数;左侧的 bool 表明返回类型是布尔值。因此,pf 是一个指向函数的指针,函数的参数是两个 const string 的引用,返回值是 bool 类型。

pf 两端的括号不能少,否则如下:

bool *pf(const string &, const string &);

此时是声明了一个名为 pf 的函数,该函数返回一个 bool 类型的指针。

6.1 使用函数指针

可以将函数名作为一个值使用,此时函数自动转换成指针,如下将函数 function 的地址赋给 pf:

// 取地址符可用可不用,前提是 pf 为指针
pf = function;
pf = &function;

可以直接使用指向函数的指针调用函数而无须解引用:

bool function(const string &, const string &) { ... };	// 定义一个 function 函数
bool (*pf)(const string &, const string &);				// 声明一个指向 function 函数的指针
bool b1 = pf("hello", "goodbye");						// 调用 function 函数,无须解引用
bool b2 = (*pf)("hello", "goodbye");					// 等价调用,解引用也可以
bool b3 = function("hello", "goodbye");					// 等价调用

因为指针指向的是函数,所以指向不同函数的指针间不存在什么转换规则。但是可以为指针赋一个 nullptr 或者值为0的整型常量表达式,表示该指针没有指向任何一个函数。

6.2 重载函数的指针

指针指向重载函数时,函数的类型一定要表示清楚:

// 定义两个重载函数
void function(int*) { ... }
void function(unsigned int) {...};

void (*pf1)(unsigned int) = function; 	// 正确:定义了一个指向第二个函数的指针
void (*pf2)(int) = function; 			// 错误:形参列表不匹配
double (*pf3)(int*) = function; 		// 错误:返回类型不匹配

6.3 函数指针形参

函数的形参也可以是指向函数的指针,类似数组,此时的形参看起来是函数类型,实际上是当成指针来使用:

// 定义一个 function 函数
bool function(const string &, const string &) { ... };
// 第二个形参虽然是函数类型,但它会自动地转换成指向函数的指针
void test(const string &s1, bool pf(const string &, const string &));
// 等价的声明:显式地将形参定义成指向函数的指针
void test(const string &s1, bool (*pf)(const string &, const string &));
// 此时可以直接将函数当做实参传入,传入的函数名会自动转换成指针
test(s1, function);

使用类型别名可以简化形参的代码,如下所示:

// func1 和 func2 是函数类型
typedef bool func1(const string &, const string &);
typedef decltype(function) func2;						// 等价类型
using func3 = bool(const string &, const string &); 	// 等价类型

第一条语句实际上是给 bool (const string, const string &) 定义了一个别名 func1,用来指代任何参数是两个 const string 的引用,返回值是 bool 类型的函数。第二条语句是第一条语句的等价语句,decltype 返回的是上述的函数类型。要定义指向函数的指针的代码如下:

// pf1 和 pf2 是指向函数的指针
typedef bool(*pf1)(const string &, const string &);
typedef decltype(function) *pf2; 						// 等价类型
using pf3 = bool(*)(const string &, const string &);	// 等价类型

同理,第一条语句实际上是给 bool (*)(const string, const string &) 定义了一个别名 pf1,用来指代任何指向该函数类型的指针。第二条语句是第一条语句的等价语句,由于 decltype 返回的结果是函数类型,所以要在 pf2 前加上 * 才能得到指针。

因此可以用类型别名重新声明 test 函数:

void test(const string &, const string &, func1);
void test(const string &, const string &, pf2);

6.4 返回指向函数的指针

函数不能返回函数,但是能返回指向函数的指针。但是要将返回类型写成指针的形式,因为编译器不会将函数返回类型当成对应的指针。使用类型别名来返回函数的指针:

using F = int(int*, int);		// F 是函数类型
using PF = int(*)(int*, int);	// PF 是指针类型

必须显式地将返回类型指定为指针:

PF f1(int);		// 正确:PF 是指向函数的指针,f1 返回指向函数的指针
F f1(int);		// 错误:F 是函数类型,f1 不能返回一个函数
F *f1(int);		// 正确:显式地指定返回类型是指向函数的指针

也可用下面的形式直接声明 f1:

int (*f1(int))(int*, int);

由内向外进行理解:f1 有形参列表,所以 f1 是个函数;f1 前面有个 *,所以 f1 返回一个指针;进一步可以看到指针类型本身也包含形参列表,因此指针指向函数,函数的返回类型是 int。

也可以使用尾置返回类型声明一个返回函数指针的函数:

auto f1(int) -> int (*)(int*, int);

希望本篇博客能对你有所帮助,也希望看官能动动小手点个赞哟~~。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 20:22:12  更:2022-10-08 20:26:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 10:56:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码