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

C语言-指针讲解(3)

文章目录

  • 1.字符指针变量
    • 1.1 字符指针变量类型是什么
    • 1.2字符指针变量的两种使用方法:
    • 1.3字符指针笔试题讲解
    • 1.3.1 代码解剖
  • 2.数组指针变量
    • 2.1 什么是数组指针
    • 2.2 数组指针变量是什么?
      • 2.2.3 数组指针变量的举例
    • 2.3数组指针和指针数组的区别是什么?
      • 2.3.1 数组指针和指针数组区别举例
    • 2.4 数组指针变量是怎么初始化
  • 3.二维数组传参的本质
  • 4.函数指针变量
    • 4.1 函数指针变量的创建
      • 4.1.1什么是函数指针变量呢?
    • 4.2 函数指针变量的使用
    • 4.3 两段有趣的代码
    • 4.4. typedef关键字
      • 4.4.1 typedef关键字是什么?
      • 4.4.2 typedef 关键字用法举例
        • 4.4.2.1 举例1
        • 4.4.2.1 举例2
        • 4.4.2.1 举例3
        • 4.4.2.1 举例4
  • 5.函数指针数组
    • 5.1 什么是函数指针数组?
      • 5.2 如何定义函数指针数组?
      • 5.3 函数指针数组举例
  • 6.转移表
    • 6.1.1用函数指针数组作为转移表实现计算器的代码逻辑
    • 6.1.2用函数指针作为转移表实现计算器的代码逻辑

在上一篇博客中:
C语言-指针讲解(2)
我们更为深层地给大家介绍了指针的其他基础用法
让下面我们来回顾一下讲了什么吧:
1.野指针指向的位置是不可知的。我们只有做到:

  • 对指针初始化
  • 小心数组越界
  • 当指针变量不再使用时,应及时置NULL,指针使用之前检查有效性。

才能在写代码避免出现野指针的情况。
2.assert.h头文件定义了宏assert(),用于在运行中确保程序符合指定程序,如果不符合,就报错运行运行。这个宏常常被称为“断言”。
3.学习指针的用处以及指针传址调用的用法
4.除了sizeof(数组名)和&数组名,其他情况下数组名都是数组首元素的地址。
5.二级指针是存放一级指针的指针,二级指针是存放一级指针的地址。二级指针的相关运算。
6.存放指针的则称作指针数组,指针数组每个元素都是用来存放地址的。
7.指针数组模拟二维数组的实例



那么这次博主会给大家介绍指针进阶的地方,具体内容看下图所示:



在这里插入图片描述

1.字符指针变量

1.1 字符指针变量类型是什么

我们知道,字符变量类型是char。
那同理,字符指针变量类型是char*。

1.2字符指针变量的两种使用方法:

int main()
{char ch = 'w';char *pc = &ch;//用指针变量pc来接收ch变量的地址*pc = 'w';//通过对指针变量pc进行解引用操作访问到ch变量的值return 0;
}

还有一种使用方法:

int main()
{const char* pstr = "hello bit.";//这里是把一个字符串放到pstr指针变量里了吗?printf("%s\n", pstr);return 0;
}

这里需要注意的是:
这行代码:const char* pstr = "hello bit."很容易让同学以为是把字符串hello bit.放到字符指针pstr里了,但其实这是不对的。它本质上是把字符串hello bit.首字符的地址放到了pstr中。
如下图所示:
在这里插入图片描述
通过此图,我们更能直观地看到上面代码的意思实际上是把一个字符串的首字符h的地址存放到指针变量pstr中。


1.3字符指针笔试题讲解

下面来自一道《剑指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");elseprintf("str1 and str2 are not same\n");if(str3==str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}

大家不妨看看这个代码运行结果是什么,输出的是哪两条语句?
在这里插入图片描述

我们不妨看一下vs的运行结果:
在这里插入图片描述
看到这里,也许有同学会有疑问为什么输出的是那两句。接下来我们给大家仔细分析一下。

1.3.1 代码解剖

