【C语言】深度剖析指针(三):回调机制、通用排序与数组指针逻辑

文章目录

  • 一、回调函数:通过函数指针实现灵活调用
    • 1.1 什么是回调函数?
    • 1.2 回调函数的实际应用:简化计算器代码
  • 二、qsort函数
    • 2.1 qsort函数的参数说明
    • 2.2 使用qsort排序整型数据
    • 2.3 使用qsort排序结构体数据
      • 示例:学生信息排序
    • 2.4 qsort函数的模拟实现:用冒泡排序理解通用排序
      • 2.4.1 模拟实现思路
        • 1. `main()`主程序:
        • 2. `bubble_sort()`
        • 3.`swap():`
      • 2.4.2 模拟实现代码
  • 三、`sizeof`与`strlen`
    • 3.1 sizeof:计算内存大小
    • 3.2 strlen:计算字符串长度
    • 3.3 核心区别对比
  • 四、数组与指针笔试题解析
    • 4.1 一维数组:数组名
    • 4.2 字符数组:`\0`
      • 代码1:无`\0`的字符数组(`sizeof`解析)
      • 代码2:无`\0`的字符数组(`strlen`解析)
      • 代码3:含`\0`的字符数组(字符串)
      • 代码4:字符指针指向常量字符串
    • 4.3 二维数组:行地址与元素地址的嵌套
    • 4.4 指针运算
      • 题目1:数组地址的跨越
      • 题目2:结构体指针的算术运算
      • 题目3:逗号表达式与数组初始化
      • 题目4:二维数组与指针偏移差异
      • 题目5:数组地址与元素地址的转换
      • 题目6:指针数组的偏移
      • 题目7:三级指针的嵌套访问

一、回调函数:通过函数指针实现灵活调用

1.1 什么是回调函数?

回调函数是一种特殊的函数调用方式:当一个函数的指针(地址)作为参数传递给另一个函数,并且这个指针在被调用时指向的函数被执行,那么这个被执行的函数就称为回调函数

简单来说,回调函数不是由函数本身直接调用,而是在特定条件下由其他函数通过指针触发,用于响应特定事件或完成特定逻辑

1.2 回调函数的实际应用:简化计算器代码

以计算器程序为例,传统实现中,switch语句需要重复处理输入操作数、调用计算函数、输出结果等逻辑,代码冗余度高。

改造前(传统写法)

int add(int a, int b){return a + b;}
int sub(int a, int b){return a - b;}
int mul(int a, int b){return a * b;}
int div(int a, int b){return a / b;}int main()
{int x, y;int input = 1;int ret = 0;do{printf("******************");printf(" ******0:退出 *****");printf(" ******1:add *****");printf(" ******2:sub *****");printf(" ******3:mul *****");printf(" ******4:div *****");printf(" **** 请选择:*****");scanf("%d", &input);switch (input){case 1:printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = add(x, y);printf("ret = %d\n", ret);break;case 2:printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = sub(x, y);printf("ret = %d\n", ret);break;case 3:printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = mul(x, y);printf("ret = %d\n", ret);break;case 4:printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = div(x, y);printf("ret = %d\n", ret);break;case 0:printf("退出程序\n");break;}} while (input != 0);return 0;
}

我们发现每个case下都有大量的重复语句,可以把这些语句封装成函数calc,提高代码的可利用性。但是每个case下调用的函数不一样,不能直接用一个函数笼统表示。这就需要利用函数指针,把需要调用的函数作为参数传给calc

改造后(回调函数版)

// 定义通用计算函数,接收函数指针作为参数
void calc(int(*pf)(int, int)) {int x, y, ret;printf("输入操作数:");scanf("%d %d", &x, &y);ret = pf(x, y);  // 通过函数指针调用回调函数printf("ret = %d\n", ret);
}// 主函数中直接通过函数名调用
case 1: calc(add); break;   
case 2: calc(sub); break;  
case 2: calc(mul); break;  
case 2: calc(div); break;  

