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++知识库 -> Linux C/C++之线程基础 -> 正文阅读

[C++知识库]Linux C/C++之线程基础

目录

1. 什么是线程

1.1 进程与线程

1.2 线程

1.3 线程的发展史

2. 如何创建,使用线程

2.1 pthread_create函数创建线程

2.2?pthread_create函数的使用

2.3?主线程结束,?分支线程也会随之结束

2.4?同一进程内多个线程共用进程资源

2.5?线程使用进程开辟的动态内存,主线程提前释放内存会造成的问题

2.6?使用地址传递传递数据到线程运行函数

2.7?使用值传递传递数据到线程运行函数

2.8?结构体数据类型的参数传递

3. 线程的结束

3.1?自然结束

3.2?主线程结束,?分支线程随之结束?

3.3?线程自己结束自己

3.4?其它线程结束某个线程

4.?线程的同步

4.1?临界区域与临界数据

4.2?多个线程同时操作临界数据导致的问题

4.3?使用线程同步解决临界数据脏的方式

4.3.1?原子锁(atomic)

4.3.2?自旋锁(spin)

4.3.3?信号量(sem)

4.3.4?读写锁(rwlock)

4.3.5?互斥锁(mutex)

4.3.6?临界变量(cond)


1. 什么是线程

1.1 进程与线程

进程是操作系统资源调度的基本单位

线程是操作系统调度的基本单位

1.2 线程

?线程,被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元

1.3 线程的发展史

2.2版本的内核中, 没有线程, 进程的个数也有限,?4090个,?线程是轻量级的进程, 2.4版本的内核中, 有了线程的概念, 线程的个数可以无限, 协程是更轻量级的线程

2. 如何创建,使用线程

2.1 pthread_create函数创建线程

?

创建并执行线程,执行代码为线程函数,并且和当前主线程并行

2.2?pthread_create函数的使用

//使用pthread_create 创建一个线程
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void* pFunc(void* arg){
	int m = 1;
	while(1){
		printf("线程 --- m: %d\n",m++);
		sleep(1);
	}
}
int main(){
	int n = 1;
	//创建一个线程
	pthread_t pid;
	pthread_create(&pid,NULL,pFunc,NULL);
	while(1){
		printf("主函数 --- n: %d\n",n++);
		sleep(1);
	}
	return 0;
}

?

2.3?主线程结束,?分支线程也会随之结束

//当主线程结束,分支线程也会随即结束
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void* pFunc(void* arg){
	int m = 1;
	while(1){
		printf("线程 --- m: %d\n",m++);
		sleep(1);
	}
}
int main(){
	int n = 1;
	//创建一个线程
	pthread_t pid;
	pthread_create(&pid,NULL,pFunc,NULL);
	for(int i = 0; i < 5; i++){
		printf("主函数 --- n: %d\n",n++);
		sleep(1);
	}
	return 0;
}

?

2.4?同一进程内多个线程共用进程资源

//同一进程内的线程共用进程的资源
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

int num = 0;
void* pFunc1(void* arg){
	while(1){
		printf("线程1 --- num: %d\n",num++);
		sleep(1);
	}
}

void* pFunc2(void* arg){	//先于线程一执行
	while(1){
		printf("线程2 --- num: %d\n",num++);
		sleep(1);
	}
}

int main(){
	//创建一个线程
	pthread_t pid1,pid2;
	pthread_create(&pid1,NULL,pFunc1,NULL);
	pthread_create(&pid2,NULL,pFunc2,NULL);
	while(1){
		printf("主函数 --- num: %d\n",num++);
		sleep(1);
	}
	return 0;
}

2.5?线程使用进程开辟的动态内存,主线程提前释放内存会造成的问题

//当使用动态内存申请时,主线程提前将内存释放会造成的问题
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

void* pFunc(void* arg){
	while(1){
		printf("线程 --- arg: %d\n",*(int*)arg);
		sleep(1);
	}
}
int main(){
	int* n = (int*)malloc(4);
	*n = 666;
	//创建一个线程
	pthread_t pid;
	pthread_create(&pid,NULL,pFunc,n);
	for(int i = 0; i < 5; i++){
		if(i == 2){
			free(n);
			n = NULL;
		}
		printf("主函数 --- n: %d\n",(*n)++);
		sleep(1);
	}
	return 0;
}