如下图所示:
在这里插入图片描述

  • 之前我们讲过,除sizeof(数组名)和&数组名,其他情况下数组名都是数组首元素的地址。 因此这行代码:if(str1==str2) 本质上就是两个地址间进行比较。虽然这里的str1和str2数组存的内容是相同的,但本质上它们都是用相同的常量字符串去初始化不同的数组,这样就会开辟出不同的内存块。所以str1和str2不同。
  • 而这里的str3和str4指向的是同一个常量字符串。通常C/C++会把常量字符串存储到单独的一个内存区域, 所以当几个指针指向同一个字符串的时候,它们实际上会指向同一块内存。所以str3和str4是相同的。


2.数组指针变量

2.1 什么是数组指针

数组指针,顾名思义它就是一种指针,里面存放的是数组的地址。

2.2 数组指针变量是什么?

根据前面的指针介绍,我们知道:
1.整形指针变量:int * ptr,存放的是整型变量的地址,能够指向整形数据的指针。
2.浮点型指针变量:float * pf;存放浮点型变量的地址,能够指向浮点型数据的指针。
那数组指针变量:就是存放的是数组的地址,能够指向数组的指针变量。

2.2.3 数组指针变量的举例

我们来看一下,下面哪个代码是数组指针变量?

int *p1[10];
int (*p2)[10];

大家可以先思考一下:
在这里插入图片描述
这里的数组指针变量是这个:

int (*p)[10];

解释如下:

p先和*结合,说明p是一个指针变量,然后它指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组指针,叫数组指针。

可能有同学会有疑问,为什么p要和*先结合呢?
我们来看下图所示:

在这里插入图片描述
从图中我们可以清晰看到,[]的优先级是比* 要高的。所以我们必须加上()来保证p先和*结合。



2.3数组指针和指针数组的区别是什么?

这两个概念在词语虽然很相近,但事实是两个不同的事实。

2.3.1 数组指针和指针数组区别举例

代码如下:

int *a[5]int (*a)[5];

分析:**我们从上面代码,和刚刚给出操作符优先级的图可以得知,它们之前相差一个括号,根据优先级的关系(()>[]>*),如果不加括号,a会先与[]结合,加了括号,a则会和 *结合,由此可见,加括号为的是改变运算的结合顺序

  • int *a[5]没有括号,a会先与[]结合,所以它是一个数组。
  • int (*a)[5]加了括号,a会与 *结合,所以它是一个指针。

根据上面分析我们得出以下结论:

  • int * [5]是指针数组,它本质上是一个数组,数组里面存放的是指针,指针类型是int*,指向的是一个整形数组。
    int( * a)[5]是数组指针,它本质是一个指针,里面存放的是数组的地址,指向的是数组的类型是int[5]。


2.4 数组指针变量是怎么初始化

我们知道:数组指针变量是用来存放数组地址的,那如何获取数组的地址呢,我们之前学习的&数组名,这个&数组名可以把整个数组的地址取出来。

1. int arr[10]={0};
2. &arr;//得到的是数组的地址

如果我们要存放个数组的地址,就得存放在数组指针变量中,
代码如下:

int (*p)[10] =&arr

在这里插入图片描述
我们在调试中也能看到&arr和p的类型是完全一致的。
因此,那个数指针组类型解析如下:
在这里插入图片描述



3.二维数组传参的本质

有了数组指针的理解,我们就能够讲一下二维数组传参的本质了。
过去我们有一个二维数组传参给一个函数的时候,我们是这么写的:

#include <stdio.h>
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;
}

这里实参是二维数组,形参也可以写成二维数组的形式,那还有什么其他的写法呢?

首先我们可以再次理解一下二维数组:二维数组起始可以看做每个元素是一维数组的数组,也就是二维数组的每个元素是一个一维数组。那么二维数组的首元素就是第一行,是个一维数组。

如下图所示:
在这里插入图片描述

所以,我们根据数组名是数组首元素的地址这个规则,二维数组的数组名表示的就是第一行的地址,是一维数组的地址。根据上面的例子,第一行的一维数组的类型就是int[5],所以第一行的地址的类型就是数组指针类型int(*)[5]。那就意味着二维数组传参本质也是传递了地址,传递的是第一行这个一维数组的地址,那么形参也是可以写成指针形式的。

代码如下:

#include <stdio.h>
void test(int (*p)[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));}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;
}

