程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<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…...

kamailio-ACC模块介绍【kamailio6.0. X】
Acc 模块 作者 Jiri Kuthan iptel.org jiriiptel.org Bogdan-Andrei Iancu Voice Sistem SRL bogdanvoice-system.ro Ramona-Elena Modroiu rosdev.ro ramonarosdev.ro 编辑 Bogdan-Andrei Iancu Voice Sistem SRL bogdanvoice-system.ro Sven Knoblich 1&1 Internet …...

数据库对象
数据库对象 数据库对象是构成数据库结构的基本单位,它们定义了数据库存储的数据类型、数据的组织方式以及数据之间的关系。在数据库中,对象可以包括表,视图,索引,触发器,存储过程,函数等多种类…...

EtherCAT主站IGH-- 27 -- IGH之globals.h文件解析
EtherCAT主站IGH-- 27 -- IGH之globals.h文件解析 0 预览一 该文件功能宏定义数据结构打印宏三 h文件翻译四 c文件翻译该文档修改记录:总结0 预览 一 该文件功能 该文件包含了一些全局定义和宏,用于 IgH EtherCAT 主站(EtherCAT Master)的实现。包括了一些超时设定、宏定义…...

2025多目标优化创新路径汇总
多目标优化是当下非常热门且有前景的方向!作为AI领域的核心技术之一,其专注于解决多个相互冲突的目标的协同优化问题,核心理念是寻找一组“不完美但均衡”的“帕累托最优解”。在实际中,几乎处处都有它的身影。 但随着需求场景的…...

15JavaWeb——Maven高级篇
Maven高级 Web开发讲解完毕之后,我们再来学习Maven高级。其实在前面的课程当中,我们已经学习了Maven。 我们讲到 Maven 是一款构建和管理 Java 项目的工具。经过前面 10 多天 web 开发的学习,相信大家对于 Maven 这款工具的基本使用应该没什…...

使用Ollama本地化部署DeepSeek
1、Ollama 简介 Ollama 是一个开源的本地化大模型部署工具,旨在简化大型语言模型(LLM)的安装、运行和管理。它支持多种模型架构,并提供与 OpenAI 兼容的 API 接口,适合开发者和企业快速搭建私有化 AI 服务。 Ollama …...

蓝桥杯刷题DAY1:前缀和
所谓刷题,讲究的就是细心 帕鲁服务器崩坏【算法赛】 “那个帕鲁我已经观察你很久了,我对你是有些失望的,进了这个营地,不是把事情做好就可以的,你需要有体系化思考的能力。” 《幻兽帕鲁》火遍全网,成为…...

【基于SprintBoot+Mybatis+Mysql】电脑商城项目之用户注册
🧸安清h:个人主页 🎥个人专栏:【计算机网络】【Mybatis篇】 🚦作者简介:一个有趣爱睡觉的intp,期待和更多人分享自己所学知识的真诚大学生。 目录 🎯项目基本介绍 🚦项…...

MINIRAG: TOWARDS EXTREMELY SIMPLE RETRIEVAL-AUGMENTED GENERATION论文翻译
感谢阅读 注意不含评估以后的翻译原论文地址标题以及摘要介绍部分MiniRAG 框架2.1 HETEROGENEOUS GRAPH INDEXING WITH SMALL LANGUAGE MODELS2.2 LIGHTWEIGHT GRAPH-BASED KNOWLEDGE RETRIEVAL2.2.1 QUERY SEMANTIC MAPPING2.2.2 TOPOLOGY-ENHANCED GRAPH RETRIEVAL 注意不含评…...

微服务入门(go)
微服务入门(go) 和单体服务对比:里面的服务仅仅用于某个特定的业务 一、领域驱动设计(DDD) 基本概念 领域和子域 领域:有范围的界限(边界) 子域:划分的小范围 核心域…...