2.6?使用地址传递传递数据到线程运行函数

//使用变量的地址传参 &
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void* pFunc(void* arg){
	while(1){
		printf("线程 --- arg: %d\n",(*(int*)arg)++);
		sleep(1);
	}
}
int main(){
	int n = 666;
	//创建一个线程
	pthread_t pid;
	pthread_create(&pid,NULL,pFunc,&n);
	while(1){
		printf("主函数 --- n: %d\n",n++);
		sleep(1);
	}
	return 0;
}

2.7?使用值传递传递数据到线程运行函数

//使用(void*)变量直接传参
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void* pFunc(void* arg){
	while(1){
		printf("线程 --- arg: %d\n",(int)arg++);
		sleep(1);
	}
}
int main(){
	int n = 666;
	//创建一个线程
	pthread_t pid;
	pthread_create(&pid,NULL,pFunc,(void*)n);
	while(1){
		printf("主函数 --- n: %d\n",n++);
		sleep(1);
	}
	return 0;
}

2.8?结构体数据类型的参数传递

//结构体类型的参数传递
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

struct Student{
	char name[20];
	int age;
	double score;
};

void* pFunc(void* arg){
	struct Student* stu = (struct Student*)arg;
	while(1){
		printf("线程===\n");
		printf("name:%s,age:%d,score:%g\n",
			stu->name,stu->age,stu->score);
		sleep(1);
	}
}

int main(){

	struct Student stu = {"张三",18,66.66};
	pthread_t pid;
	pthread_create(&pid,NULL,pFunc,&stu);
	for(int i = 0; i < 5; i++){
		printf("主函数---\n");
		sleep(1);
	}

	return 0;
}

3. 线程的结束

3.1?自然结束

线程运行完自己应该执行的代码块后自然结束

3.2?主线程结束,?分支线程随之结束?

注: 主线程最好等待分支线程结束再结束

pthread_join函数使用

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

void* func(void* arg){
	while(1){
		sleep(2);
		printf("线程-----\n");	
	}
}

int main(){
	pthread_t pid;

	pthread_create(&pid,NULL,func,NULL);

	printf("主线程-----\n");

	return 0;
}

没有pthread_join函数的情况 ,?主线程不会等待分支线程结束后再结束

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

void* func(void* arg){
	while(1){
		sleep(2);
		printf("线程-----\n");	
	}
}

int main(){
	pthread_t pid;

	pthread_create(&pid,NULL,func,NULL);

	printf("主线程-----\n");

	pthread_join(pid,NULL);

	return 0;
}

?有pthread_join的情况,主线程会等待分支线程结束后,自己再结束

3.3?线程自己结束自己

进程的结束使用 exit? ?或? ? _exit

线程的结束使用 pthread_exit

//线程自己结束自己pthread_exit
//void pthread_exit(void *retval); retval线程结束的返回值
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

void* func(void* arg){
	int num = 666;
	for(int i = 0;i < 10; i++){
		if(i == 5){
			//pthread_exit((void*)num);
			pthread_exit((void*)"线程退出!");
		}
		printf("线程 >> %d\n",(*(int*)arg)++);
		sleep(1);
	}
	
}

int main(){
	int n = 1;
	void* pthread_res; //接收线程结束的返回值
	pthread_t pid;

	pthread_create(&pid,NULL,func,&n);

	pthread_join(pid,&pthread_res); //等待线程结束并接收返回值

	//printf("pthread_return: %d\n",(int)pthread_res);
	printf("pthread_return: %s\n",(char*)pthread_res);
	printf(" >> %d\n",n);

	return 0;
}

3.4?其它线程结束某个线程

pthread_cancel函数:?仅向线程发送一个结束请求,?至于是否结束看pthread_setcancelstate()函数参数一是忽略(PTHREAD_CANCEL_DISABLE)还是响应(PTHREAD_CANCEL_ENABLE)? ?

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

int n = 1;
pthread_t pid1;

void* func1(void* arg){
	while(1){
		printf("线程1 >> %d\n",n++);
		//PTHREAD_CANCEL_ENABLE(缺省:响应)
		//PTHREAD_CANCEL_DISABLE(忽略)
		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
		pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
		//PTHREAD_CANCEL_DEFERRED(下个取消点)
		//PTHREAD_CANCEL_ASYNCHRONOUS(异步(随时)取消)

		sleep(1);
	}
}