优势:将重复的输入输出逻辑封装到calc函数中,通过传递不同的计算函数指针(add/sub等)实现灵活调用,减少代码冗余,提高可维护性。

二、qsort函数

qsort是C语言标准库中的快速排序函数,支持对任意类型的数据进行排序(整型、结构体等),其核心是通过回调函数定义排序规则。

2.1 qsort函数的参数说明

void qsort(void* base,        // 待排序数组的起始地址size_t nmemb,      // 数组元素个数size_t size,       // 每个元素的大小(单位:字节)int (*compar)(const void*, const void*)  // 比较函数(回调函数)
);
  • 比较函数:需用户自定义,返回值规则:
    • p1 > p2,返回正数;
    • p1 == p2,返回0;
    • p1 < p2,返回负数。

注意:

  • 使用qsort()要先包含头文件#include<stdlib.h>
  • void*是无具体类型的指针,不可以直接解引用,也不可以进行±整数的运算,必须先强制类型转换

2.2 使用qsort排序整型数据

#include <stdio.h>
#include <stdlib.h>  // qsort所在头文件int int_cmp(const void* p1, const void* p2) {// 将void*转换为int*,再解引用获取值return *(int*)p1 - *(int*)p2;
}int main() {int arr[] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 0};int n = sizeof(arr) / sizeof(arr[0]);qsort(arr, n, sizeof(int), int_cmp);for (int i = 0; i < n; i++) {printf("%d ", arr[i]);  // 输出:0 1 2 3 4 5 6 7 8 9}return 0;
}

2.3 使用qsort排序结构体数据

结构体包含多种类型成员(如姓名、年龄),可通过不同的比较函数实现按不同字段排序。

示例:学生信息排序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>  // strcmp所在头文件struct stu
{char name[20];int age;
};int cmp_stu_by_name(const void* p1, const void* p2)
{return strcmp(((struct stu*)p1)->name, ((struct stu*)p2)->name);
}int cmp_stu_by_age(const void* p1, const void* p2)
{return ((struct stu*)p1)->age - ((struct stu*)p2)->age;
}int main()
{struct stu arr[3] = { {"rare",20},{"daisy",18}, {"sivan",22} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);return 0;
}

在这里插入图片描述

注意:

  • 比较字符串大小可以用strcmp()函数,其所在的头文件是string.h
  • strcmp()是按照对应字符的ASCII码值比较的,不是比较长度。例如:"abq">"abcd"
  • 结构体中指针的访问要用->

2.4 qsort函数的模拟实现:用冒泡排序理解通用排序

qsort的核心是通用性(支持任意类型),其底层可基于冒泡、快速排序等算法实现。以下用冒泡排序模拟qsort的逻辑。

2.4.1 模拟实现思路

1. main()主程序:
  • 创建一个无序数组
  • 计算数组元素个数sz
  • 调用bubble_sort()函数
2. bubble_sort()
  • qsort()四个参数:
    • 首个地址void* base
    • 元素个数size_t sz
    • 每个元素大小size_t width
    • 自定义比较函数int(*cmp)(const void* p1 , const void* p2)
  • 调用cmp比较大小:
    • 将首个元素地址转为char*类型,因为char大小为1字节,方便不同类型传入,进行比较。
    • 这里相当于“指针 ± 整数”,指针类型是char*,所以 ± 整数都是跳过整数个字节,j*width就是跳过一个元素的长度。因此这里传入的是第jj+1个元素进行比较。
  • 调用swap 升序排序,cmp()>0则交换。这里原理同上,传入第jj+1个元素的地址。
3.swap():
  • 逐字节进行交换,循环次数就是width,每个元素的大小。

2.4.2 模拟实现代码