可能有同学对上述代码不理解什么意思?

printf("%d ", *(*(p+i)+j));

我们就来解释一下吧~

  • 我们知道,二维数组的数组名是第一行数组的地址。至于我们为什么要+j?
  • 这是因为我们要拿到二维数组中每个元素的地址,而如果说不加j的话,我们总是拿到都是二维数组所在那行的第一个元素地址,这显然是不合理的。
  • 另外,我们看到外层还有一个*,因为是( * (p+i)+j)只是拿到的是每个元素的地址,但我们目的是要拿到它每个元素的值,因此我们还要再对它进行解引用操作。也就是在最外层的括号左边,再加个 * 才能获取二维数组每个元素的值

总结:二维数组传参,形参的部分可以写成数组,也可以写成指针形式。



4.函数指针变量

4.1 函数指针变量的创建

4.1.1什么是函数指针变量呢?

通过前面我们介绍整型指针,数组指针的时候,我们类比关系,不难的得出结论:
函数指针变量是用来存放函数地址,未来通过地址来调用函数的。

那么函数是否有地址呢?
我们不妨测试下面这几行代码:

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

输出结果如下:

我们可以看到vs编译器确实打印出来了地址。
因此我们可以得出以下结论:

  • 函数是有地址的。
  • 函数名就是函数的地址
  • 可以通过&函数名的方式获得函数的地址。

但如果我们要把函数的地址存放起来,那应该怎么做呢?

我们应该创建函数指针变量来存放函数的地址。

其实函数指针变量的写法其实和数组指针非常类似。
代码如下所示:

void test()
{printf("hehe\n");
}void (*pf1)() = &test;
void (*pf2)()= test;int Add(int x, int y)
{return x+y;
}int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的

函数指针类型解析:
在这里插入图片描述

4.2 函数指针变量的使用

这里我们来给大家演示一下函数指针指向的函数的例子。

在这里插入图片描述

	include <stdio.h>int Add(int x, int y){return x+y;}int main(){int(*pf3)(int, int) = Add;printf("%d\n", (*pf3)(2, 3));printf("%d\n", pf3(3, 5));return 0;}

在这里插入图片描述

4.3 两段有趣的代码

接下来有两段有趣的函数指针的代码,给大家讲一下代码里面的逻辑~

代码1:

(*(void (*)())0)();