int main(){

	pthread_create(&pid1,NULL,func1,NULL);
	while(1){
		if(n == 5){
			pthread_cancel(pid1);
			break;
		}
	}

	pthread_join(pid1,NULL);

	printf(">> %d\n",n);

	return 0;
}

?PTHREAD_CANCEL_ENABLE:?

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

int n = 1;
pthread_t pid1;

void* func1(void* arg){
	while(1){
		printf("线程1 >> %d\n",n++);
		//PTHREAD_CANCEL_ENABLE(缺省:响应)
		//PTHREAD_CANCEL_DISABLE(忽略)
		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
		pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
		//PTHREAD_CANCEL_DEFERRED(下个取消点)
		//PTHREAD_CANCEL_ASYNCHRONOUS(异步(随时)取消)
		//pthread_testcancel();  //加一个取消点

		printf("sleep1\n");
		sleep(1);

		printf("sleep2\n");
		sleep(1);

		printf("sleep3\n");
		sleep(1);
	}
}

int main(){

	pthread_create(&pid1,NULL,func1,NULL);
	while(1){
		if(n == 5){
			pthread_cancel(pid1);
			break;
		}
	}

	pthread_join(pid1,NULL);

	printf(">> %d\n",n);

	return 0;
}

?

4.?线程的同步

4.1?临界区域与临界数据

多个线程可以同时 访问 的 区域 称之为?临界区域

多个线程可以同时 操作 的 数据 称之为 临界数据

4.2?多个线程同时操作临界数据导致的问题

//多个线程同时操作临界数据,导致临界数据脏的问题
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

int n = 0;
void* func1(void* arg){
	for(int i = 0; i < 25000000; i++)
		n++;
}

void* func2(void* arg){
	for(int i = 0; i < 25000000; i++)
		n++;
}

int main(){
	pthread_t p1,p2,p3,p4;

	pthread_create(&p1,NULL,func1,NULL);
	pthread_create(&p2,NULL,func2,NULL);
	pthread_create(&p3,NULL,func1,NULL);
	pthread_create(&p4,NULL,func2,NULL);

	pthread_join(p1,NULL); 	//等待线程结束
	pthread_join(p2,NULL); 	//等待线程结束
	pthread_join(p3,NULL); 	//等待线程结束
	pthread_join(p4,NULL); 	//等待线程结束

	printf("n: %d\n",n);	// <= 100000000

	return 0;
}

4.3?使用线程同步解决临界数据脏的方式

内核态用户态
原子锁(atomic)读写锁(rwlock)
自旋锁(spin)互斥锁(mutex)
信号量(sem)临界变量(cond)

4.3.1?原子锁(atomic)

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

#define atomic_inc(x)  __sync_fetch_and_add(x,1)

void* func1(void* arg){
	for(int i = 0; i < 50000000; i++)
		atomic_inc((int*)arg);
}

void* func2(void* arg){
	for(int i = 0; i < 50000000; i++)
		atomic_inc((int*)arg);
}

int main(){
	pthread_t p1,p2;
	int n = 0;
	
	pthread_create(&p1,NULL,func1,&n);
	pthread_create(&p2,NULL,func2,&n);

	pthread_join(p1,NULL); 	//等待线程结束
	pthread_join(p2,NULL); 	//等待线程结束

	printf("n: %d\n",n);	

	return 0;
}

4.3.2?自旋锁(spin)

自旋锁的建立消耗的资源少,?但是当线程阻塞时,?它会一直循环不断地检查锁是否可用,?因此当线程处于阻塞状态下,?消耗的资源相对较多

  1. 初始化自旋锁? ? ? ? ?pthread_spin_init????????
  2. 获取(上)锁? ? ? ? ? ? ? pthread_spin_lock
  3. 释放(解)锁? ? ? ? ? ? ? pthread_spin_unlock
  4. 销毁锁? ? ? ? ? ? ? ? ? ? pthread_spin_destroy
//自旋锁(spin)的使用
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

int n = 0;
//0. 定义自旋锁
pthread_spinlock_t spin;

void* func1(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁
		pthread_spin_lock(&spin);
		//操作
		n++;
		//3. 解锁
		pthread_spin_unlock(&spin);
	}
}