#include <stdio.h>
int cmp_int(const void* p1, const void* p2)
{return *((int*)p1) - *((int*)p2);
}void swap(char* buf1, char* buf2, size_t width)
{for (int i = 0; i < width; i++){char temp = *buf1;*buf1 = *buf2;*buf2 = temp;buf1++;buf2++;//逐字节交换}
}void bubble_sort(void* base,size_t sz,size_t width,int(*cmp)(const void* p1 ,const void* p2))
{for (int i = 0; i < sz - 1; i++){for (int j = 0;j < sz - 1 - i;j++){if ( cmp( (char*)base + j * width,(char*)base + ( j + 1 )*width   )> 0){swap((char*)base + j * width, (char*)base + (j + 1) * width,width);}}}
}int main()
{int arr[10] = { 1,2,7,6,9,8,10,6,4,9 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr,sz,sizeof(arr[0]),cmp_int);for (size_t i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}

三、sizeofstrlen

3.1 sizeof:计算内存大小

sizeof是C语言的操作符,其核心功能是计算变量或类型所占用的内存空间大小(单位:字节)。它的特点是:

  • 只关注内存占用,不关心内存中存储的数据
  • 操作数可以是变量,也可以是类型(如sizeof(int));
  • 对于数组名,sizeof(数组名)计算的是整个数组的大小(其他情况数组名通常表示首元素地址)。

示例代码

#include <stdio.h>
int main() {int a = 10;printf("%zd\n", sizeof(a));    // 结果:4(int类型占4字节)printf("%zd\n", sizeof a);     // 结果:4(变量名可省略括号)printf("%zd\n", sizeof(int));  // 结果:4(int类型的大小)return 0;
}

3.2 strlen:计算字符串长度

strlen是C语言标准库函数(声明于<string.h>),功能是计算字符串的长度,其核心逻辑是:

  • 从传入的地址开始,逐个字符计数,直到遇到\0停止\0不计入长度);
  • 若字符串中没有\0,会越界查找,导致结果未定义(UB)。

示例代码

#include <stdio.h>
#include <string.h>
int main() {char arr1[3] = {'a', 'b', 'c'};  // 无\0char arr2[] = "abc";             // 隐含\0printf("%d\n", strlen(arr1));    // 结果:随机值(越界查找)printf("%d\n", strlen(arr2));    // 结果:3(遇到\0停止)return 0;
}

3.3 核心区别对比

特性sizeofstrlen
本质操作符(编译期计算)库函数(运行期计算)
功能计算内存空间大小(字节)计算字符串长度(\0前字符数)
关注内容仅关心内存占用,与数据无关依赖\0结束符,无\0则越界
参数类型变量、类型、表达式等仅接受char*(字符串首地址)
返回值size_t(无符号整数)size_t(无符号整数)

sizeof()不会计算括号内的表达式,而strlen()会计算。

int main()
{int a = 0;short b = 4;printf("%d\n",sizeof(b = b + a));//输出 2 ,取决于 b的类型printf("%d\n",b);//输出4return 0;
}

四、数组与指针笔试题解析

4.1 一维数组:数组名

代码示例

int a[] = {1,2,3,4};
printf("%d\n", sizeof(a));        // 16
printf("%d\n", sizeof(a+0));      // 4/8(首元素地址,指针大小)
printf("%d\n", sizeof(*a));       // 4(首元素是int,占4字节)
printf("%d\n", sizeof(a+1));      // 4/8(第二个元素地址,指针大小)
printf("%d\n", sizeof(a[1]));     // 4
printf("%d\n", sizeof(&a));       // 4/8(数组地址,指针大小)
printf("%d\n", sizeof(*&a));      // 16(*&a等价于a,整个数组大小)
printf("%d\n", sizeof(&a+1));     // 4/8(跳过整个数组的地址,指针大小)
printf("%d\n", sizeof(&a[0]));    // 4/8(首元素地址,指针大小)
printf("%d\n", sizeof(&a[0]+1));  // 4/8(第二个元素地址,指针大小)

关键结论

  • 数组名asizeof(a)&a中表示整个数组,其他情况均表示首元素地址
  • 指针运算(如a+1)的结果仍是指针,大小为4/8字节。

4.2 字符数组:\0

字符数组的核心陷阱在于是否包含\0,这直接影响strlen的结果。

代码1:无\0的字符数组(sizeof解析)

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));       // 6(6个char,每个1字节)
printf("%d\n", sizeof(arr+0));     // 4/8
printf("%d\n", sizeof(*arr));      // 1(首元素是char)
printf("%d\n", sizeof(&arr));      // 4/8
printf("%d\n", sizeof(&arr+1));    // 4/8