这个代码的含义可能很多人都无法理解,但没事,接下来我将把代码拆解来给大家讲解一下。
我们先看下图:
在这里插入图片描述
解剖代码1

  • 从上图,我们知道:它其实把0这个整数强制转换为(void (*)()这种函数指针类型,想让0当做这种函数的地址。

当我们分析到这样子,这个代码的意思也就一目了然了。

它这个代码1本质的意思就是:
调用0地址处的函数,函数的参数为无参,返回的类型为void。

代码2:

void (*signal(int , void(*)(int)))(int);

我们继续来分析一下这个代码,看看它是什么意思。
同样地,我们可以把这个代码拆解成这样,方便大家可以理解。
在这里插入图片描述
解剖代码2:

从上面我们的代码分析图可以得知:

  • 声明的signal函数有2个参数,第一个参数是int类型的,第二个参数是函数指针类型的,该函数指向的是int类型,返回的类型是void。
  • signal函数的返回类型也是一个函数指针,该函数指针向的函数,参数是int,返回的类型也是void。

另外,这两段代码均出自:《C陷阱和缺陷》这本书,大家有兴趣的话可以找来看看~



4.4. typedef关键字

4.4.1 typedef关键字是什么?

typedef关键字,顾名思义,就是用来类型重名名的。可以将复杂的类型,简单化。

4.4.2 typedef 关键字用法举例

4.4.2.1 举例1

1.比如,我们觉得数据类型 unsingned int写起来不方便,如果写成uint就方便多了,那我们可以怎么写呢?
具体如下图所示:
在这里插入图片描述

4.4.2.1 举例2

如果是指针类型,能否重名呢?其实也是可以的,比如:将double*重命名为ptr,要怎么写呢?
具体写法如下所示:
在这里插入图片描述

4.4.2.1 举例3

那我们能否对数组指针和函数指针进行重命名呢,这个也是可以的。但是这个跟之前的重名稍微有点区别:
如果我们数组类型是int(*)[10],要重命名为tmp,那我们可以这样写:

typedef int(*tmp)[10];//新的类型名必须在*右边,这里就是将int(*)[10]类型重命名为tmp
int main() {int(*arr)[10];tmp v;return 0;
}

如下图所示:
在这里插入图片描述

同时,我们通过vs调试,发现变量v也是数组指针int(*)[10]类型。

4.4.2.1 举例4

同理:函数指针类型的重命名也是一样的,比如,将void(*)(int)类型重命名为ptr_t,就可以这样写:

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

如果说,我们要简化之前的这个代码,我们该怎么写呢?

void (*signal(int,void(*)(int)))(int);

如下图所示:在这里插入图片描述

从图中,我们发现当我们把函数指针类型重命名为parr_t,vs编译器是没有发生任何报错的。



5.函数指针数组

我们已经到知道:

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

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

5.1 什么是函数指针数组?

如果我们把函数的地址存到一个数组中,那这个数组就叫函数指针数组。


5.2 如何定义函数指针数组?

我们来看下这行代码:

int (*parr1[3])();

代码分析:

由于我们前面介绍过,[]的优先级比*要高,因此,[]会先和parr1结合,说明parr1是数组。那数组的内容是什么呢?答案是:int(*)();

5.3 函数指针数组举例

如下图所示:
在这里插入图片描述
代码分析:

从图中我们发现,由于函数指针p1和p2都是相同的类型,且返回的数据类型为int。因此我们可以创建一个函数指针数组来把两个函数指针的地址存进去。



6.转移表

我们前面介绍了函数指针数组和函数指针的概念和基本用法。
那它们的用途是什么呢:转移表
接下来我将介绍用函数指针函数指针数组作为转移表实现计算器的代码逻辑:

一般来说,我们实现计算器的逻辑是这么写的。

#include <stdio.h>
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("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:div \n");printf(" 0:exit \n");printf("*************************\n");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;default:printf("选择错误\n");break;}} 		while (input);return 0;
}

虽然这样这个计算器代码这么写也是可以的。
但是我认为这个代码有以下不足的:

  • switch语句中case子语句很多代码都是相似的,这样会显得有点冗余。
  • 如果这个计算器要增加更多的计算功能,比如:
    a%b,a&b,a^b,a|b。那这样的话就Switch的case子语句就会写得更多,这样整个代码逻辑就会变得复杂了。

那如何优化这个计算器的代码逻辑呢?

6.1.1用函数指针数组作为转移表实现计算器的代码逻辑

这个代码我们可以改进成这样:

#include <stdio.h>
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;
}void menu() {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(*ptr[5])(int x,int y) = {0,add,sub,mul,div};//转移表//至于这里为什么函数指针数组要写5个,主要是因为数组的下标是从0开始的,而这里存的add~div函数地址最好用下标1-4来表示,这样可以方便后续计算。do{menu();printf("请选择:");scanf("%d", &input);if (input >= 1 && input <= 4) {int x = 0;int y = 0;int ret = 0;printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = (*ptr[input])(x, y);printf("%d\n", ret);}else if (input == 0) {printf("退出计算器。\n");}else {printf("输入有误,请重新输入:\n");}} while (input);return 0;
}

分析代码

从上面这行代码:

int(*ptr[5])(int x,int y) = {0,add,sub,mul,div};

我们用函数指针数组ptr[5]来把add,sub,mul,div这四个函数的地址分别存进函数指针数组里面去。这里就相当于一个跳板,也就是我们说的转移表,后续根据用户输入input的值,通过函数数组指针ptr下标的值来调用所对应的函数。而那个变量x和y是调用函数时所传的参数。

6.1.2用函数指针作为转移表实现计算器的代码逻辑

代码如下:

#include <stdio.h>
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;
}void menu() {printf("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:div \n");printf(" 0:exit \n");printf("*************************\n");
}void calc(int(*ptr)(int x, int y)) {int x = 0;int y = 0;int ret = 0;printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = (*ptr)(x, y);printf("%d\n", ret);
}int main()
{int input = 0;do{menu();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");}}} while (input);return 0;
}

分析代码:

  • 从上述代码中,我们是封装了一个calc函数。根据用户输入input的值,进到switch所对应的case的子语句中,并把对应的函数地址传给calc函数。然后在calc函数它的参数是函数指针类型,返回类型的是int。由于我们只用在calc函数内部进行打印,因此只用在calc函数中左边写上个void类型即可。
  • 接着进入calc函数内部,根据用户输入变量x和y的值,将函数指针ptr的地址和参数x,y的值调用所对应的函数,并把最终计算的结果返回到ret,从而实现计算出两个操作数的结果出来。


**好了,今天的指针讲解3到这就结束了。 **
在这里插入图片描述
**如果觉得博主讲得不错的话,欢迎大家支持一下博主!!谢谢大家~ **

相关文章:

C语言-指针讲解(3)

文章目录 1.字符指针变量1.1 字符指针变量类型是什么1.2字符指针变量的两种使用方法&#xff1a;1.3字符指针笔试题讲解1.3.1 代码解剖 2.数组指针变量2.1 什么是数组指针2.2 数组指针变量是什么&#xff1f;2.2.3 数组指针变量的举例 2.3数组指针和指针数组的区别是什么&#…...

慢 SQL 分析及优化

目录 分析慢 SQL SQL 优化 单表优化 多表优化 慢 SQL&#xff1a;指 MySQL 中执行比较慢的 SQL排查慢 SQL 最常用的方法&#xff1a;通过慢查询日志来查找慢 SQL MySQL 的慢查询日志是 MySQL 提供的一种日志记录&#xff0c;它用来记录在 MySQL 中响应时间超过阈值的语句&…...

PTA:计算m到n之间所有素数的和

题目 计算m到n之间所有素数的和&#xff0c;其中 2 < m <n <100 输入格式: 请在这里写输入格式。例如&#xff1a;输入两个正整数 输出格式: 请在这里描述输出格式。例如&#xff1a;输出两个正整数之间的素数和。 样例 输入样例: 在这里给出一组输入。例如&…...

Golang实现YOLO:高性能目标检测算法

引言 目标检测是计算机视觉领域的重要任务&#xff0c;它不仅可以识别图像中的物体&#xff0c;还可以标记出物体的位置和边界框。YOLO&#xff08;You Only Look Once&#xff09;是一种先进的目标检测算法&#xff0c;以其高精度和实时性而闻名。本文将介绍如何使用Golang实…...

文档 + 模型

文档 模型 0: 基于MATLAB的soc锂电池系统设计 1: 电力系统继电保护仿真设计-毕业论文 2: 继电保护仿真-三段式电流保护的方案设计及分析-相间短路 3: 直流电机双闭环控制系统 转速电流双闭环调速 4: matlab电力系统继电保护仿真 三段电流保护仿真-双侧电源系统 5: OFDM-MIMO课…...

计算机毕业设计php+bootstrap小区物业管理系统

意义&#xff1a;随着我国经济的发展和人们生活水平的提高&#xff0c;住宅小区已经成为人们居住的主流&#xff0c;人们生活质量提高的同时&#xff0c;对小区物业管理的要求也越来越高&#xff0c;诸如对小区的维修维护&#xff0c;甚至对各项投诉都要求小区管理者做得好&…...

Osg线程模型(选择不当,会引发崩溃)

来自《最长的一帧》...

2161根据数字划分数组

给你一个下标从 0 开始的整数数组 nums 和一个整数 pivot 。请你将 nums 重新排列&#xff0c;使得以下条件均成立&#xff1a; 所有小于 pivot 的元素都出现在所有大于 pivot 的元素 之前 。所有等于 pivot 的元素都出现在小于和大于 pivot 的元素 中间 。小于 pivot 的元素之…...

Oracle Linux 9.3 发布

导读Oracle Linux 9 系列发布了第 3 个版本更新&#xff0c;支持 64 位 Intel 和 AMD (x86_64) 以及 64 位 Arm (aarch64) 平台。与所有的 Oracle Linux 版本一样&#xff0c;此版本与相应 RHEL 版本 100% 应用二进制兼容。 对于 x86_64 和 aarch64 架构&#xff0c;Oracle Li…...

XML Schema中的simpleContent 元素

XML Schema中的simpleContent 元素出现在complexType 内部&#xff0c;是对complexType 的一种扩展、或者限制。 simpleContent 元素在complexType元素内部最多只能出现1次。 simpleContent元素下面必须包含1个restriction或者extension元素。 例如&#xff0c;下面的Schema片…...

线性分类器--分类模型

记录学习 北京邮电大学计算机学院 鲁鹏 为什么从线性分类器开始&#xff1f;  形式简单、易于理解  通过层级结构&#xff08;神经网络&#xff09;或者高维映射&#xff08;支撑向量机&#xff09;可以 形成功能强大的非线性模型 什么是线性分类器&#xff1f; 线性分…...

【开源】基于Vue和SpringBoot的企业项目合同信息系统

项目编号&#xff1a; S 046 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S046&#xff0c;文末获取源码。} 项目编号&#xff1a;S046&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 合同审批模块2.3 合…...

指针数组用指针变量模拟二维数组

指针数组 指针数组是指针还是数组&#xff1f;&#xff1f; 整型数组&#xff1a;数组中的每个元素都是整型 字符数组&#xff1a;数组中的每个元素都是字符 指针数组&#xff1a;数组中的每个元素都是指针 #include<stdio.h> int main() { int a 10; int* …...

接口文档自动生成工具:详细教程和实用技巧

本篇文章详细教你如何使用 Apifox 的 IDEA 插件实现自动生成接口代码。好处简单总结有以下几点&#xff1a; 自动生成接口文档&#xff1a; 不用手写&#xff0c;一键点击就可以自动 生成文档&#xff0c;当有更新时&#xff0c;点击一下就可以自动同步接口文档&#xff1b;代…...

C语言--不创建第三个变量,实现对两个数字的交换

我们先来看一下&#xff0c;创建临时变量交换两个数字。 #include<stdio.h> {int a2;int b3;int tmp0;printf("before:a%d b%d\n",a,b);tmpa;ab;btmp;printf("after:a%d b%d\n",a,b);return 0; } 图解&#xff1a; 运行结果&#xff1a; 再看一下不…...

Java中的mysql——面试题+答案(数据库连接池,批处理操作)——第22期

当涉及Java中的MySQL时&#xff0c;面试题的范围可以涵盖更多方面&#xff0c;包括高级主题和实践经验。 什么是Hibernate&#xff1f;它与JDBC有什么区别&#xff1f; 答案&#xff1a; Hibernate是一个开源的对象关系映射&#xff08;ORM&#xff09;框架&#xff0c;它允许J…...

商用车的智慧眼车规级激光雷达

1、商用车自动驾驶技术&#xff1a;巨大的降本增效空间 2、感知是第一步&#xff1a;看懂环境路况才能安全的自动驾驶 3、多传感器融合&#xff0c;感知信息冗余&#xff0c;保障自动驾驶安全 4、商用车需要什么样的激光雷达 5、车规级激光雷达的软硬件成熟度及延展性 &#x…...

【NI-RIO入门】为CompactRIO供电

在大多数情况下&#xff0c;您可以使用可直接连接系统的电源&#xff0c;例如墙上的电源插座。但是&#xff0c;某些应用程序或环境缺乏可用电源&#xff0c;您必须使用其他电源&#xff0c;例如电池。无论您是否有可用电源&#xff0c;您可能都希望通过为系统提供一些冗余来确…...

【数据结构/C++】栈和队列_链队列

#include <iostream> using namespace std; // 链队列 typedef int ElemType; typedef struct LinkNode {ElemType data;struct LinkNode *next; } LinkNode; typedef struct {LinkNode *front, *rear; } LinkQueue; // 初始化 void InitQueue(LinkQueue &Q) {Q.fron…...

C#,《小白学程序》第二十一课:大数的减法(BigInteger Subtract)

1 文本格式 using System; using System.Linq; using System.Text; using System.Collections.Generic; /// <summary> /// 大数的&#xff08;加减乘除&#xff09;四则运算、阶乘运算 /// 乘法计算包括小学生算法、Karatsuba和Toom-Cook3算法 /// </summary> p…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

STM32HAL库USART源代码解析及应用

STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...