void* func2(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁
		pthread_spin_lock(&spin);
		//操作
		n++;
		//3. 解锁
		pthread_spin_unlock(&spin);
	}
}

int main(){
	pthread_t p1,p2;

	//1. 初始化自旋锁
	pthread_spin_init(&spin,PTHREAD_PROCESS_PRIVATE);

	pthread_create(&p1,NULL,func1,NULL);
	pthread_create(&p2,NULL,func2,NULL);

	pthread_join(p1,NULL); 	//等待线程结束
	pthread_join(p2,NULL); 	//等待线程结束

	printf("n: %d\n",n);	

	//4. 销毁自旋锁
	pthread_spin_destroy(&spin);

	return 0;
}

4.3.3?信号量(sem)

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>

int n = 0;
//0. 定义信号量
sem_t sem;

void* func1(void* arg){
	//信号量值减一
	if(0 == sem_wait(&sem)){
		for(int i = 0; i < 50000000; i++){
			n++;
			//信号量值加一
			sem_post(&sem);
		}
	}	
}

int main(){
	pthread_t p1,p2;

	//1. 初始化信号量
	//参数二 0 当前进程使用   1  多个进程间共享
	sem_init(&sem, 0, 2);

	pthread_create(&p1,NULL,func1,NULL);
	pthread_create(&p2,NULL,func1,NULL);

	pthread_join(p1,NULL); 	//等待线程结束
	pthread_join(p2,NULL); 	//等待线程结束

	printf("n: %d\n",n);

	//销毁信号量
	sem_destroy(&sem);	

	return 0;
}

4.3.4?读写锁(rwlock)

  1. 初始化读写锁? ? ? ? pthread_rwlock_init
  2. 获取锁? ? ? ? ? ? ? ? ? ?pthread_rwlock_wrlock(写锁)? ? ? ?pthread_rwlock_rdlock(读锁)
  3. 释放锁? ? ? ? ? ? ? ? ? ?pthread_rwlock_unlock
  4. 销毁锁? ? ? ? ? ? ? ? ? ?pthread_rwlock_destroy
//读写锁(rwlock)的使用	读读相容
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

int n = 0;
//0. 定义读写锁变量
pthread_rwlock_t rwlock;

void* func1(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁(读锁)
		pthread_rwlock_rdlock(&rwlock);
		//pthread_rwlock_wrlock(&rwlock);
		//操作
		n++;
		//3. 解锁
		pthread_rwlock_unlock(&rwlock);
	}
}

void* func2(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁(读锁)
		pthread_rwlock_rdlock(&rwlock);
		//pthread_rwlock_wrlock(&rwlock);
		//操作
		n++;
		//3. 解锁
		pthread_rwlock_unlock(&rwlock);
	}
}

int main(){
	pthread_t p1,p2;

	//1. 初始化读写锁
	pthread_rwlock_init(&rwlock,NULL);

	pthread_create(&p1,NULL,func1,NULL);
	pthread_create(&p2,NULL,func2,NULL);

	pthread_join(p1,NULL); 	//等待线程结束
	pthread_join(p2,NULL); 	//等待线程结束

	printf("n: %d\n",n);	

	//4. 销毁读写锁
	pthread_rwlock_destroy(&rwlock); 

	return 0;
}

?读读相容?

?

void* func1(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁(读锁)
		pthread_rwlock_rdlock(&rwlock);
		//操作
		n++;
		//3. 解锁
		pthread_rwlock_unlock(&rwlock);
	}
}

void* func2(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁(写锁)
		pthread_rwlock_wrlock(&rwlock);
		//操作
		n++;
		//3. 解锁
		pthread_rwlock_unlock(&rwlock);
	}
}

?读写相斥

void* func1(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁(写锁)
		pthread_rwlock_wrlock(&rwlock);
		//操作
		n++;
		//3. 解锁
		pthread_rwlock_unlock(&rwlock);
	}
}

void* func2(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁(写锁)
		pthread_rwlock_wrlock(&rwlock);
		//操作
		n++;
		//3. 解锁
		pthread_rwlock_unlock(&rwlock);
	}
}

写写相斥????????

4.3.5?互斥锁(mutex)

互斥锁适合频繁操作的情况

若等待时间较长,?突然要解决事务(注意临界数据安全的问题)