代码2:无\0的字符数组(strlen解析)

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));       // 结果:随机值(无\0,越界查找)
printf("%d\n", strlen(arr+0));     // 结果:随机值(同arr,首元素地址)
printf("%d\n", strlen(*arr));       
// 错误(*arr是'a',ASCII值97,相当于把 97作为地址传给strlen,视为地址越界)

代码3:含\0的字符数组(字符串)

char arr[] = "abcdef";             // 隐含'\0',共7个元素
printf("%d\n", sizeof(arr));       // 结果:7(包含'\0')
printf("%d\n", strlen(arr));       // 结果:6('\0'前共6个字符)

代码4:字符指针指向常量字符串

char *p = "abcdef";  // p存储字符串首地址
printf("%d\n", sizeof(p));        // 结果:4/8(指针大小)
printf("%d\n", strlen(p));        // 结果:6(字符串长度)
printf("%d\n", strlen(p+1));      // 结果:5(从'b'开始计数)

4.3 二维数组:行地址与元素地址的嵌套

代码示例

int a[3][4] = {0};  // 3行4列的二维数组
printf("%d\n", sizeof(a));        // 结果:48(3×4×4字节,整个数组大小)
printf("%d\n", sizeof(a[0]));     // 结果:16(第0行数组大小:4×4字节)
printf("%d\n", sizeof(a[0]+1));   // 结果:4/8(第0行第1列元素地址)
printf("%d\n", sizeof(a+1));      // 结果:4/8(第1行的地址,行指针)
printf("%d\n", sizeof(*(a+1)));   // 结果:16(第1行数组大小)
printf("%d\n", sizeof(*a));       // 结果:16(第0行数组大小,*a等价于a[0])

核心逻辑

  • 二维数组a可视为“数组的数组”,a[i]是第i行的一维数组名。
  • a表示首行地址(行指针,类型为int(*)[4]),a+1指向第1行。

4.4 指针运算

题目1:数组地址的跨越

int a[5] = {1,2,3,4,5};
int *ptr = (int*)(&a + 1);  // &a是数组地址,+1跳过整个数组
printf("%d,%d", *(a+1), *(ptr-1));  // 结果:2,5
  • a+1指向第1个元素(值2),ptr-1指向数组最后一个元素(值5)。

题目2:结构体指针的算术运算

假设结构体大小为20字节

struct Test {int Num; char *pcName; short sDate;char cha[2]; short sBa[4];
};
struct Test *p = (struct Test*)0x100000; printf("%p\n", p + 0x1);       // 结果:0x100014(+20字节,指针按结构体大小偏移)
printf("%p\n", (unsigned long)p + 0x1);  // 结果:0x100001(整数+1)
printf("%p\n", (unsigned int*)p + 0x1);  // 结果:0x100004(+4字节,按int*偏移)

题目3:逗号表达式与数组初始化

int a[3][2] = { (0,1), (2,3), (4,5) };  // 逗号表达式取右值,等价于{1,3,5}
int *p = a[0];  // p指向第0行第0列
printf("%d", p[0]);  // 结果:1

注意:

  • 二维数组初始化要用{{},{},{}}!!!
  • 第一行相当于三个逗号表达式(依次执行每个表达式,结果为最后一个表达式的值),所以这里只初始化了三个元素,分别是1,3,5.
13
50
00

题目4:二维数组与指针偏移差异

x86环境下:

int a[5][5];
int(*p)[4] ;
p = a;  //类型差异,会发出警告,不影响运行
printf("%p %d", &p[4][2] - &a[4][2] , &p[4][2] - &a[4][2]);  
// 结果:fffffffc  -4

