Linux下加密库Libsodium 使用实践(ip监听、封装的加密消息、运行系统命令)

news2025/1/21 14:01:38

Libsodium 是一个用C语言编写的库,是一种新的易于使用的高速软件库,用于网络通信、加密、解密、签名等实现和简化密码学。

完成 Libsodium 安装

Libsodium 是一个用于加密,解密,数字签名,密码哈希,等的,现代的,易用的密码学库。如果你在环境中没有安装sodium。可以执行如下操作:

yum -y install libsodium libsodium-devel


Libsodium 使用方法

在程序中,只需包含头文件 sodium.h 即可。库的名字是 sodium (使用 -lsodium 来链接)。


sodium_init() 初始化 libsodium 库,必须在 libsodium 库的其他函数之前被调用。也可以自己实现sodium_init。也就是在 Unix /Linux系统中, 会打开 /dev/urandom 文件,并且会保持这个文件 fd 打开.

void randombytes(uint8_t buffer[], unsigned long long size)
{
	int fd;

	fd = open( "/dev/urandom", O_RDONLY );
	if( fd < 0 ) {
		fprintf( stderr, "Failed to open /dev/urandom\n" );
		exit(1);
	}

	int rc;
	if( (rc = read( fd, buffer, size )) >= 0 ) {
		close( fd );
	}
}

一些基础知识

  • 什么是密钥

一般就是一个字符串或数字,在加密或者解密时传递给加密/解密算法。

  • 什么是对称加密算法

加密和解密都是使用的同一个密钥。因此对称加密算法要保证安全性的话,密钥要做好保密,只能让使用的人知道,不能对外公开。

  • 什么是非对称加密算法

加密使用的密钥和解密使用的密钥是不同的。 公钥密码体制就是一种非对称加密算法。

  • 如何理解公钥密码体制

主要分为三个部分:公钥、私钥、加密/解密算法。

可以理解为,加密解密过程如下:

加密:通过加密算法和公钥对内容(或者说明文)进行加密,得到密文。
解密:通过解密算法和私钥对密文进行解密,得到明文。

公钥密码体制的公钥和算法都是公开的,私钥是保密的。在实际的使用中,有需要的人会生成一对公钥和私钥,把公钥发布出去给别人使用,自己保留私钥。

  • 简解签名和验签

服务器有两把钥匙一把公钥、一把私钥。客户端想和服务器达到秘密交流就需要服务器特有的公钥进行加密这样别人就不知道你发送的是什么,服务器收到客户端的信息后用特有的私钥解密就能看到信息内容。服务器要给客户端返回消息,这时候就会使用Hash函数对信息生成一个摘要(Digest),用私钥对摘要加密生成数字签名(Signature),将消息和数字签名一起返回给客户端。客户端收到返回后,先使用公钥对数字签名解密得到摘要,再将信息通过Hash函数生成摘要,只要这两个摘要相同则证明该信息是中途没有被篡改过的。

值得注意的是,这样也容易出现信息泄漏。因为数字签名一被更换也就不能确定是否为真实的信息发起者发出的信息了。所以为了证明服务器的数字签名是否为真实的,服务器需要将公钥进行认证,会有一个数字证书,这个证书证明了这个公钥就是这台服务器独有的。服务器返回消息的时候会将数字证书一起发送过来,客户端接收到后就可以使用证书中心声明的公钥去解开数字证书来得到这台服务器的公钥。就能判断是否为真实的公钥了。然后客户端就可以使用证书中的服务器公钥,对信息进行加密,然后与服务器交换加密信息。

Libsodium 一些常用API

 int crypto_sign_keypair(unsigned char *pk, unsigned char *sk);

crypto_sign_keypair()函数随机生成密钥和相应的公钥。公钥放入pk(crypto_sign_PUBLICKEYBYTES字节),密钥放入sk(crypt_sign_SECRETKEYBYTES)。

int crypto_box_keypair(unsigned char *pk, unsigned char *sk);

crypto_box_keypair()函数随机生成密钥和相应的公钥。公钥放入pk(crypto_box_PUBLICKEYBYTES字节),密钥放入sk(crypt_box_SECRETKEYBYTES)。

libsodium 的设计尽可能地隐藏了加密算法的实现细节,从它的函数名就可以看出来。例如包含了 X25519 密钥交换和 ChaCha20Poly1305 认证加密的相关函数统一称为 crypto_box_*

