程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<3>
大家好啊,我是小象٩(๑òωó๑)۶
我的博客:Xiao Xiangζั͡ޓއއ
很高兴见到大家,希望能够和大家一起交流学习,共同进步。
今天我们来对上一节做一些小补充,了解学习一下assert断言,指针的使用和传址调用等…
目录
- 一、assert 断言
- 二、指针的使用和传址调用
- 2.1 strlen的模拟实现
- 2.2 传值调用和传址调用
一、assert 断言
assert 断言是一种在程序中用于检查某个条件是否为真的语句。它基于这样的假设:在程序的某个特定点上,某个条件应该始终成立。如果这个条件确实为真,程序将继续正常执行;但如果条件为假,断言就会失败,通常会导致程序抛出一个AssertionError 异常,从而中断程序的执行。这有助于快速定位程序中的错误,尤其是在开发和测试阶段。
assert.h 头文件定义了宏 assert() ,用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行。这个宏常常被称为“断言”。
作用:
调试辅助:帮助开发者快速定位程序中的逻辑错误。例如,在一个计算平均值的函数中,可以使用断言来确保传入的列表不为空,因为计算空列表的平均值是没有意义的,这能让开发者在函数被错误调用时迅速发现问题。
代码契约:在编写函数或方法时,断言可以用于定义函数的前置条件和后置条件,形成一种代码契约。比如,一个除法函数,使用断言可以确保除数不为零这个前置条件,明确函数的使用规则,也让其他阅读和使用代码的人清楚函数的约束条件。
测试验证:在测试用例中,断言是验证测试结果是否符合预期的重要手段。比如在单元测试中,使用断言来检查函数的返回值是否等于预期值,或者检查某个对象的属性是否处于特定状态,从而判断测试是否通过。
assert(p != NULL);
上面代码在程序运行到这一行语句时,验证变量 p 是否等于 NULL 。如果确实不等于 NULL ,程序继续运行,否则就会终止运行,并且给出报错信息提示。
assert() 宏接受⼀个表达式作为参数。如果该表达式为真(返回值⾮零), assert() 不会产生任何作用,程序继续运行。如果该表达式为假(返回值为零), assert() 就会报错,在标准错误流 stderr 中写入一条错误信息,显示没有通过的表达式,以及包含这个表达式的文件名和行号。
assert() 的使用对程序员是非常友好的,使用assert()
有几个好处:它不仅能自动标识文件和出问题的行号,还有一种无需更改代码就能开启或关闭 assert()的机制。如果已经确认程序没有问题,不需要再做断言,就在 #include <assert.h> 语句的前面,定义⼀个宏 NDEBUG 。
#define NDEBUG
#include <assert.h>
然后,重新编译程序,编译器就会禁用文件中所有的 assert() 语句。如果程序又出现问题,可以移除这条 #define NDEBUG 指令(或者把它注释掉),再次编译,这样就重新启用了 assert() 语句。
assert() 的缺点是,因为引入了额外的检查,增加了程序的运行时间。
一般我们可以在 Debug 中使用,在 Release 版本中选择禁用 assert 就行,在 VS 这样的集成开发环境中,在 Release 版本中,直接就是优化掉了。这样在debug版本写有利于程序员排查问题,在 Release 版本不影响用户使用时程序的效率。
二、指针的使用和传址调用
2.1 strlen的模拟实现
库函数strlen的功能是求字符串长度,统计的是字符串中 \0 之前的字符的个数。
函数原型如下:
size_t strlen ( const char * str );
参数str接收一个字符串的起始地址,然后开始统计字符串中 \0 之前的字符个数,最终返回长度。
如果要模拟实现只要从起始地址开始向后逐个字符的遍历,只要不是 \0 字符,计数器就+1,这样直到 \0 就停止。
举个例子:
int my_strlen(const char* str)
{int count = 0;assert(str);while (*str){count++;str++;}return count;
}
int main()
{int len = my_strlen("abcdef");printf("%d\n", len);return 0;
}
2.2 传值调用和传址调用
学习指针的目的是使用指针解决问题,那什么问题,非指针不可呢?
例如:写一个函数,交换两个整型变量的值
我们可能写出这样的代码:
#include <stdio.h>
void Swap1(int x, int y)
{int tmp = x;x = y;y = tmp;
}
int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);printf("交换前:a=%d b=%d\n", a, b);Swap1(a, b);printf("交换后:a=%d b=%d\n", a, b);return 0;
}
我们发现其实没产生交换的效果,这是为什么呢?
在这个代码里,Swap1 函数采用的是传值调用。传值调用的特点是,函数接收的是实参的副本,而不是实参本身。具体来说:
在 main 函数中调用 Swap1(a, b) 时,a 和 b 的值会被复制一份,分别传递给 Swap1 函数的形参 x 和 y。
在 Swap1 函数内部,虽然 x 和 y 的值进行了交换,但这只是对副本的操作,并不会影响到 main 函数中原始的 a 和 b 的值。
结论:实参传递给形参的时候,形参会单独创建一份临时空间来接收实参,对形参的修改不影响实参。
所以Swap1是失败的了。
那怎么办呢?
我们现在要解决的就是当调用Swap函数的时候,Swap函数内部操作的就是main函数中的a和b,直接将a和b的值交换了。那么就可以使用指针了,在main函数中将a和b的地址传递给Swap函数,Swap函数里边通过地址间接的操作main函数中的a和b,并达到交换的效果就好了。
像这样:
#include <stdio.h>
void Swap2(int* px, int* py)
{int tmp = 0;tmp = *px;*px = *py;*py = tmp;
}
int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);printf("交换前:a=%d b=%d\n", a, b);Swap2(&a, &b);printf("交换后:a=%d b=%d\n", a, b);return 0;
}
我们可以看到实现成Swap2的方式,顺利完成了任务,这里调用Swap2函数的时候是将变量的地址传递给了函数,这种函数调用方式叫:传址调用。
传址调用,可以让函数和主调函数之间建立真正的联系,在函数内部可以修改主调函数中的变量;所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采用传值调用。如果函数内部要修改主调函数中的变量的值,就需要传址调用。
好了,这节内容便结束了,附上这两节所写的代码:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//int main()
//{
// int a = 10;
// //int* pa = &a;//int*
// //char* pc = &a;
// void* pv2 = &a;
// //*pv2;
// //pv2++;
//
// char ch = 'w';
// //char*pc = &ch;
// //int* pi = &ch;
// void* pv = &ch;//char*
// //*pv;//err
// return 0;
//}//void test(void* pv)
//{
// //pv在使用的时候,会强制类型转换,然后去使用
// //
//}
//
//
//int main()
//{
// int a = 10;
// test(&a);
// char c = 'w';
// test(&c);
// double d = 3.14;
// test(&d);
//
// return 0;
//}//int main()
//{
// int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
// int i = 0;
// int sz = sizeof(arr) / sizeof(arr[0]);//10
// int* p = &arr[0];
//
// //for (i = 0; i < sz; i++)
// //{
// // *(p + i) = 0;
// //}
//
// for (i = 0; i < sz; i++)
// {
// scanf("%d", p + i);
// }
//
//
// for (i = 0; i < sz; i++)
// {
// printf("%d ", *(p + i));
// }
//
// return 0;
//}//int main()
//{
// int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
// int i = 0;
// int sz = sizeof(arr) / sizeof(arr[0]);//10
// int* p = &arr[sz-1];
//
// for (i = 0; i < sz; i++)
// {
// *(p - i) = i+1;
// }
//
// for (i = 0; i < sz; i++)
// {
// printf("%d ", arr[i]);
// }
//
// return 0;
//}
//
//int main()
//{
// int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
// int i = 0;
// int sz = sizeof(arr) / sizeof(arr[0]);//10
// int* p = &arr[sz - 1];
//
// for (i = 0; i < sz; i++)
// {
// *p = i + 1;
// p--;
// }
//
// for (i = 0; i < sz; i++)
// {
// printf("%d ", arr[i]);
// }
//
// return 0;
//}//int main()
//{
// int arr[10] = { 0 };
// int n = &arr[0] - &arr[9];
// printf("%d\n", n);
//
// return 0;
//}//int main()
//{
// int arr[10] = { 0 };
// char ch[5] = {0};
// printf("%d\n", & arr[9] - &ch[0]);//err
//
// return 0;
//}#include <string.h>
//strlen 是求字符串长度,统计的是字符串中\0之前的字符个数//int main()
//{
// char arr[] = "abcdef";
// //a b c d e f \0
// //数组名其实是数组首元素的地址
// //arr == &arr[0]
// size_t len = strlen(arr);//6
// printf("%zd\n", len);
//
// return 0;
//}//size_t my_strlen(char* str)
//{
// size_t count = 0;
// while (*str != '\0')
// {
// count++;
// str++;
// }
// return count;
//}
//
//size_t my_strlen(char* str)
//{
// char* start = str;
// while (*str != '\0')
// str++;
// return str - start;//指针-指针
//}
//
//
//int main()
//{
// char arr[] = "abcdefghi";
// //a b c d e f \0
// //数组名其实是数组首元素的地址
// //arr == &arr[0]-- char*
// size_t len = my_strlen(arr);//6
// printf("%zd\n", len);
//
// return 0;
//}
////
//int main()
//{
// int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
// int sz = sizeof(arr) / sizeof(arr[0]);
// int* p = arr;
// while (p < &arr[sz])
// {
// printf("%d ", *p);
// p++;
// }
//
//
//
// //int i = 0;
// //int* p = arr;//p = &arr[0];
// //for (i = 0; i < sz; i++)
// //{
// // printf("%d ", *(p + i));
// //}
//
//
// //for (i = 0; i < sz; i++)
// //{
// // printf("%d ", arr[i]);
// //}
//
//
// return 0;
//}//const 是常属性-不能改变的属性//int main()
//{
// const int a = 10;
// //a变成了常变量,a的本质还是变量,但是因为被const修饰,所以不能改变
// //a = 1;//err
// int* p = &a;
// *p = 1;
//
// printf("%d\n", a);
//
// return 0;
//}//int main()
//{
// const int n = 10;
// int arr[n];
//
// return 0;
//}//int main()
//{
// const int a = 10;
// //a变成了常变量,a的本质还是变量,但是因为被const修饰,所以不能改变
// //a = 1;//err
// const int* p = &a;
// *p = 1;
//
// printf("%d\n", a);
//
// return 0;
//}//const 修饰指针变量
//可以放在*的左边,也可以放在*的右边,意义是不一样的
//const 放在*的左边表示指针指向的内容,不能通过指针来改变了,但是指针变量本身是可以改变的
//const 放在*的右边表示指针变量本身不能被修改了,但是指针指向的内容是可以通过指针变量来改变的//int main()
//{
// int a = 100;
// int b = 1000;
// int* const p = &a;
// *p = 0;//ok
// p = &b;//err
//
// return 0;
//}//int main()
//{
// int a = 100;
// int b = 1000;
// const int * p = &a;
// //*p = 0;//err
// p = &b;
//
// return 0;
//}//int main()
//{
// int a = 100;
// int b = 1000;
//
// const int* const p = &a;
//
// return 0;
//}
//
//
//
////int main()
//{
// int* p;//局部变量不初始化的时候,里边放的是随机值
//
// *p = 20;//非法访问,p就是野指针
//
// return 0;
//}//int main()
//{
// int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
// int* p = arr;
// int sz = sizeof(arr) / sizeof(arr[0]);
// int i = 0;
// for (i = 0; i <= sz; i++)
// {
// *p = i;
// p++;
// }
//
// return 0;
//}
////
//int* test()
//{
// int n = 100;
// return &n;
//}
//
//int main()
//{
// int* p = test();
// printf("%d\n", *p);
//
// return 0;
//}//
//int main()
//{
// int* p = NULL;
// if(p != NULL)
// *p = 200;
//
// return 0;
//}
//#define NDEBUG
#include <assert.h>//int main()
//{
// int arr[10] = { 1,2,3,4,5 };
// int* p = arr;
// assert(p != NULL);
// int i = 0;
// for (i = 0; i < 5; i++)
// {
// printf("%d ", *p);
// p++;
// }
// //
// return 0;
//}//int main()
//{
// int arr[10] = { 1,2,3,4,5 };
// int* p = arr;
// assert(p != NULL);
// int i = 0;
// for (i = 0; i < 5; i++)
// {
// printf("%d ", *p);
// p++;
// }
//
// return 0;
//}//int main()
//{
// int a = 5;
// assert(a != 5);
//
// return 0;
//}
//
//size_t my_strlen(const char* str)
//{
// size_t count = 0;
// assert(str != NULL);
//
// while (*str)//'\0' -- 0
// {
// count++;
// str++;
// }
// return count;
//}
//
//int main()
//{
// char arr[] = "abcdef";
// size_t len = my_strlen(arr);
// printf("%zd\n", len);
//
// return 0;
//}//void Swap1(int x, int y)
//{
// int z = 0;
// z = x;
// x = y;
// y = z;
//}
//
//int main()
//{
// int a = 0;
// int b = 0;
// scanf("%d %d", &a, &b);
// //写一个函数,交换a和b的内容
//
// printf("交换前:a = %d b = %d\n", a, b);
// Swap1(a, b);
// printf("交换后:a = %d b = %d\n", a, b);
//
// return 0;
//}//int main()
//{
// int a = 10;
// int* p = &a;
// *p = 20;
// return 0;
//}//void Swap2(int* pa, int* pb)
//{
// int z = 0;
// z = *pa;//z = a
// *pa = *pb;//a = b
// *pb = z;//b = z
//}
//
//int main()
//{
// int a = 0;
// int b = 0;
// scanf("%d %d", &a, &b);
// //写一个函数,交换a和b的内容
//
// printf("交换前:a = %d b = %d\n", a, b);
// Swap2(&a, &b);
// printf("交换后:a = %d b = %d\n", a, b);
//
// return 0;
//}//int Add(int x, int y)
//{
// return x + y;
//}
//
//int main()
//{
// int a = 3;
// int b = 5;
// int r = Add(a, b);//传值调用
// printf("%d\n", r);
// return 0;
//}// 3 5
int Max(int x, int y)
{return (x > y ? x : y);
}int main()
{int a = 3;int b = 5;int r = Max(a, b);//传值调用printf("%d\n", r);return 0;
}
相关文章:

程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<3>
大家好啊,我是小象٩(๑ω๑)۶ 我的博客:Xiao Xiangζั͡ޓއއ 很高兴见到大家,希望能够和大家一起交流学习,共同进步。 今天我们来对上一节做一些小补充,了解学习一下assert断言,指针的使用和传址调用…...

(三)QT——信号与槽机制——计数器程序
目录 前言 信号(Signal)与槽(Slot)的定义 一、系统自带的信号和槽 二、自定义信号和槽 三、信号和槽的扩展 四、Lambda 表达式 总结 前言 信号与槽机制是 Qt 中的一种重要的通信机制,用于不同对象之间的事件响…...

Qt 5.14.2 学习记录 —— 이십이 QSS
文章目录 1、概念2、基本语法3、给控件应用QSS设置4、选择器1、子控件选择器2、伪类选择器 5、样式属性box model 6、实例7、登录界面 1、概念 参考了CSS,都是对界面的样式进行设置,不过功能不如CSS强大。 可通过QSS设置样式,也可通过C代码…...

Hot100之哈希
1两数之和 题目 思路解析 解法1--两次循环 解法2--哈希表一次循环 代码 解法1--两次循环 class Solution {public int[] twoSum(int[] nums, int target) {int nums1[] new int[2];int length nums.length;for (int i 0; i < length; i) {for (int j i 1; j < …...
油漆面积——蓝桥杯
1.题目描述 X 星球的一批考古机器人正在一片废墟上考古。 该区域的地面坚硬如石、平整如镜。 管理人员为方便,建立了标准的直角坐标系。 每个机器人都各有特长、身怀绝技。它们感兴趣的内容也不相同。 经过各种测量,每个机器人都会报告一个或多个矩…...
深度解析:网站快速收录与服务器性能的关系
本文转自:百万收录网 原文链接:https://www.baiwanshoulu.com/37.html 网站快速收录与服务器性能之间存在着密切的关系。服务器作为网站运行的基础设施,其性能直接影响到搜索引擎对网站的抓取效率和收录速度。以下是对这一关系的深度解析&am…...

925.长按键入
目录 一、题目二、思路三、解法四、收获 一、题目 你的朋友正在使用键盘输入他的名字 name。偶尔,在键入字符 c 时,按键可能会被长按,而字符可能被输入 1 次或多次。 你将会检查键盘输入的字符 typed。如果它对应的可能是你的朋友的名字&am…...
JavaScript 中的 var 和 let :关键区别与使用建议
在 JavaScript 开发中,变量声明是基础且重要的部分。 var 和 let 都是用于声明变量的关键字,但它们在作用域、变量提升、重复声明等方面存在显著差异。本文将详细探讨它们的区别,并给出使用建议。 1. 作用域 1.1 var 的作用域 …...
寒假刷题Day19
一、923. 三数之和的多种可能 class Solution { public:int threeSumMulti(vector<int>& arr, int target) {const int MOD 1000000007; // 正确的模数long long ans 0; // 使用 long long 防止溢出std::sort(arr.begin(), arr.end());for (size_t i 0; i < a…...
写好简历的三个关键认知
在当今竞争激烈的就业市场中,一份优秀的简历往往是敲开理想企业大门的第一把钥匙。然而,很多求职者在写简历时往往不得要领,让宝贵的机会从指间溜走。今天,让我们一起探讨如何提升简历写作水平,让你的职业之路走得更顺…...

工具的应用——安装copilot
一、介绍Copilot copilot是一个AI辅助编程的助手,作为需要拥抱AI的程序员可以从此尝试进入,至于好与不好,应当是小马过河,各有各的心得。这里不做评述。重点在安装copilot的过程中遇到了一些问题,然后把它总结下&…...
Koa 基础篇(二)—— 路由与中间件
let app new Koa() router.get(“/”,async ctx > { ctx.body “hello koa router” }) app.use(router.routes()) app.use(router.allowedMethods()) app.listen(3000) 运行项目,在浏览器访问本地3000端口,在页面上就会看到输出的语句。这就…...

帆软 FCA -业务分析师认证学习
帆软 FCA -业务分析师认证学习 认证概述 适合人群 企业中有需求管理、指标梳理、业务逻辑梳理、项目规划等需求的人员,想提升综合数据能力、推进数据应用落地的业务/IT骨干。 具体-FCA-业务分析理论 考试要求: FCA-业务分析理论考试- 费用:…...
Miniconda 安装及使用
文章目录 前言1、Miniconda 简介2、Linux 环境说明2.1、安装2.2、配置2.3、常用命令2.4、常见问题及解决方案 前言 在 Python 中,“环境管理”是一个非常重要的概念,它主要是指对 Python 解释器及其相关依赖库进行管理和隔离,以确保开发环境…...
solidity高阶 -- Eth支付
在区块链的世界里,智能合约是实现去中心化应用(DApp)的核心技术之一。Solidity 是一种专门用于编写以太坊智能合约的编程语言,它可以帮助开发者构建各种功能,包括支付功能。 今天,我们就来探讨如何使用 Sol…...
深入理解Java中的String
前言 在Java中,String类是一个非常重要的内置类,用于处理字符串数据。字符串是不可变的(immutable),这意味着一旦创建,字符串的内容不能被修改。作为Java中最为基础和常用的类之一,String类在内…...

洛谷 P1734 最大约数和 C语言
P1734 最大约数和 - 洛谷 | 计算机科学教育新生态 题目描述 选取和不超过 S 的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大。 输入格式 输入一个正整数 S。 输出格式 输出最大的约数之和。 输入输出样例 输入 #1复制 …...

Golang 执行流程分析
文章目录 1. 编译和运行2. 编译和运行说明 1. 编译和运行 如果是对源码编译后,再执行,Go的执行流程如下图 如果我们是对源码直接 执行 go run 源码,Go的执行流程如下图 两种执行流程的方式区别 如果先编译生成了可执行文件,那么…...

python学opencv|读取图像(五十一)使用修改图像像素点上BGR值实现图像覆盖效果
【1】引言 前序学习了图像的得加方法,包括使用add()函数直接叠加BGR值、使用bitwise()函数对BGR值进行按位计算叠加和使用addWeighted()函数实现图像加权叠加至少三种方法。文章链接包括且不限于: python学opencv|读取图像(四十二ÿ…...
Flask数据的增删改查(CRUD)_flask删除数据自动更新
查询年龄小于17的学生信息 Student.query.filter(Student.s_age < 17) students Student.query.filter(Student.s_age.__lt__(17))模糊查询,使用like,查询姓名中第二位为花的学生信息 like ‘_花%’,_代表必须有一个数据,%任何数据 st…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...