在这里插入图片描述

  • 二维数组虽然以多行多列的表格形式理解起来更直观,但其实在内存中也是连续存储的,相当于一行多列的表格。
  • p[4][2]==*(*(p+4)+2) p指向元素个数为4的数组,p+整数 跳过一个数组的大小,也就是4个整型,16字节。
  • 根据图示找到p[4][2]的位置,与a[4][2]做差,得到其中元素个数,%d用于打印有符号整数,结果为-4.
  • 内存中以补码的形式存储(x86):
-4 原码:1000 0000 0000 0000 0000 0000 0000 0100
-4 补码:1111 1111 1111 1111 1111 1111 1111 1100
十六进制:f    f    f    f    f    f    f    c
  • 因此%p打印结果:fffffffc
  • p只计算了指针偏移量,没有访问内容,所以不会造成野指针问题

题目5:数组地址与元素地址的转换

int aa[2][5] = {1,2,3,4,5,6,7,8,9,10};
int *ptr1 = (int*)(&aa + 1);  // 跳过整个数组,指向数组后地址
int *ptr2 = (int*)(*(aa + 1));  
//aa是首元素地址,也就是第0行地址, +1跳过1行,等价于aa[1],指向第1行首元素
printf("%d,%d", *(ptr1-1), *(ptr2-1));  // 结果:10,5

题目6:指针数组的偏移

char *a[] = {"work","at","alibaba"};
//a 是指针数组,每个元素是char*类型 ,每个元素指向一个字符串
char **pa = a;  // pa指向指针数组首元素
pa++;  // 指向第二个元素("at")
printf("%s\n", *pa);  // 结果:at

在这里插入图片描述

题目7:三级指针的嵌套访问

char *c[] = {"ENTER","NEW","POINT","FIRST"};
char **cp[] = {c+3,c+2,c+1,c};
char ***cpp = cp;
printf("%s\n", **++cpp);        // 结果:POINT
printf("%s\n", *--*++cpp+3);   // 结果:ER
printf("%s\n", *cpp[-2]+3);    // 结果:ST
printf("%s\n", cpp[-1][-1]+1); // 结果:EW

图解:
在这里插入图片描述

printf("%s\n", **++cpp);      

在这里插入图片描述

  • 跳过一个char**,解引用两次得到是P的地址,%s打印完整字符串,输出POINT
printf("%s\n", *--*++cpp + 3);   

在这里插入图片描述

  • 第一次打印的++cpp会改变cpp指向的位置
  • ++cpp再跳过一个char**,解引用得到c+1,再自减,向前跳一个char*,再解引用得到E的地址
  • +3,得到E的地址,输出结果为ER
printf("%s\n", *cpp[-2]+3);    

在这里插入图片描述

  • 第二次打印的++cpp会改变cpp指向的位置
  • cpp[-2] ==*(cpp-2) cpp向前跳过2个char**,再解引用得到c+3,再解引用得到F的地址
  • +3得到S的地址,输出结果为ST
printf("%s\n", cpp[-1][-1]+1);

在这里插入图片描述

  • 第三次打印的cpp[-2]不会改变cpp的值,此时cpp依旧指向c+1
  • cpp[-1]向前跳过1个char**,再解引用得到c+2cpp[-1][-1]再向前跳过1个char*,再解引用得到N的地址
  • +1得到E的地址,输出结果为EW

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

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

相关文章

sql调优总结

sql调优 线上发现部分sql查询时间过长。使用explain观察是否命中表的索引。未命中索引&#xff0c;使用 TABLE add index 语句添加索引。 除此之外&#xff0c;单个字段命中联合索引的情况也会导致查询变慢 针对多个字段的查询可添加联合索引。 总结如下慢sql的原因&#xff1a…

如何在nuxt项目中使用axios进行网络请求?