int crypto_scalarmult_curve25519_base(unsigned char *q, const unsigned char *n)

从私钥生成公钥需要用到 crypto_scalarmult_curve25519_base, curve25519 系列算法可以使用任意的 32 字节作为私钥,所以可以从密码学安全的伪随机数生成器自行生成私钥。

int crypto_sign(unsigned char *sm, unsigned long long *smlen_p,
                const unsigned char *m, unsigned long long mlen,
                const unsigned char *sk);

crypto_sign()函数使用密钥sk为长度为mlen字节的消息m添加签名。签名的消息(包括签名和未更改的消息副本)放入sm中,长度为crypto_sign_BYTES+mlen字节。

int crypto_sign_open(unsigned char *m, unsigned long long *mlen_p,
                     const unsigned char *sm, unsigned long long smlen,
                     const unsigned char *pk);

crypto_sign_open()函数检查长度为smlen字节的签名消息sm是否具有公钥pk的有效签名。如果签名无效,则函数返回-1。

Libsodium 测试例子

一个关于如何使用libnaid加密库的简单示例。这个例子也适用于几乎相同的libnacl库——只需更改include即可。

  • 加密/解密

...


void randombytes(uint8_t buffer[], unsigned long long size)
{
	int fd;

	fd = open( "/dev/urandom", O_RDONLY );
	if( fd < 0 ) {
		fprintf( stderr, "Failed to open /dev/urandom\n" );
		exit(1);
	}

	int rc;
	if( (rc = read( fd, buffer, size )) >= 0 ) {
		close( fd );
	}
}

char* to_hex( char hex[], const uint8_t bin[], size_t length )
{
	int i;
	uint8_t *p0 = (uint8_t *)bin;
	char *p1 = hex;

	for( i = 0; i < length; i++ ) {
		snprintf( p1, 3, "%02x", *p0 );
		p0 += 1;
		p1 += 2;
	}

	return hex;
}

int is_zero( const uint8_t *data, int len )
{
	int i;
	int rc;

	rc = 0;
	for(i = 0; i < len; ++i) {
		rc |= data[i];
	}

	return rc;
}



...

User *new_user(char* name)
{
	User* user;

	user = (User*) malloc(sizeof(User));
	user->name = name;

	crypto_box_keypair(user->public_key, user->secret_key);

	return user;
}

void print_user(User *user)
{
	char phexbuf[2*crypto_box_PUBLICKEYBYTES+1];
	char shexbuf[2*crypto_box_SECRETKEYBYTES+1];

	printf("username: %s\n", user->name);
	printf("public key: %s\n", to_hex(phexbuf, user->public_key, crypto_box_PUBLICKEYBYTES ));
	printf("secret key: %s\n\n", to_hex(shexbuf, user->secret_key, crypto_box_SECRETKEYBYTES ));
}

int main( int argc, char **argv )
{
	...
	rc = encrypt(encrypted, bob->public_key, eve->secret_key, nonce, msg, strlen(msg));
	if( rc < 0 ) {
		return 1;
	}
	printf("encrypted: %s\n", to_hex(hexbuf, encrypted, rc ));

	uint8_t decrypted[1000];
	rc = decrypt(decrypted, eve->public_key, bob->secret_key, nonce, encrypted, rc);
	if( rc < 0 ) {
		return 1;
	}

	decrypted[rc] = '\0';
	printf("decrypted: %s\n", decrypted);

	return 0;
}

编译运行:

  • 签名/验签
...


int sign(uint8_t sm[], const uint8_t m[], const int mlen, const uint8_t sk[]) {
	unsigned long long smlen;

	if( crypto_sign(sm,&smlen, m, mlen, sk) == 0) {
		return smlen;
	} else {
		return -1;
	}
}

int verify(uint8_t m[], const uint8_t sm[], const int smlen, const uint8_t pk[]) {
	unsigned long long mlen;

	if( crypto_sign_open(m, &mlen, sm, smlen, pk) == 0) {
		return mlen;
	} else {
		return -1;
	}
}

int main(int argc, char **argv[]) 
{
...

	int rc = crypto_sign_keypair(pk, sk);
	if(rc < 0) {
		return 1;
	}

	int smlen = sign(sm, m, mlen, sk);
	if(smlen < 0) {
		return 1;
	}

	mlen = verify(m, sm, smlen, pk);
	if(mlen < 0) {
		return 1;
	}

	printf("Verified!\n");
	return 0;
}

