C语言 -- 深入理解指针(二)
C语言 -- 深入理解指针(二)
- 1. 数组名的理解
- 2. 使用指针访问数组
- 3. 一维数组传参的本质
- 4. 冒泡排序
- 5. 二级指针
- 6. 指针数组
- 7. 指针数组模拟二维数组
- 8. 字符指针变量
- 9. 数组指针变量
- 2.1数组指针变量是什么?
- 2.2 数组指针变量怎么初始化
- 10. 二维数组传参的本质
- 11. 函数指针变量
- 4.1 函数指针变量的创建
- 4.2 函数指针变量的使用
- 4.3 两段有趣的代码
- 4.3.1 typedef关键字
- 12. 函数指针数组
- 13. 转移表
- 14.回调函数是什么?
1. 数组名的理解
使用指针访问数组的内容时,有这样的代码:
&arr[0] 的方式拿到了数组第一个元素的地址
但是其实数组名arr本来就是地址,而且是数组首元素的地址
下面我们来看个代码:
我们发现数组名和数组首元素的地址打印出的结果一模一样,数组名就是数组首元素(第一个元素)的地址。
- 这时候有同学会有疑问?数组名如果是数组首元素的地址,那下面的代码怎么理解呢?
输出的结果是:40,如果arr是数组首元素的地址,那输出应该的应该是4/8才对。
其实数组名就是数组首元素(第一个元素)的地址是对的,但是有两个例外:
- sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的)
- 除此之外,任何地方使用数组名,数组名都表示首元素的地址。
这时有好奇的同学,再试一下这个代码:
三个打印结果一模一样,这时候又纳闷了,那arr和&arr有啥区别呢?
请看下面代码:
运行结果:
- 这里我们发现&arr[0]和&arr[0]+1相差4个字节,arr和arr+1 相差4个字节,是因为&arr[0] 和 arr 都是首元素的地址,+1就是跳过一个元素。
- 但是&arr 和 &arr+1相差40个字节,这就是因为&arr是数组的地址,+1 操作是跳过整个数组的。
- 到这里大家应该搞清楚数组名的意义了吧。
数组名是数组首元素的地址,但是有2个例外。
2. 使用指针访问数组
1.因为数组在内存中是连续存放的
2.数组名就是首元素的地址(方便找到起始位置)
所以可以用指针来访问数组
代码如下:
这个代码搞明白后,我们再试一下,如果我们再分析一下,数组名arr是数组首元素的地址,可以赋值给p,其实数组名arr和p在这里是等价的。
同理arr[i] 应该等价于 *(arr+i),数组元素的访问在编译器处理的时候,也是转换成首元素的地址+偏移量求出元素的地址,然后解引用来访问的。
3. 一维数组传参的本质
首先从一个问题开始,我们之前都是在函数外部计算数组的元素个数,那我们可以把函数传给一个函数后,函数内部求数组的元素个数吗?
代码如下:
我们发现在函数内部是没有正确获得数组的元素个数。
这就要学习数组传参的本质了,在上面我们学习了:数组名是数组首元素的地址;那么在数组传参的时候,传递的是数组名,也就是说本质上数组传参本质上传递的是数组首元素的地址。所以函数形参的部分理论上应该使用指针变量来接收首元素的地址。那么在函数内部我们写sizeof(arr) 计算的是一个地址的大小(单位字节)而不是数组的大小(单位字节)。正是因为函数的参数部分是本质是指针,所以在函数内部是没办法求的数组元素个数的。
再看一组打印数组的代码如下:
结论:数组传参本质上传递的是数组首元素的地址
一维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。
4. 冒泡排序
冒泡排序的核心思想就是:两两相邻的元素进行比较,如果不满足顺序就交换。
练习:写一个函数,对一个整形数组的数据进行排序。
n个元素,n-1趟
第一趟过程中,排n个元素,n-1对比较
第二趟过程中,排n-1个元素,n-2对比较
以此类推…
方法一:
运行结果:
方法二:优化上面代码 已经有序或者接近有序的数字就没必要重复比较了。
运行结果:
5. 二级指针
指针变量也是变量,是变量就有地址,二级指针变量是用来存放一级指针变量的地址。
对于二级指针的运算有:
- *ppa 通过对ppa中的地址进行解引用,这样找到的是 pa ,*ppa 其实访问的就是 pa .
- **ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a .
6. 指针数组
指针数组是指针还是数组?
我们类比一下
- 整型数组 - 存放整型的数组(数组中每个元素是整形类型)
- 字符数组 - 存放字符的数组(数组中每个元素是字符类型)
- 指针数组- 存放指针的数组(数组中每个元素是指针类型)
int arr [10] 整形数组
char ch [5] 字符数组
希望有一个数组,数组有4个元素,每个元素是整形指针
int * arr [4];
下面请看图例:
指针数组的每个元素都是用来存放地址(指针)的。
如下图:
指针数组的每个元素是地址,又可以指向一块区域。
7. 指针数组模拟二维数组
模拟出二维数组的效果,但不是二维数组。
二维数组的每一行是一个一维数组
看下面代码:
图示:
- parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型一维数组,parr[i][j]就是整型一维数组中的元素。
- 上述的代码模拟出二维数组的效果,实际上并非完全是二维数组,因为每一行并非是连续的。
8. 字符指针变量
在指针的类型中我们知道有一种指针类型为字符指针 char* ;
一般使用:
还有一种使用方式如下:
- 你可以把字符串想象为一个字符数组,但是这个数组是不能修改的
- 当常量字符串出现在表达式中的时候,他的值是第一个字符的地址
- 常量字符串是不能被修改的 ,所以要加const
4.const char* p = “abcdef”;//不是把字符串abcdef\0存放在p中,而是把第一个字符的地址存放在p中
《剑指offer》中收录了一道和字符串相关的笔试题,我们一起来学习一下:
运行结果:
这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。
9. 数组指针变量
2.1数组指针变量是什么?
- 整形指针变量: int * pint; 存放的是整形变量的地址,能够指向整形数据的指针。
- 浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。
- 数组指针变量应该是:存放的应该是数组的地址,能够指向数组的指针变量。
int *p1[10]; p1是数组,数组10个元素,每个元素的类型是int *,所以p1是指针数组
int (*p2)[10]; p2是指针,指向数组 ,数组有10个元素,每个元素的类型是int,p2是数组指针
数组指针变量
int (*p)[10];
- p先和*结合,说明p是一个指针变量,然后指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫 数组指针。
- 这里要注意:[]的优先级要高于号的,所以必须加上()来保证p先和结合。
2.2 数组指针变量怎么初始化
数组指针变量是用来存放数组地址的,那怎么获得数组的地址呢?就是我们之前学习的 &数组名
如果要存放个数组的地址,就得存放在数组指针变量中,如下:
例子:
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p1 = arr;//int* int*int* p2 = &arr[0];//int* int*int (*p3)[10] = &arr;//p3是数组指针 //&arr 和 p3的类型是完全一致的//int (*)[10] int (*)[10] //return 0;
}
数组指针类型解析:
10. 二维数组传参的本质
过去我们有一个二维数组的需要传参给一个函数的时候,我们是这样写的:
#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]
1.二维数组传参本质上也是传递了地址,传递的是第一行这个一维数组的地址,那么形参也是可以写成指针形式的。
2.二维数组传参,形参的部分可以写成数组,也可以写成指针形式。
代码如下:
#include <stdio.h>
void Print_arr(int(*arr)[5], int r, int c)
{int i = 0;for (i = 0; i < r; i++){int j = 0;for (j = 0; j < c; j++){//arr[i][j]printf("%d ", *(*(arr + 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} };Print_arr(arr, 3, 5); //传递的是第一行一维数组的地址return 0;
}
运行结果:
11. 函数指针变量
4.1 函数指针变量的创建
函数指针变量应该是用来存放函数地址的,未来通过地址能够调用函数的。
那么函数是否有地址呢?我们做个测试:
#include <stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{printf("%p\n", &Add);printf("%p\n", Add);return 0;
}
运行结果:
确实打印出来了地址,所以函数是有地址的,函数名就是函数的地址,当然也可以通过 &函数名 的方式获得函数的地址。
数组名–>数组首元素的地址
&数组名 -->整个数组的地址
函数名 -->函数的地址
&函数名 -->函数的地址
如果我们要将函数的地址存放起来,就得创建函数指针变量咯,函数指针变量的写法其实和数组指针
非常类似。如下:
#include <stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{int (*p)(int, int) = &Add;return 0;
}
4.2 函数指针变量的使用
通过函数指针调用指针指向的函数。
#include <stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{/*printf("%p\n", &Add);printf("%p\n", Add);*/int (*p)(int, int) = &Add; //<==> Addint ret = Add(3, 5);printf("%d\n", ret);int ret1 = (*p)(4, 5);int ret2 = p(5, 5);printf("%d\n", ret1);printf("%d\n", ret2);return 0;
}
运行结果:
1.把一个函数地址放到一个函数指针变量里面,函数指针变量指向的对象是一个函数,解引用就可以得到函数。
2.函数名= 函数的地址=Add ,就可以写成 函数的地址(3,5)
函数指针变量里面放的就是函数地址 也就可以写成 函数指针变量(3,5),因此Add(3,5) == (*p)(3,5) ==p(3,5)
3. Add(3,5) == (*p)(3,5) ==p(3,5) 叫法:通过函数指针来调用它所对应的函数。
4.3 两段有趣的代码
代码1:
(*(void (*)())0)(); //把0当作一个函数的地址
代码2:
void (*signal(int , void(*)(int)))(int);
typedef void(*pf_t)(int); //函数指针类型重命名
pf_t signal(int, pf_t);
上述代码是函数声明
1.signal是一个函数
2.signal函数的参数有2个,第一个是int类型,第二个是函数指针类型,该指针指向的函数参数是int,返回类型是void
3.signal函数的返回类型是这种类型的void(*)(int)函数指针
该指针指向的函数参数是int,返回类型是void
4.3.1 typedef关键字
typedef 是用来类型重命名的,可以将复杂的类型,简单化。
比如,你觉得 unsigned int 写起来不方便,如果能写成 uint 就方便多了,那么我们可以使用:
如果是指针类型,能否重命名呢?其实也是可以的,比如,将 int* 重命名为 ptr_t ,这样写:
但是对于数组指针和函数指针稍微有点区别:
比如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:
函数指针类型的重命名也是⼀样的,比如,将 void( * )(int) 类型重命名为 pf_t ,就可以这样写:
例子:
typedef unsigned int uint; typedef int(*pArr_t)[10] ; //数组指针类型重命名typedef int (*pf_t)(int, int) ; //函数指针类型重命名int main()
{unsigned int num;uint num2;pArr_t pa; //数组指针变量int(*pb)[10]; //数组指针变量pf_t pf; //函数指针变量int (*pf2)(int, int); //函数指针变量return 0;
}
12. 函数指针数组
数组是⼀个存放相同类型数据的存储空间,我们已经学习了指针数组,
比如:
那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
答案是: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 (*pf)(int, int) = Add;//pf是函数指针int (* pfArr[4])(int, int) = {Add, Sub, Mul, Div};//存放函数指针的数组-函数指针数组//0 1 2 3int i = 0;for (i=0; i < 4; i++){int ret = pfArr[i](6, 2);printf("%d\n", ret);}return 0;
}
运行结果:
13. 转移表
函数指针数组的用途:转移表
举例:计算器的一般实现:
#include <stdio.h>
void menu()
{printf("******************************\n");printf("**** 1. add 2. sub ****\n");printf("**** 3. mul 4. div ****\n");printf("**** 0. exit ****\n");printf("******************************\n");
}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 input = 0;int x = 0;int y = 0;int ret = 0;do{menu();printf("请选择:");scanf("%d", &input);switch (input){case 1:printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = Add(x, y);printf("%d\n", ret);break;case 2:printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = Sub(x, y);printf("%d\n", ret);break;case 3:printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = Mul(x, y);printf("%d\n", ret);break;case 4:printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = Div(x, y);printf("%d\n", ret);break;case 0:printf("退出计算器\n");break;default:printf("选择错误,重新选择\n");break;}} while (input);return 0;
}
运行结果:
使用函数指针数组的实现:
#include <stdio.h>
void menu()
{printf("******************************\n");printf("**** 1. add 2. sub ****\n");printf("**** 3. mul 4. div ****\n");printf("**** 0. exit ****\n");printf("******************************\n");
}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 input = 0;int x = 0;int y = 0;int ret = 0;do{menu();//函数指针数组的方式解决一下//这里的函数指针数组,我们称为转移表//int (*pfArr[])(int, int) = { NULL, Add, Sub, Mul, Div };// 0 1 2 3 4printf("请选择:");scanf("%d", &input);if (input == 0){printf("退出计算器\n");}else if (input >= 1 && input <= 4){printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = pfArr[input](x, y);printf("%d\n", ret);}else{printf("选择错误,重新选择\n");}} while (input);return 0;
}
运行结果:
14.回调函数是什么?
函数指针可以用来实现回调函数。
- 如果你把函数的指针(地址)作为参数传递给另一个函数,通过这个指针函数去调用对应的函数时,被调用的函数就是回调函数。
- 回调函数不是由该函数的实现方直接调用,而是在特定的事件或条 件发生时由另外的一方调用的,用于对该事件或条件进行响应。
- 这里我们可以把调用的函数的地址以参数的形式传递过去,使用函数指针接收,函数指针指向什么函数就调用什么函数,这里其实使用的就是回调函数的功能。
#include <stdio.h>
void menu()
{printf("******************************\n");printf("**** 1. add 2. sub ****\n");printf("**** 3. mul 4. div ****\n");printf("**** 0. exit ****\n");printf("******************************\n");
}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;
}//calc功能强大了void calc(int (*pf)(int,int))
{int x = 0;int y = 0;int ret = 0;printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = pf(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");break;}} while (input);return 0;
}
代码实现过程图解:
运行结果:
完
相关文章:

C语言 -- 深入理解指针(二)
C语言 -- 深入理解指针(二) 1. 数组名的理解2. 使用指针访问数组3. 一维数组传参的本质4. 冒泡排序5. 二级指针6. 指针数组7. 指针数组模拟二维数组8. 字符指针变量9. 数组指针变量2.1数组指针变量是什么?2.2 数组指针变量怎么初始化 10. 二维…...

HTTP协议详解
HTTP协议详解 一、HTTP协议概述二、网络基础与HTTP2.1 TCP/IP协议2.2 发送HTTP请求过程2.3 HTTP请求的组成部分 三、HTTP报文HTTP请求报文HTTP响应报文 结语 一、HTTP协议概述 HTTP,即超文本传输协议(Hypertext Transfer Protocol)ÿ…...

一年时间业绩增长2倍,茅台保健酒业公司在川销售的“三板斧”
执笔 | 尼 奥 编辑 | 扬 灵 作为土地面积全国第5、人口总数全国第3、GDP全国第6的产酒、销酒大省,四川酒类消费总额已达800亿元,其中白酒市场规模达到500亿元。 近年来,随着省外名酒提升对四川市场重视,其市场份额也从20年前的3%…...

土豆炒肉做法
菜单:土豆、葱、铁辣子、纯瘦肉、淀粉、生抽、酱油、刀、案板、十三香、盐巴、擦板 流程: 洗土豆,削皮,擦成条,用凉水过滤两遍淀粉,顺便放个燥里洗肉,切成条,按照生抽、酱油、淀粉、…...

VPS拨号服务器:独享的高效与安全
在当今互联网高速发展的时代,虚拟私人服务器(VPS)已成为许多企业和个人用户托管网站、应用程序的首选。特别是带有拨号功能的VPS服务器,以其独特的优势受到广泛关注。本文将深入探讨VPS拨号服务器的独享特性,以及它如何…...

网络安全设备——防火墙
网络安全设备防火墙是一种用来加强网络之间访问控制的特殊网络互联设备。以下是对防火墙的详细解释: 一、定义与基本概念 定义:防火墙是指设置在不同网络(如可信任的企业内部网和不可信的公共网)或网络安全域之间的一系列部件的…...

Redis 管道技术
Redis 管道技术 引言 Redis,作为一个高性能的键值存储系统,被广泛应用于各种场景,如缓存、消息队列等。为了进一步提高Redis的处理能力和效率,Redis管道技术应运而生。本文将深入探讨Redis管道技术的原理、应用及其优势。 什么是Redis管道技术 Redis管道技术是一种允许…...

使用vue3-treeselect问题
1.当vue3-treeselect是单选时,使用watch监听绑定value,无法监听到值清空 对照后将:value改为v-model,如图 2.使用vue3-treeselect全部清空按钮如何置空select的值,使用watch监听 多选:pageInfo.officeName(val) {// …...

每日直播分享车载知识:硬件在环、UDS诊断、OTA升级、TBOX测试、CANoe、ECU刷写、CAN一致性测试:物理层、数据链路层等
每日直播时间:(进腾讯会议方式:QazWsxEdc_2010) 周一到周五:20:00-23:00(讲一个小时,实操两个小时) 周六与周日:9:00-17:0…...

flex布局---子元素未设置高度,默认与父元素同高---侧轴方向的拉伸
父元素未设置固定高度,由子元素高度撑开,并给父元素开启 flex 布局,成为伸缩容器。 父元素中有三个子元素,为伸缩项目,三个伸缩项目都未指定高度,div.inner1 的高度由内容撑开,div.inner2 和 d…...

资源分享—2021版三调符号库
汇总整理平台软件支持过程中客户项目提供的各类资源(包括但不限于符号库、地图模板等),在客户允许情况下进行集团内分享。 本次分享新版国土空间规划【三调符号库(2021版)】,提供SuperMap格式符号库下载。 …...

解决selenium手动下载驱动问题
解决selenium手动下载驱动问题 每次都需要手动下载驱动很头疼,今天发现一个可以自动下载最新驱动的包webdriver_manager,挺不错的 安装依赖包 pip install selenium pip install webdriver_manager from selenium import webdriver from selenium.webdr…...

使用fifo IP核,给fifo写数据,当检测到ALMOST_EMPTY时,为什么不能立即赋值
这涉及到FIFO(先入先出缓冲器)的内部工作机制和时序考虑。让我详细解释为什么在检测到ALMOST_EMPTY信号时不能立即向FIFO写入数据。 ALMOST_EMPTY信号的特性: ALMOST_EMPTY是一个预警信号,表示FIFO中的数据量已经接近空。这个信号…...

【Python123题库】#汽车迷 #编写函数输出自除数 #身份证号基本信息
禁止转载,原文:https://blog.csdn.net/qq_45801887/article/details/140080109 参考教程:B站视频讲解——https://space.bilibili.com/3546616042621301 有帮助麻烦点个赞 ~ ~ Python123题库 汽车迷编写函数输出自除数身份证号基本信息 汽车…...

普通人怎么利用GPT赚钱之SEO优化内容
如何利用GPT撰写SEO优化内容:全面指南 在当今的数字化世界,搜索引擎优化(SEO)是提升网站流量和曝光率的关键。高质量的SEO优化内容不仅可以提高搜索引擎排名,还能吸引更多潜在客户。GPT(生成预训练变换器)作为一种先进的人工智能工具,可以大大提升SEO内容创作的效率和…...

LeetCode热题100刷题8:54. 螺旋矩阵、73. 矩阵置零、48. 旋转图像
54. 螺旋矩阵 class Solution { public:vector<int> spiralOrder(vector<vector<int>>& matrix) {vector<int> vec;if(matrix.empty())return vec;int left0;int right matrix[0].size()-1;int up0;int down matrix.size()-1;while(true) {for(i…...

景联文科技打造高质量图文推理问答数据集,赋能大语言模型提升推理能力
大语言模型在处理推理任务时,不同于人类能够反思错误并修正思维路径,当它遇到自身知识盲区时,缺乏自我校正机制,往往导致输出结果不仅无法改善,反而可能变得更不准确。 需要依赖外部的知识库和推理能力来克服其在理解和…...

用网络编程完成windows和linux跨平台之间的通信(服务器)
服务器代码逻辑: 服务器功能 创建 Socket: 服务器首先创建一个 Socket 对象,用于进行网络通信。通常使用 socket() 函数创建。 绑定(Bind): 服务器将 Socket 绑定到一个特定的 IP 地址和端口号上。这是通过…...

力扣3148.矩阵中的最大得分
力扣3148.矩阵中的最大得分 类似二维前缀和 枚举右下角 求(i,j) - (0,0)的子矩阵的最小值每次与当前点作差 求答案 class Solution {public:int maxScore(vector<vector<int>>& grid) {int res INT_MIN;int m grid.size(),n grid[0].size();vector<ve…...

解决数据库PGSQL,在Mybatis中创建临时表报错TODO IDENTIFIER,连接池用的Druid。更换最新版本Druid仍然报错解决
Druid版本1.1.9报错Caused by: java.sql.SQLException: sql injection violation, syntax error: TODO IDENTIFIER : CREATE TEMPORARY TABLE temp_ball_classify (id int8 NOT NULL,create_time TIMESTAMP,create_by VARCHAR,classify_name VARCHAR) 代码如下: 测…...

【WPF】桌面程序开发之xaml页面基础布局方式详解
使用Visual Studio开发工具,我们可以编写在Windows系统上运行的桌面应用程序。其中,WPF(Windows Presentation Foundation)项目是一种常见的选择。然而,对于初学者来说,WPF项目中xaml页面的布局设计可能是一…...

第十五章 Nest Pipe(内置及自定义)
NestJS的Pipe是一个用于数据转换和验证的特殊装饰器。Pipe可以应用于控制器(Controller)的处理方法(Handler)和中间件(Middleware),用于处理传入的数据。它可以用来转换和验证数据,确…...

实战篇(八):使用Processing创建动态图形:诡异八爪鱼
使用Processing创建动态图形:诡异八爪鱼 引言 在这个教程中,我们将深入探讨如何使用Processing编程语言创建一个动态的图形效果。我们将通过一个具体的例子,展示如何绘制一个跟随鼠标移动的“鱿鱼”图形,并使其颜色和形状动态变化。这个项目不仅适合初学者学习Processing…...

大模型成为软件和数据工程师
前言 想象一下这样一个世界:人工智能伙伴负责编码工作,让软件和数据工程师释放他们的创造天赋来应对未来的技术挑战! 想象一下:你是一名软件工程师,埋头于堆积如山的代码中,淹没在无数的错误中࿰…...

【鸿蒙学习笔记】页面布局
官方文档:布局概述 常见页面结构图 布局元素的组成 线性布局(Row、Column) 了解思路即可,更多样例去看官方文档 Entry Component struct PracExample {build() {Column() {Column({ space: 20 }) {Text(space: 20).fontSize(15)…...

GIT 使用相关技巧记录
目录 1、commit 用户信息变更 全局用户信息(没有特殊配置的情况下默认直接用全局信息) 特定仓库用户信息(只针对于当前项目) 方法一:修改config文件 方法二:命令方式 2、idea同一代码推向多个远端仓库…...

1-认识网络爬虫
1.什么是网络爬虫 网络爬虫(Web Crawler)又称网络蜘蛛、网络机器人,它是一种按照一定规则,自动浏览万维网的程序或脚本。通俗地讲,网络爬虫就是一个模拟真人浏览万维网行为的程序,这个程序可以代替真人…...

ROS2使用Python开发动作通信
1.创建接口节点 cd chapt4_ws/ ros2 pkg create robot_control_interfaces --build-type ament_cmake --destination-directory src --maintainer-name "joe" --maintainer-email "1027038527qq.com" mkdir -p src/robot_control_interfaces/action touch…...

Bug记录:【com.fasterxml.jackson.databind.exc.InvalidDefinitionException】
bug记录 序列化错误 异常com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 完整错误(主要是FAIL_ON_EMPTY_BEANS) 00:15:20.250 [http-nio-3000-exec-1] ERROR org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet] - S…...

Mongodb索引的删除
学习mongodb,体会mongodb的每一个使用细节,欢迎阅读威赞的文章。这是威赞发布的第87篇mongodb技术文章,欢迎浏览本专栏威赞发布的其他文章。如果您认为我的文章对您有帮助或者解决您的问题,欢迎在文章下面点个赞,或者关…...