在 Nuxt 项目中使用 Axios 进行网络请求有两种常用方式&#xff1a;一是直接安装 Axios 并全局配置&#xff0c;二是使用 Nuxt 官方推荐的 nuxtjs/axios 模块&#xff08;更便捷&#xff09;。以下是详细步骤&#xff1a; 方法一&#xff1a;使用官方推荐的 nuxtjs/axios 模块&…

Unity 实现手机端和电脑项目在局域网内通信

电脑端启动后自动广播自身存在&#xff0c;手机端启动后监听广播并发现服务器。发现后自动建立 UDP 连接&#xff0c;双方可互发消息。内置心跳检测&#xff0c;网络中断时会自动检测并提示断开using UnityEngine; using System.Net; using System.Net.Sockets; using System.T…

C++_389_定义一个禁用了赋值操作、具有线程同步资源保护的结构体,作为一些回调函数的参数,方便获取响应操作的结果等信息

/* 回调参数。注意:此结构体禁用了赋值,会编译报错 */struct API_CALLBACK_T{public:API_CALLBACK_T(){eRet = e_fail;bWait = true;

uniapp基础 (一)

目录 UniApp 是什么&#xff1f;有什么优势 跨平台高效开发 Vue.js 技术生态 插件生态丰富 渐进式开发支持 UniApp 跨平台兼容的实现原理 编译时转 运行时适配层 条件编译 性能优化策略 1.预编译模 2.原生组件混合渲 3.分包加载 UniApp 的生命周期钩子有哪些&#x…

【图像算法 - 10】进阶入门:改进 YOLO11 安全帽检测的关键参数与场景适配

一、项目背景与需求 视频全文介绍 【图像算法 - 10】进阶入门&#xff1a;改进 YOLO11 安全帽检测的关键参数与场景适配今天我们使用深度学习来训练一个安全帽检测系统&#xff0c;基于YOLO11的安全帽检测系统。我们使用了两万张图片的数据集训练了这次的基于YOLO11的安全帽检…

【C 学习】04.1-类型转换浮点数

“知道做不到就是不知道”一、类型转换1.自动类型转换&#xff1a;当运算符&#xff08;常见、-、*、/、%&#xff09;两边出现不一致的类型时&#xff0c;编译器会自动转换成较大的&#xff08;范围更大&#xff09;类型。从小到大&#xff1a;char-short-int-long-long long;…

基于反事实对比学习的鲁棒图像表征|文献速递-医学影像算法文献分享

Title题目Robust image representations with counterfactual contrastive learning基于反事实对比学习的鲁棒图像表征01文献速递介绍医学影像中的对比学习已成为利用未标记数据的有效策略。这种自监督学习方法已被证明能显著提升模型跨领域偏移的泛化能力&#xff0c;并减少训…

机器学习(5):朴素贝叶斯分类算法

贝叶斯的核心思想就是&#xff0c;谁的概率高就归为哪一类。贝叶斯推论P(A):先验概率。即在B事件发生之前&#xff0c;我们对A事件概率的一个判断。P(A|B)&#xff1a;后验概率。即在B事件发生之后&#xff0c;我们对A事件概率的重新评估。P(B|A)/P(B)&#xff1a;可能性函数。…

Docker 容器内进行 frp 内网穿透

开始之前需要有一台可以进行公网访问的服务器 下载安装 frp 这个直接到 github 官网就可以下载了 点击Releases 就可以查看到可以下载的源&#xff0c;根据自己电脑的型号进行选择就好了。 linux服务器上下载 如果是在linux的服务器上的话可以直接通过wget进行下载 例如&a…

复制网页文字到Word、WPS文字?选中后直接拖放

要把网页、PDF或其他应用中的文字内容复制到Word、WPS文字、记事本等&#xff0c;不一定要先复制、再粘贴&#xff0c;也可以选中文字后直接拖动到目标位置即可。多次操作&#xff0c;可以把窗口并排再拖动。如果你经常需要在不同应用之间引用文字&#xff0c;不妨试一试。操作…

Starrocks中的 Query Profile以及explain analyze及trace命令中的区别

背景 本文基于Starrocks 3.5.5 现有公司因为业务的不同&#xff0c;可能会更加关系单个SQL 的RT&#xff0c;因为如果一个SQL的RT比较大的话&#xff0c;影响的就是这个业务&#xff0c;从而影响收入&#xff0c;所以对于这方面我们就比较关心&#xff0c; 而最近在基于Starro…

网络 —— 笔记本(主机)、主机虚拟机(Windows、Ubuntu)、手机(笔记本热点),三者进行相互ping通

背景介绍最近在笔记本电脑上的虚拟机(Ubuntu、Windows Server搭配)上部署了"WD"开源手游服务器(旧版本)&#xff0c;手机连接上了笔记本电脑开启的WIFI热点&#xff0c;同时手机上安装了"WD"手游客户端。于是首先得保证网络相互畅通才能玩游戏&#xff0c;…

裸露土堆识别准确率↑32%:陌讯多模态融合算法在生态监测的实战解析

原创声明本文为原创技术解析文章&#xff0c;涉及技术参数及架构描述均参考《陌讯技术白皮书》&#xff0c;禁止任何形式的转载与抄袭。一、行业痛点&#xff1a;裸露土堆识别的现实挑战在生态环境保护、建筑工地监管等场景中&#xff0c;裸露土堆的精准识别是遏制扬尘污染、防…

网站从HTTP升级到HTTPS网址方法

将网站从HTTP升级到HTTPS涉及几个关键步骤&#xff0c;以确保安全连接以及用户和搜索引擎的平稳过渡。获取并安装SSL/TLS证书&#xff1a;1、从CA机构授权提供商Gworg获取SSL/TLS证书。选项包括域名验证(DV)、组织验证(OV)和扩展验证(EV)证书&#xff0c;验证严格度各不相同&am…

WaitForSingleObject 函数参数影响及信号处理分析

一、第二个参数&#xff08;超时时间&#xff09;的影响 DWORD result WaitForSingleObject(hHandle, 1000);中的第二个参数1000表示等待超时时间为1000毫秒&#xff08;1秒&#xff09;&#xff0c;其核心影响如下&#xff1a; 1. 函数行为控制 立即返回&#xff1a;若对象已…

dbeaver导入数据及配置讲解

导入数据教程&#xff1a; 前提.csv文件&#xff1a;且只能导入一个sheet点击下一步选中导入的.csv文件对应好数据字段和表字段&#xff0c;感觉不需要导入的可以skip配置一下&#xff0c;下面有介绍&#xff1a;以下为你详细解析这些数据加载相关功能的含义与作用&#xff1a;…

JAVA学习笔记 自增与自减的使用-006

目录 1 基本概述 2 自增与自减的用法 2.1单独使用 2.2 参与运算 3 思考与练习 3.1 基础题 3.2 中等题 3.3 进阶题 4 总结 源计划&#xff1a;我从来不认为自己的成功过程有多心酸&#xff0c;只是心中不惧失败&#xff0c;能够承受别人不能接受的失望而已&#xff01;…

从LCM到SomeIP,再到DDS:技术演进与工作原理剖析

文章目录一、LCM&#xff1a;轻量级通信与编组库工作原理C 代码示例局限性二、SomeIP&#xff1a;面向服务的可扩展中间件工作原理C 代码示例优势与特点三、DDS&#xff1a;数据分发服务工作原理C 代码示例优势与应用场景四、技术演进总结在分布式系统通信领域&#xff0c;技术…

Redis里面什么是sdshdr,可以详细介绍一下吗?

文章目录为什么 Redis 不直接使用 C 语言的字符串&#xff1f;sdshdr 的结构sdshdr 的不同类型sdshdr 带来的优势总结我们来详细解析一下 Redis 的核心数据结构之一&#xff1a; sdshdr。sdshdr 是 “Simple Dynamic String header” 的缩写&#xff0c;意为“简单动态字符串头…