编译运行:

  • 密钥的派生
...

int crypto_box_recover_public_key(uint8_t secret_key[]) {
	uint8_t public_key[crypto_sign_PUBLICKEYBYTES];
	char phexbuf[2*crypto_sign_PUBLICKEYBYTES+1];

	crypto_scalarmult_curve25519_base( public_key, secret_key );

	printf("recovered public_key: %s\n", to_hex(phexbuf, public_key, crypto_sign_PUBLICKEYBYTES));
}

void crypto_box_example()
{
	uint8_t public_key[crypto_box_PUBLICKEYBYTES];
	uint8_t secret_key[crypto_box_SECRETKEYBYTES];
	char phexbuf[2*crypto_box_PUBLICKEYBYTES+1];
	char shexbuf[2*crypto_box_SECRETKEYBYTES+1];

	crypto_box_keypair(public_key, secret_key);

	printf("public_key: %s\n", to_hex(phexbuf, public_key, crypto_box_PUBLICKEYBYTES));
	printf("secret_key: %s\n", to_hex(shexbuf, secret_key,  crypto_box_SECRETKEYBYTES));

	crypto_box_recover_public_key(secret_key);
}

int crypto_sign_recover_public_key(uint8_t secret_key[]) {
	uint8_t public_key[crypto_sign_PUBLICKEYBYTES];
	char phexbuf[2*crypto_sign_PUBLICKEYBYTES+1];

	memcpy(public_key, secret_key+crypto_sign_PUBLICKEYBYTES, crypto_sign_PUBLICKEYBYTES);

	printf("recovered public_key: %s\n", to_hex(phexbuf, public_key, crypto_sign_PUBLICKEYBYTES));
}


void crypto_sign_example()
{
	uint8_t public_key[crypto_sign_PUBLICKEYBYTES];
	uint8_t secret_key[crypto_sign_SECRETKEYBYTES];
	char phexbuf[2*crypto_sign_PUBLICKEYBYTES+1];
	char shexbuf[2*crypto_sign_SECRETKEYBYTES+1];

	crypto_sign_keypair(public_key, secret_key);

	printf("public_key: %s\n", to_hex(phexbuf, public_key, crypto_sign_PUBLICKEYBYTES));
	printf("secret_key: %s\n", to_hex(shexbuf, secret_key,  crypto_sign_SECRETKEYBYTES));

	crypto_sign_recover_public_key(secret_key);
}

int main( int argc, char **argv )
{
	printf("\ncrypto_sign_example:\n");
	crypto_sign_example();

	printf("\ncrypto_box_example:\n");
	crypto_box_example();
}

编译运行

实践:发送/接收可验证的单个加密消息

gen_keypair将创建单个密钥对 。需要两个密钥对才能发送经过验证的消息。

gen_keypair:

...

int main(int argc, char *argv[])
{
	...

	if(argc != 2) {
		fprintf(stderr, "%s: <NAME>\n", argv[0]);
		return 1;
	}

	/* Generate Key Pair */
	crypto_box_keypair(&public_key[0], &secret_key[0]);


	snprintf(filename, sizeof(filename), "%s.pub", argv[1]);
	err = savefile(filename, &public_key[0], crypto_box_PUBLICKEYBYTES);
	if(err) {
		printf("ERROR saving public key: %s\n", filename);
	} else {
		printf("Saved public key: %s\n", filename);
	}

	snprintf(filename, sizeof(filename), "%s.key", argv[1]);
	err = savefile(filename, &secret_key[0], crypto_box_SECRETKEYBYTES);
	if(err) {
		printf("ERROR saving secret key: %s\n", filename);
	} else {
		printf("Saved secret key: %s\n", filename);
	}

	return 0;
}

编译运行:

ip_receptor:

...