互斥锁的建立消耗的资源多,?但是当线程阻塞时,?不会循环不断地检查锁是否可用,?因此当线程处于阻塞状态下不消耗资源

因此自旋锁适用于阻塞时间很短的情况,?互斥锁适用于阻塞时间很长的情况

  1. 初始化互斥锁? ? ? ? ?pthread_mutex_init????????
  2. 获取(上)锁? ? ? ? ? ? ? pthread_mutex_lock
  3. 释放(解)锁? ? ? ? ? ? ? pthread_mutex_unlock
  4. 销毁锁? ? ? ? ? ? ? ? ? ? pthread_mutex_destroy
//互斥锁(mutex)的使用
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

int n = 0;
//0. 定义互斥变量
pthread_mutex_t mutex;

void* func1(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁
		pthread_mutex_lock(&mutex);
		//操作
		n++;
		//3. 解锁
		pthread_mutex_unlock(&mutex);
	}
}

void* func2(void* arg){
	for(int i = 0; i < 50000000; i++){
		//2. 上锁
		pthread_mutex_lock(&mutex);
		//操作
		n++;
		//3. 解锁
		pthread_mutex_unlock(&mutex);
	}
}

int main(){
	pthread_t p1,p2;

	//1. 初始化互斥量
	pthread_mutex_init(&mutex,NULL);

	pthread_create(&p1,NULL,func1,NULL);
	pthread_create(&p2,NULL,func2,NULL);

	pthread_join(p1,NULL); 	//等待线程结束
	pthread_join(p2,NULL); 	//等待线程结束

	printf("n: %d\n",n);
	//4. 销毁互斥锁
	pthread_mutex_destroy(&mutex);

	return 0;
}

?

4.3.6?临界变量(cond)

?临界变量是互斥锁的升级(因此临界变量一般搭配互斥锁使用)

  1. 初始化互斥锁,临界变量 ? ? ? ? pthread_mutex_init? ? ? ??pthread_cond_init????????
  2. 获取(上)锁,?临界变量等待? ? ? pthread_mutex_lock? ? ? pthread_cond_wait
  3. 释放(解)锁? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pthread_mutex_unlock
  4. 发信号给临界变量? ? ? ? ? ? ? ? ??pthread_cond_signal? ? ? ??pthread_cond_broadcast
  5. 销毁锁,销毁临界变量? ? ? ? ? ? ?pthread_mutex_destroy? ?pthread_cond_destroy
//临界(条件)变量(cond)的使用
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

int n = 0;
//0. 定义互斥变量
pthread_mutex_t mutex;
//0. 定义临界变量
pthread_cond_t cond;

void* func1(void* arg){
	for(int i = 0; i <= 5; i++){
		//2. 上锁,临界变量等待
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond,&mutex);
		//操作
		printf("线程1 ------ %d\n",n++);
		//3. 解锁
		pthread_mutex_unlock(&mutex);
	}
}

void* func2(void* arg){
	for(int i = 0; i <= 5; i++){
		//2. 上锁,临界变量等待
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond,&mutex);
		//操作
		printf("线程2 ====== %d\n",n++);
		//3. 解锁
		pthread_mutex_unlock(&mutex);
	}
}

int main(){
	pthread_t p1,p2,p3,p4;

	//1. 初始化互斥锁和临界变量
	pthread_mutex_init(&mutex,NULL);
	pthread_cond_init(&cond,NULL);

	pthread_create(&p1,NULL,func1,NULL);
	pthread_create(&p2,NULL,func2,NULL);

	//4. 发信号给临界变量
	for(int i = 0; i < 13; i++){
		//解除全部线程的阻塞状态(一次)
		//pthread_cond_broadcast(&cond);
		//解除至少一个线程的阻塞状态(一次)
		pthread_cond_signal(&cond);
		//pthread_cond_signal(&cond);
		sleep(1);
	}
	
	pthread_join(p1,NULL); 	//等待线程结束
	pthread_join(p2,NULL); 	//等待线程结束

	printf("n: %d\n",n);	

	//5. 销毁互斥锁和临界变量
	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&cond);

	return 0;
}

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-24 09:11:57  更:2022-04-24 09:14:46 
 
开发: 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年11日历 -2024/11/23 22:17:50-

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