当前位置: 首页 > news >正文

【C语言系列】深入理解指针(3)

深入理解指针(3)

  • 一、字符指针变量
  • 二、数组指针变量
    • 2.1数组指针变量是什么?
    • 2.2数组指针变量怎么初始化?
  • 三、二维数组传参的本质
  • 四、函数指针变量
    • 4.1函数指针变量的创建
    • 4.2函数指针变量的使用
    • 4.3两段有趣的代码
      • 4.4 typedef关键字
  • 五、函数指针数组
  • 六、转移表
  • 七、总结

一、字符指针变量

在指针的类型中我们知道有⼀种指针类型为字符指针char*。
下面我们来看这样一段代码:

#include <stdio.h>
int main()
{
int a = 20;
a = 200;
//3 = 5;
const char*p = "hello world";//常量字符串是不能被修改的。
printf("%c\n",*p);//h
printf("%s\n",p);
//*p = 'q';//err
return 0;
}

运行结果如下图:
在这里插入图片描述
观察代码及运行结果,这里我们看到其实是把hello world中的首字母的地址存放在p中,在这里很多人会认为是把整个字符串放在指针p中。
《剑指offer》中有一道和字符串相关的笔试题,我们一起来看一下,代码如下:

#include <stdio.h>
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char*str3 = "hello bit.";
const char*str4 = "hello bit.";
if(str1 == str2)
printf("str1 and str2 are same\n");//1X
else
printf("str1 and str2 are not same\n");//2
if(str3 == str4)
printf("str3 and str4 are same\n");//3
else
printf("str3 and str4 are not same\n");//4X
//常量字符串是不能被修改的,内容相同的常量字符串只会在内存上存放1份。
return 0;
}

运行结果如下图:
在这里插入图片描述
通过观察我们发现,用相同的字符串常量初始化不同的数组时就会开辟出不同的内存块,所以str1和str2是不同的,常量字符串是不能被修改的,内容相同的常量字符串只会在内存上存放1份,所以str3和str4是相同的。

二、数组指针变量

2.1数组指针变量是什么?

之前我们学过指针数组,是存放地址(指针)的数组。
那数组指针变量又是什么呢?是指针变量?还是数组?
类比:
字符指针 char *p 指向字符的指针,存放的是字符的地址。
整型指针 int *p 指向整型的指针,存放的是整型的地址。
数组指针 指向数组的指针,是存放数组地址的指针变量。
答案不言而喻,是指针变量,是存放数组地址的指针变量。
数组指针变量:

int (*p)[10];

p先和*结合,说明p是一个指针变量变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针
注:[ ]的优先级比 * 高所以必须要带括号,保证 p 先和 * 结合。

2.2数组指针变量怎么初始化?

数组指针变量是用来存放数组地址的,那怎么获得数组的地址呢?这就用到了我们之前学习的&数组名 。
例:

int(*p)[10] = &arr;

我们再看看下面一段代码:

#include <stdio.h>
int main()
{
int arr[10] = {0};
int(*p)[10] = &arr;
return 0;
}

调试结果如下图:
在这里插入图片描述
通过调试后发现,&arr和p的类型是一样的。
在这里插入图片描述
注:数组指针,会在二维数组中使用。

三、二维数组传参的本质

有了数组指针的理解,我们就能够了解一下⼆维数组传参的本质了。
以前我们有一个⼆维数组,需要传参给⼀个函数的时候,我们是这样写的:

void test(int a[3][5],int r,int c)
{
int i = 0;
int j = 0;
for(i = 0;i < r;i++)
{
for(j = 0;j < c;j++)
{
printf("%d",a[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = {{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}};
test(arr,3,5);
return 0;
}

这里实参是二维数组,形参也写成⼆维数组的形式,那还有什么其他的写法吗?
首先我们再次理解一下二维数组,二维数组其实可以看做是每个元素都是一维数组的数组,也就是二维数组的每个元素一个一维数组。那么⼆维数组的首元素就是第一行,是个一维数组
如图:
在这里插入图片描述
arr是二维数组,数组名是数组首元素的地址,那么二维数组arr的数组名是谁呢?
答案显而易见,二维数组的首元素就是第一行(一维数组),每一行都是一个元素(一维数组)。
那么上述代码就可以改成以下代码:

void test(int(*arr)[5],int r,int c)
{
int i = 0;
int j = 0;
for(i = 0;i < r;i++)
{
for(j = 0;j < c;j++)
{
printf("%d",*(*(p + i) + j));//(*(p + i))[j]
}
printf("\n");
}
}
int main()
{
int arr[3][5] = {{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}};
test(arr,3,5);
return 0;
}

二维数组传参的本质二维数组传参本质上也是传递了地址,传递的是第一行这个一维数组的地址,那么形参也是可以写成指针的形式的

四、函数指针变量

4.1函数指针变量的创建

什么是函数指针变量呢?
函数指针变量是用来存放函数地址的,后面通过地址能够调用函数的。
那么函数是否有地址呢?下面我们做一个测试,代码如下:

#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("test:  %p\n", test); 
printf("&test: %p\n", &test);
return 0;
}

运行结果如下图:
在这里插入图片描述
这里我们可以看出确实打印出了地址,所以函数是有地址的,函数名就是它的地址,可以通过函数名(或者&函数名)来获取地址。
函数指针变量写法与数组指针变量的写法非常类似,代码如下:

int Add(int a,int b)
{
return a + b;
}
int main()
{
int arr[8] = {0};
int (*pa)[8] = &arr;//pa是数组指针变量。
int (*pf)(int,int) = &Add;//pf就是函数指针变量。
return 0;
}

在这里插入图片描述

4.2函数指针变量的使用

通过函数指针调⽤指针指向的函数。

#include <stdio.h>
int Add(int a,int b)
{
return a + b;
}
int main()
{
int(*pf1)(int,int) = &Add;//int(*)(int,int)函数指针类型
int(*pf2)(int,int) = Add;
int r1 = (*pf1)(3,7);//int r1 = (pf1)(3,7);//*是个摆设
int r2 = (*pf2)(3,7);//int r2 = (pf2)(3,7);
int r3 = Add(3,7);
printf("%d\n",r1);
printf("%d\n",r2);
printf("%d\n",r3);
return 0;
}

运行结果如下图:
在这里插入图片描述

4.3两段有趣的代码

第一段代码:

int main()
{//void(*p)();
(*(void (*)())0)();//函数调用
//1.将0强制类型转换成void(*)()类型的函数指针。
//2.调用0地址处放的这个函数。
return 0;
}

运行时报错,如下图:
在这里插入图片描述
第二段代码:

int main()
{
void (*signal(int , void(*)(int)))(int);
//函数声明,声明的函数的名字叫:signal
//signal函数有2个参数,第一个参数的类型是int,第二个参数是void(*)(int)的函数指针类型,该指针可以指向一个函数,指向的函数参数是int,返回类型是void。
//signal函数的返回类型是void(*)(int)的函数指针,该指针指向一个函数,指向的函数参数是int,返回类型是void。
//void(*)int signal(int,void(*)(int));//err
return 0;
}

运行结果如下图:
在这里插入图片描述
在这里插入图片描述
注:这两个代码来自《C陷阱和缺陷》这本书。

4.4 typedef关键字

typedef 是用来类型重命名的,可以将复杂的类型,简单化。
例如:你觉得unsigned int 写起来不太方便,如果能写成uint 就方便多了,那么我们就可以使用:

typedef unsigned int uint;
//将unsigned int重命名为uint

那么指针类型是否可以重命名呢?
可以,代码如下:

//typedef对指针类型重命名
typedef int*pint;
int main()
{
int* p1 = NULL;
pint p2 = NULL;
return 0;
}

但是对于数组指针和函数指针稍微有点区别,数组指针代码如下:

typedef int(*parr_t)[5];//新的类型名必须在*的右边
//parr_t等价于int(*)[5]
int main()
{
int arr[5] = {0};
int(*p)[5] = &arr;//p是数组指针变量,p是变量的名字
//int(*)[5] —— 数组指针类型
parr_t p2 = &arr;
return 0;
}

函数指针类型重命名,代码如下:

typedef void(*pf_t)(int);//新的类型名必须在*的右边

对4.3中第二段代码进行简化:

typedef void (*pf_t)(int);//新的类型名
void (*signal(int , void(*)(int)))(int);//简化前代码
pf_t signal(int,pf_t);//简化后代码

接下来我们看下面这段代码:

typedef int* ptr_t;
#define PTR_T int*;
int main()
{
//ptr_t p1;//p1是整型指针
//PTR_T P2;//p2是整型指针
ptr_t p1,p2;//p1,p2是整型指针
PTR_T p3,p4;//p3是指针,p4是整型//p3 —— int*	p4 —— int
//int*p3,p4;
}

观察后不难发现:#define定义的PTR_T,PTR_T p3,p4;被分解为PTR_Tp3(即int*p3)和p4(即int p4),而typedef定义的ptr_t,却被分解为ptr_t p1和ptr_t p2。
注:函数指针 —— 指向函数的指针;函数指针变量 —— 存放函数地址的变量。

五、函数指针数组

数组是一个存放相同类型数据的存储空间,之前我们学习了指针数组,如:

int *arr[10];//数组的每个元素是int* 

类比:
整数数组:是存放整型的数组
字符数组:是存放字符的数组
指针数组:是存放指针的数组
chararr1[5];//字符指针数组
int
arr2[7];//整数指针数组

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

int (*parr1[3])();
int *parr2[3]();
int (*)() parr3[3];

答案:parr1,parr1 先和[ ] 结合,说明parr1是数组,数组的内容是什么呢?是int (*)() 类型的函数指针。

#include <stdio.h>
int Add(int x,int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x,int y)
{
return x * y;
}
int Div(int x,int y)
{
return x / y;
}
int main()
{
int (*pf1)(int,int) = Add;
int (*pf2)(int,int) = Sub;
int (*pf3)(int,int) = Mul;
int (*pf4)(int,int) = Div;
int(*pfArr[4])(int,int) = {Add,Sub,Mul,Div};//pfArr就是函数指针数组
int i = 0;
for(i = 0;i < 4;i++)
{
int ret = pfArr[i](8,4);
printf("%d\n",ret);
}
return 0;
}

六、转移表

函数指针数组的用途:转移表
举例:计算器的一般实现:

#include <stdio.h>
int Add(int x,int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x,int y)
{
return x * y;
}
int Div(int x,int y)
{
return x / y;
}
nemu()
{printf("*************************\n");printf("  1:add           2:sub  \n");printf("  3:mul           4:div  \n");printf("  0:exit                 \n");printf("*************************\n");
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
//创建一个函数指针的数组
int(*pfArr[5])(int,int) = {NULL,Add,Sub,Mul,Div};
do 
{
nemu();
printf("请选择:");
scanf("%d",&input);//2
if(input >= 1 && input <= 4)
{
printf("请输入2个操作数:");
scanf("%d %d",&x,&y);
ret = pfArr[input](x,y);
printf("%d\n",ret);
}
else if(input == 0)
{
printf("退出计算器\n");
break;
}
else
{
printf("选择错误,重新选择\n");
}
}while(input);
return 0;
}

在这里插入图片描述
数组指针 —— 指向的是数组

int arr[5];
int(*p)[5] = &arr;

函数指针 —— 指向的是函数

char*test(int n,char*s)
{
}
char*(*pf)(int,char*) = test;//pf是函数指针变量

指针是否可以存放到数组中呢?—— 可以
指针数组

char*arr[5];
int*arr2[5];
double*arr3[9];
float*arr4[6];

函数指针数组

char*(*pfArr[4])(int,char*);

拓展:指向函数指针数组的指针

char*(*(*p)[4])(int,char*) = &pfArr;
//取出的是函数指针数组地址,p就是指向一个函数指针数组的指针。

实现一个计算器,代码如下:

#include <stdio.h>
int Add(int x,int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x,int y)
{
return x * y;
}
int Div(int x,int y)
{
return x / y;
}
void nemu()
{printf("*************************\n");printf("  1:add           2:sub  \n");printf("  3:mul           4:div  \n");printf("  0:exit                 \n");printf("*************************\n");
}
void Calc(int(*pf)(int,int))
{
int x = 0;
int y = 0;
int ret = 0;
printf("请输入2个操作数:");
scanf("%d %d",&x,&y);
ret = pf(x,y);
printf("%d\n",ret);
}
int main()
{
int input = 0;
do
{
nemu();
printf("请选择:");
scanf("%d",&input);
switch(input)
{
case 1:Calc(Add);break;
case 2:Calc(Sub);break;
case 3:Calc(Mul);break;
case 4:Calc(Div);break;
case 0:printf("退出计算器\n");break;
default:printf("选择错误,重新选择\n");break;
}
}while(input);
return 0;
}

数组传参:形参可以是数组,也可以是指针(数组指针),实参是函数地址:形参只能是函数指针。

七、总结

这篇文章详细介绍了C语言中指针相关的重要概念,包括字符指针、数组指针、函数指针及其数组的使用。通过代码示例,文章解释了如何利用指针进行内存管理以及如何实现更灵活、动态的功能调用。以下是对文章内容的总结。

首先,文章介绍了字符指针(char*),它是指向字符的指针类型,常用于操作字符串。通过代码实例展示了如何通过字符指针访问字符串内容,特别是常量字符串的特点。常量字符串存储在只读内存中,因此不能修改。作者通过对比字符数组与字符指针的内存分配,强调了字符指针指向的是字符串的首字符地址,而非整个字符串。

接着,文章讲解了数组指针(如int (*p)[10]),它是指向数组的指针,存储的是数组的地址。数组指针与指针数组有所不同,数组指针指向的是整个数组,而指针数组是存储指针的数组。通过示例代码,读者可以清楚地理解如何声明和初始化数组指针,以及如何利用数组指针处理二维数组。

文章还深入探讨了二维数组传参的本质。二维数组在内存中其实是由多个一维数组组成的,每个元素都是一个一维数组。传递二维数组时,实际上是传递了二维数组的首元素地址,即第一行的地址。文章通过代码示例说明了如何用数组指针来传递二维数组,这样避免了传递整个数组的额外开销。

此外,文章详细讲解了函数指针变量。函数指针允许程序在运行时动态调用不同的函数。文章通过示例演示了函数指针的创建和使用,包括如何声明、初始化并通过函数指针调用函数。作者还展示了如何使用 typedef 来简化函数指针的声明,提高代码的可读性。

最后,文章介绍了函数指针数组及其应用。函数指针数组是存储多个函数指针的数组,允许根据不同的条件动态选择要调用的函数。通过实现一个简单的计算器,文章展示了如何通过函数指针数组实现不同操作的选择。函数指针数组在实际编程中有广泛的应用,尤其适用于需要根据输入选择不同操作的场景。

总的来说,本文通过详细的代码实例,帮助读者理解了C语言中指针的不同类型及其应用,特别是如何通过指针和函数指针提高程序的灵活性和动态性。这些技巧不仅能简化复杂的代码,还能增强程序的可维护性和扩展性。

相关文章:

【C语言系列】深入理解指针(3)

深入理解指针&#xff08;3&#xff09; 一、字符指针变量二、数组指针变量2.1数组指针变量是什么&#xff1f;2.2数组指针变量怎么初始化&#xff1f; 三、二维数组传参的本质四、函数指针变量4.1函数指针变量的创建4.2函数指针变量的使用4.3两段有趣的代码4.4 typedef关键字 …...

three.js+WebGL踩坑经验合集:写在前面的话

笔者从2023年初开始参与一个基于three.js引擎的web项目的开发工作。本打算2024年春节就把期间踩过的坑写到博客上分享给大家&#xff0c;然而哪怕本专栏的各种构思和内容已经在笔者的脑海里翻滚了一年&#xff0c;得了严重拖延症患者的我还是一直拖到了现在&#xff0c;实在惭愧…...

利用Linux的工作队列(Workqueue)实现中断下半部的处理

本文代码在哪个基础上修改而成&#xff1f; 本文是在博文 https://blog.csdn.net/wenhao_ir/article/details/145228617 的代码基础上修改而成。 关于工作队列(Workqueue)的概念 工作队列(Workqueue)可以用于实现Linux的中断下半部的&#xff0c;之前在博文 https://blog.cs…...

LabVIEW处理复杂系统和数据处理

LabVIEW 是一个图形化编程平台&#xff0c;广泛应用于自动化控制、数据采集、信号处理、仪器控制等复杂系统的开发。它的图形化界面使得开发人员能够直观地设计系统和算法&#xff0c;尤其适合处理需要实时数据分析、高精度控制和复杂硬件集成的应用场景。LabVIEW 提供丰富的库…...

spring-springboot -springcloud

目录 spring: 动态代理: spring的生命周期(bean的生命周期): SpringMvc的生命周期: SpringBoot: 自动装配: 自动装配流程: Spring中常用的注解&#xff1a; Spring Boot中常用的注解&#xff1a; SpringCloud: 1. 注册中心: 2. gateway(网关): 3. Ribbon(负载均…...

DRG/DIP 2.0时代下基于PostgreSQL的成本管理实践与探索(下)

五、数据处理与 ETL 流程编程实现 5.1 数据抽取与转换(ETL) 在 DRG/DIP 2.0 时代的医院成本管理中,数据抽取与转换(ETL)是将医院各个业务系统中的原始数据转化为可供成本管理分析使用的关键环节。这一过程涉及从医院 HIS 系统中抽取患者诊疗数据,并对其进行格式转换、字…...

打造本地音乐库

文章目录 存储介质硬盘&#xff08;NAS&#xff09;媒体播放器&#xff08;可视MP3、MP4&#xff09;实体介质&#xff08;CD光盘、黑胶片&#xff09;注意事项为什么不使用在线音乐&#xff08;App&#xff09;和网盘打造一套HiFi系统的成本非常高 获取音乐正版音乐途径免费音…...

【2024 - 年终总结】叶子增长,期待花开

写在前面&#xff1a;本博客仅作记录学习之用&#xff0c;部分图片来自网络&#xff0c;如需引用请注明出处&#xff0c;同时如有侵犯您的权益&#xff0c;请联系删除&#xff01; 文章目录 前言论博客创作保持2024的记录清单博客科研开源工作生活 总结与展望互动致谢参考 前言…...

python 统计相同像素值个数

目录 python 统计相同像素值个数 最大值附近的值 python 统计相同像素值个数 import cv2 import numpy as np import time from collections import Counter# 读取图像 image cv2.imread(mask16.jpg)# 将图像转换为灰度图像 gray_image cv2.cvtColor(image, cv2.COLOR_BGR2…...

蓝卓“1+2+N”智慧工厂架构,让工业智能更简单

面对复杂的工业环境、海量的数据以及多样化的业务需求&#xff0c;如何实现智能化转型&#xff0c;让工业智能触手可及&#xff0c;成为了众多企业面临的难题。蓝卓以创新精神为引领&#xff0c;推出了“12N”智慧工厂架构&#xff0c;旨在简化工业智能的实现路径&#xff0c;让…...

12、MySQL锁相关知识

目录 1、全局锁和表锁使用场景 2、行锁的意义 3、为什么说间隙锁解决了快照的幻读? 4、RR隔离级别产生幻读的场景 5、详解元数据锁(MDL)作用以及如何减少元数据锁 6、出现死锁场景 7、查看MySQL锁情况 8、自增锁 1、全局锁和表锁使用场景 全局锁 备份数据库:当需要…...

某大厂一面:HashMap 的put方法具体做了哪些操作

HashMap 的 put 方法是一个常用的操作&#xff0c;它将一个键值对插入到哈希表中。下面是 put 方法执行的详细流程&#xff0c;包括各个步骤的解释&#xff0c;并附上相应的代码片段。 1. 检查键是否为 null 如果传入的键为 null&#xff0c;HashMap 会特别处理这种情况&…...

WPF基础 | 深入 WPF 事件机制:路由事件与自定义事件处理

WPF基础 | 深入 WPF 事件机制&#xff1a;路由事件与自定义事件处理 一、前言二、WPF 事件基础概念2.1 事件的定义与本质2.2 常见的 WPF 事件类型 三、路由事件3.1 路由事件的概念与原理3.2 路由事件的三个阶段3.3 路由事件的标识与注册3.4 常见的路由事件示例 四、自定义事件处…...

精选100+套HTML可视化大屏模板源码素材

大屏数据可视化以大屏为主要展示载体的数据可视化设计。 “大面积、炫酷动效、丰富色彩”&#xff0c;大屏易在观感上给人留下震撼印象&#xff0c;便于营造某些独特氛围、打造仪式感。 原本看不见的数据可视化后&#xff0c;便能调动人的情绪、引发人的共鸣。 使用方法&…...

如何使用Python爬虫按关键字搜索AliExpress商品:代码示例与实践指南

在电商领域&#xff0c;能够按关键字搜索并获取商品信息对于市场分析、选品和竞品研究至关重要。AliExpress&#xff08;速卖通&#xff09;作为全球知名的跨境电商平台&#xff0c;提供了丰富的商品数据。以下将详细介绍如何使用Python爬虫按关键字搜索AliExpress商品&#xf…...

No.36 学习 | Python 函数:从基础到实战

最近我在学 Python 编程&#xff0c;今天可算是狠狠钻研了一把 Python 里的函数&#xff0c;感觉脑袋里的知识又充实了不少&#xff0c;赶紧来记一记。 一、Python函数基础概念 &#xff08;一&#xff09;pass语句&#xff1a;代码块的“占位符” 在编写代码时&#xff0c;有…...

Unity常用特性(Attribute)用法

一.UnityEngine命名空间 1.[Header(string)] inspector面板上给显示的字段上加一个描述 通常情况下&#xff0c;用于在 Inspector 窗口中创建字段的逻辑分组 public class AttributeTest : MonoBehaviour {[Header("public_field_num")]public int num; }2.[Tool…...

VUE对接deepseekAPI调用

1.先去开放平台注册账号申请api key。开放平台&#xff1a;https://platform.deepseek.com/api_keys 2.你的项目需要有发送请求的axios或者自己写。 npm install axios # 或 yarn add axios 3.创建 API 调用函数 在 Vue 项目中&#xff0c;通常会将 API 调用的逻辑封装到一个…...

【Postman 接口测试】接口测试基础知识

在软件开发与测试领域&#xff0c;接口测试是保障软件质量的关键环节之一&#xff0c;而 Postman 作为一款功能强大且广泛使用的接口测试工具&#xff0c;能帮助我们高效地进行接口测试工作。下面&#xff0c;我们将详细介绍接口测试的基础知识&#xff0c;包括接口的认识、接口…...

谷粒商城——商品服务-三级分类

1.商品服务-三级分类 1.1三级分类介绍 1.2查询三级分类查询-递归树型结构数据获取 1.2.1导入数据pms_catelog.sql到数据表pms_category 1.2.2一次性查出所有分类及子分类 1.2.2.1修改CategoryController.java /*** 查出所有分类以及子分类&#xff0c;以树形结构组装起来*/R…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

Java毕业设计:WML信息查询与后端信息发布系统开发

JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发&#xff0c;实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构&#xff0c;服务器端使用Java Servlet处理请求&#xff0c;数据库采用MySQL存储信息&#xff0…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...