int main(int argc, char *argv[])
{
...

	parse_args(argc, argv);

	g_publickey = (unsigned char *)get_file(g_pfilename);
	if(!g_publickey) {
		fprintf(stderr, "get_file(%s) failed!\n", g_pfilename);
		exit(EXIT_FAILURE);
	}

	g_secretkey = (unsigned char *)get_file(g_sfilename);
	if(!g_secretkey) {
		fprintf(stderr, "get_file(%s) failed!\n", g_sfilename);
		exit(EXIT_FAILURE);
	}



	ph = pcap_open_live(g_dev, 65535, 1, 1, pcap_errbuf);
	if(!ph) {
        fprintf(stderr, "pcap_open_live(%s, 65535, 1, 1): %s\n", g_dev, pcap_errbuf);
        return 2;
	}

...

	printf("Starting capture on %s (%s)\n", (g_dev ? g_dev : "all"), "LINKTYPE");
	printf("Looking for IP Protocol %u\n", g_proto);

...

	pcap_close(ph);
	...
	printf("Packet Count: %lu\n", g_pktcount);
	return 0;
}

struct options opts[] = 
{
	{ 1, "proto",	"IP Protocol to listen for",	"p",	1 },
	{ 2, "dev",		"Interface to listen on",		"i",	1 },
	{ 3, "public",	"Public Key",					NULL, 1 },
	{ 4, "secret",	"Secret Key",					NULL, 1 },
	{ 0, NULL,		NULL,							NULL,	0 }
};

static void parse_args(int argc, char **argv)
{
	char *args;
	int c;

	while ((c = getopts(argc, argv, opts, &args)) != 0) {
		switch(c) {
			case -2:
				fprintf(stderr, "Unknown Getopts Option: %s\n", args);
				break;
			case -1:
				fprintf(stderr, "Unable to allocate memory for getopts().\n");
				exit(EXIT_FAILURE);
				break;
			case 1:
				g_proto = atoi(args);
				break;
			case 2:
				g_dev = strdup(args);
				break;
			case 3:
				g_pfilename = strdup(args);
				break;
			case 4:
				g_sfilename = strdup(args);
				break;
			default:
				fprintf(stderr, "Unexpected getopts Error! (%d)\n", c);
				break;
		}

		free(args);
	}

	if(g_proto > 255) {
		fprintf(stderr, "I need a protocol! (Fix with -p)\n");
		exit(EXIT_FAILURE);
	}

	if(!g_pfilename) {
		fprintf(stderr, "I need a Public Key! (Fix with --public)\n");
		exit(EXIT_FAILURE);
	}

	if(!g_sfilename) {
		fprintf(stderr, "I need a Secret Key! (Fix with --secret)\n");
		exit(EXIT_FAILURE);
	}
}

ip_knock:

...

int main(int argc, char *argv[])
{
...

	parse_args(argc, argv);

	publickey = (unsigned char *)get_file(g_pfilename);
	if(!publickey) {
		fprintf(stderr, "get_file(%s) failed!\n", g_pfilename);
		exit(EXIT_FAILURE);
	}

	secretkey = (unsigned char *)get_file(g_sfilename);
	if(!secretkey) {
		fprintf(stderr, "get_file(%s) failed!\n", g_sfilename);
		exit(EXIT_FAILURE);
	}


	l = libnet_init(LIBNET_RAW4, g_dev, errbuf);
	if(l == NULL) {
		fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
		exit(EXIT_FAILURE);
	}



	dst_ip = libnet_name2addr4(l, g_dst, LIBNET_RESOLVE);
	if(dst_ip == -1) {
		fprintf(stderr, "Bad destination IP address: %s\n", g_dst);
		exit(EXIT_FAILURE);
	}

	src_ip = libnet_get_ipaddr4(l);
	if(src_ip == -1) {
		fprintf(stderr, "Couldn't get own IP address: %s\n", libnet_geterror(l));
		exit(EXIT_FAILURE);
	} else {
		printf("Using: %s\n", libnet_addr2name4(src_ip, LIBNET_DONT_RESOLVE));
	}

...
	libnet_destroy(l);
	return 0;
}

struct options opts[] = 
{
	{ 1, "dst",		"Destination",	"d",  1 },
	{ 2, "proto",	"Protocol",		"p",  1 },
	{ 3, "dev",		"Device",		"i",  1 },
	{ 4, "public",	"Public Key",	NULL, 1 },
	{ 5, "secret",	"Secret Key",	NULL, 1 },
	{ 6, "message",	"Message",		"m",  1 },
	{ 0, NULL,		NULL,			NULL, 0 }
};

...

运行结果:
ip_receptor:

ip_knock:
在这里插入图片描述
tcpdump抓包:

ip_knock将发送一个用ip封装的加密消息,协议值由你选择。

ip_receptor将监听具有特定ip协议值的加密消息,并使用提供的密钥对其进行认证。

验证消息后,ip_receptor将默认对消息运行系统命令。

If you need the complete source code of xxd, add your WeChat number (c17865354792)

总结

使用libsodium库头文件sodium.h 是唯一需要包含的头文件。编译时库的名字是 sodium (使用 -lsodium 来链接)。Libsodium 是一个用于加密,解密,数字签名,密码哈希,等的现代的易用的密码学库。更多了解,请参考官网文档。

Welcome to follow WeChat official account【程序猿编码

参考:
1.https://doc.libsodium.org/installation#integrity-checking
2.https://learnku.com/articles/54114
3.https://www.itcodet.com/cpp/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/40678.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java每日一练(4)

java每日一练(4) 文章目录单选部分不定项选择题多选题编程题单选部分 1.下列与队列结构有关联的是&#xff08;&#xff09; A 函数的递归调用   B 数组元素的引用   C 多重循环的执行   D 先到先服务的作业调度 队列的特点 &#xff1a; 先进先出 , 所以 答案非常明显 D  …

【python】通过gitlab v4版本api接口获取所有项目代码示例

目录一、环境信息二、参数说明三、脚本使用说明1. 使用python2运行git.py2. python脚本执行完毕会自动生成如下四个文件3. 其他脚本说明四、脚本源码1. git.py2. update.sh五、脚本扩展说明附录一、环境信息 脚本适用于&#xff1a;python2 测试版本&#xff1a;2.7.18 二、…

于我来说,赌才是世界杯的灵魂~

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 先看这里前言了解足球首看世界杯再看世界杯前言 身边朋友也有踢球的&#xff0c;但是不多。就两个&#xff0c;一个是我同学&#xff0c;一个是我同事…打篮球的倒是不少&#xff0c;猜想…

这五个适合上班族的副业你知道多少

第二职业赚钱的路子有什么&#xff1f;从理论上讲&#xff0c;第二职业就是一个创业的过程&#xff0c;也遵照自主创业一般规律。可是第二职业是在业余时间和没有灵活运用资源挣钱&#xff0c;和创业有所不同。第二职业门坎变低&#xff0c;更比较发达&#xff0c;因此今天小编…

Arduino--音乐频谱

本文主要介绍基于Arduino实现的音乐频谱显示&#xff0c;音乐频谱原理就是声音传感器&#xff08;MIC&#xff09;接收音频信号后通过FFT将时域信号转换成频域信号&#xff0c;再将音频信号频域分量分别显示在对应的LED点阵屏上&#xff0c;呈现出音乐随频律动的感觉&#xff0…

Windows Docker Desktop安装K8S

Docker DeskTop提供了K8S支撑&#xff0c;安装也较为简单。对于本地开发&#xff0c;测试部署项目较为方便。下面进行简单说明。 DockerDesktop配置镜像源&#xff0c;较为简单&#xff0c;有许多网上例子直接参考即可。启用K8S等待一阵子&#xff0c;K8S即可安装成功。可以看…

MySQL存储引擎介绍

首先 我们要知道 什么是引擎 我们常见的 客机 直升机 火箭等等 他们都有自己的引擎 引擎也就是指一个机器的核心 当然 你如果是一个飞机 那你自然是不能用火箭的引擎的 存储引擎就是存储数据 建立索引 更新/查询数据等技术的实现方式&#xff0c;存储引擎是基于表的&#xf…

TDK | CeraLink 电容器快速切换逆变器的革新

本周向大家介绍另一款压电技术的产品CeraLink。 CeraLink 是一系列非常紧凑的电容器&#xff0c;用于稳定直流链路中的电压。因此它们适合用作缓冲器或直流母线电容器。这些产品基于 PLZT 陶瓷&#xff0c;旨在为工程师提供针对快速开关转换器、空间要求非常紧凑的转换器和需要…

Java项目:饰品商城系统(java+SSM+JSP+javascript+jQuery+Mysql)

源码获取&#xff1a;俺的博客首页 "资源" 里下载&#xff01; 项目介绍 本项目分为前台与后台&#xff0c;有普通用户与管理员两种角色&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录,用户管理,一级分类管理,二级分类管理,饰品管理,订单管理、发货、…

Linux中对磁盘(硬盘)分区和挂载

记录&#xff1a;346 场景&#xff1a;在CentOS 7.9操作系统上&#xff0c;使用fdisk对磁盘分区&#xff1b;使用mkfs.xfs创建文件系统&#xff1b;使用mount挂载磁盘到目录&#xff1b;使用umount卸载目录已挂载的磁盘&#xff1b;修改文件系统表fstab&#xff0c;满足开机启…

【计算机毕业设计】网上游戏代练商城系统

一、系统截图&#xff08;需要演示视频可以私聊&#xff09; 摘要 随着当今社会的发展&#xff0c;时代的进步&#xff0c;各行各业也在发生着变化&#xff0c;本系统健身房这一方面&#xff0c;利用网站游戏代练已经逐步进入人们的生活。传统的网上游戏代练&#xff0c;都是用…

前端基础入门

HTML的基本概念 WWW&#xff08;World Wide Web&#xff0c;万维网&#xff09;是一种建立在Internrt上的、全球性的、交互的、多平台的、分布式的信息资源网络。它采用HTML语言描述超文本&#xff08;Hypertext&#xff09;文件。这里所说的超文本文件指的是包含链接关系和多…

数据结构与算法中的图

数据结构与算法中的图 图的定义与术语 在线性结构之间&#xff0c;数据元素之间满足唯一的线性关系。每个数据元素&#xff08;除第一个和最后一个外&#xff09;只有一个****直接前趋和直接后继** 在树形结构中&#xff0c;数据元素之间有着明显的层次关系&#xff0c;并且每…

实验四、R_b变化对Q点和电压放大倍数的影响 ​

一、题目 研究 RbR_bRb​ 变化对 QQQ 点和 A˙u\dot A_uA˙u​ 的影响。 二、仿真电路 仿真电路如图1(b)、(ccc)所示。晶体管采用 FMMT5179。其datasheet的相关参数如下&#xff1a;(a)FMMT5179的参数(a)\textrm{FMMT5179}的参数(a)FMMT5179的参数 (b)Rb为3MΩ时的情况(b)R…

RDD——Action算子

常用Action算子 countByKeycollectreducefoldfirsttaketopcounttakeSampletakeOrderedforeachsaveAsTextFile countByKey() 返回值是一个 字典类型 ## 读取文本文件 rdd1 sc.textFile(f"file:///{ROOT}/data/input/words.txt") rdd2 rdd1.flatMap(lambda x:x.spl…

【数字信号去噪】小波软阈值+硬阈值+改进阈值数字信号去噪【含Matlab源码 1025期】

⛄一、小波语音降噪简介 对于噪声频谱遍布于语音信号频谱之中的宽带噪声&#xff0c;如果噪声振幅比大部分的语音信号振幅低&#xff0c;则削去低幅度成分也就削去了宽带噪声。基于这种思路&#xff0c;可以在频域中采取中心限幅的方法&#xff0c;即让带噪语音信号通过一限幅…

实战关于登录框的渗透测试

前言 登录框可以说是我们漏洞挖掘中最常见的点&#xff0c;漏洞的种类也是相当的多&#xff0c;相信大家在登录框中发现的漏洞数不胜数。 主要有以下这些漏洞 弱口令 SQL注入 水平越权 垂直越权 逻辑漏洞 短信轰炸 邮箱轰炸 信息泄露SQL注入 看到登录框&#xff0c;输入信…

C++智能指针之weak_ptr

C智能指针之weak_ptr前言weak_ptr总结前言 在C中&#xff0c;动态内存的申请和释放是通过运算符&#xff1a;new 和 delete 进行管理的。其中 new 负责申请内存&#xff0c;delete负责释放内存。 动态内存的使用很容易出现问题&#xff0c;这主要在于你需要保证在正确的时间释放…

【MAX78000基础案例演示】

【MAX78000基础案例演示】1. MAX78000配套软硬件1.1 硬件1.2 软件1.3 demo例程2. ADC2.1 描述2.2 设置2.3 输出3. GPIO3.1 描述3.2 设置3.3 输出4. UART4.1 描述4.2 设置4.3 输出6.小结1. MAX78000配套软硬件 有关使用 SDK 的详细信息&#xff0c;请参阅MAX78000 评估套件入门…

(附源码)计算机毕业设计Java巴音学院学生资料管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven Vue 等等组成&#xff0c;B/…