13 学习总结:指针 · 其一
目录
一、内存和地址
(一)内存
(二)内存单元
(三)地址
(四)拓展:CPU与内存的联系
二、指针变量和地址
(一)创建变量的本质
(二)取地址操作符:&
(三)指针变量和解引用操作符:*
1、指针变量
2、指针变量的理解
(1)【int* pa】的理解
(2)【int*】的理解
3、解引用操作符:*
(四)指针变量的大小
三、指针变量类型的意义
(一)解引用操作时,决定可以操作多少个字节
(二)指针 + - 整数时,向前/向后走多大的区别
(三)void* 指针
四、const修饰指针
(一)const修饰变量
(二)const修饰指针变量
五、指针的运算
(一)指针 + 或 - 整数
(二)指针 - 指针
(三)指针的关系运算(指针的比较)
六、野指针
(一)野指针造成的原因
1、指针未初始化
2、指针越界访问
3、指针指向的空间释放
(二)如何规避野指针
1、指针初始化
2、小心指针越界
3、指针变量不再使用时,及时置NULL,指针使用之前检查有效性
4、避免返回局部变量的地址
七、assert断言
八、指针的使用和传址调用
(一)指针的使用:strlen的模拟实现
(二)传值调用和传址调用
一、内存和地址
(一)内存
又称内存储器或主存储器,计算机中所有程序的运行都在内存中进行,计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中,这样使用内存则需要高效地管理内存空间;
(二)内存单元
就是把内存划分为一个个的内存单元,每个内存单元的大小取1个字节(8个比特位),每个内存单元都有⼀个编号,有了这个内存单元的编号,CPU就可以快速找到⼀个内存空间;
(三)地址
在计算机中我们把【内存单元的编号】也称为【地址】,C语言中给【地址】起了新的名字叫:【指针】
可以理解为:【内存单元的编号 == 地址 == 指针】
(四)拓展:CPU与内存的联系
有三条总线将CPU与内存连接彼此,交换数据:①地址总线;②数据总线;③控制总线
交换过程:地址信息通过【地址总线】被下达给内存,在内存上就可以找到相应的数据,将数据通过【数据总线】传入CPU做处理,【控制总线】则负责传递对数据的操作,如读操作、写操作等
二、指针变量和地址
(一)创建变量的本质
创建变量的本质是在内存中申请空间,例如创建一个 int 变量就是向内存申请4个字节的空间,每个字节都有自己的编号(地址),变量的名字仅仅是给程序员看的,编译器不看名字,编译器是通过地址找内存单元的
(二)取地址操作符:&
使用:拿到变量的地址
例如:
int a = 10;&a;
&a 就可以拿到变量a的地址,虽然整型变量占用4个字节,我们只要知道了第⼀个字节地址,春藤摸瓜访问到4个字节的数据也是可行的
注:当一个变量占多个内存单元的时候,总会取出该变量的第一个内存单元(地址较小的那个字节)
(三)指针变量和解引用操作符:*
1、指针变量
通过取地址操作符(&)拿到的地址是⼀个数值,比如:0x0012ff40,这个数值有时候也是需要存储起来,方便后期再使用的,那我们把这样的地址值存放在哪里呢?答案是:指针变量中,例如:
#include <stdio.h>
int main()
{int a = 10;int * pa = &a;//取出a的地址并存储到指针变量pa中return 0;
}
指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址
2、指针变量的理解
上面例子的写法中的 int *pa 拆开来理解:
(1)【int* pa】的理解
①【int *】是变量pa的类型;
② pa是一个变量,用来存放地址(指针)的,所以pa又叫指针变量
(2)【int*】的理解
① * 表示pa是指针变量;
② int 表示【pa 指针变量中保存的地址】所指向的【变量 a】的类型是int
3、解引用操作符:*
又称为间接访问操作符,用法:
int main()
{int a = 100;int* pa = &a;*pa = 0;此处*pa == a,相当于对a进行修改return 0;
}
总结:通过【指针变量pa】找到指向的变量a—— *pa(通过pa的值,找到a)
① pa —— 指针变量
② &pa —— 指针变量pa的地址
③ *pa —— pa指向的变量a
(四)指针变量的大小
【指针变量类型的大小】取决于【地址的大小】,而地址大小由计算机是32位操作系统还是64位操作系统决定
① 指针变量是用来存放地址的,一个地址的存放需要多大空间,那么指针变量类型就是多大,所以32位平台总共有32根地址总线,每根线的电信号转化成数字信号后是1或0,那我们把32根地址总线产生的2进制序列作为一个地址,那么一个地址就是32个比特位,就是4个字节;同理,在64位的机器中,一个地址的大小就是8字节
② 地址的大小与【指向的原变量的类型大小】无关,就是4字节或者8字节
#include <stdio.h>//指针变量的⼤⼩取决于地址的⼤⼩
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)int main()
{printf("%zd\n", sizeof(char *));printf("%zd\n", sizeof(short *));printf("%zd\n", sizeof(int *));printf("%zd\n", sizeof(double *));return 0;
}
X86环境输出结果如下:
X64环境输出结果如下:
结论:
• 32位平台下地址是32个bit位,指针变量大小是4个字节
• 64位平台下地址是64个bit位,指针变量大小是8个字节
• 指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的
三、指针变量类型的意义
指针变量的大小和类型无关,只要是指针变量,在同⼀个平台下,大小都是⼀样的,都是4字节或者8字节,为什么还要有各种各样的指针类型呢?
(一)解引用操作时,决定可以操作多少个字节
如下演示:
#include <stdio.h>
int main()
{int a = 0x11223344;int* p = &a;*p = 0;return 0;
}
变量a的地址与4个字节的值如下:
经过 *p = 0;的语句后,4个字节的值全部改为0,如下:
若代码中指针变量的类型改为char*:
#include <stdio.h>
int main()
{int a = 0x11223344;char* p = &a;*p = 0;return 0;
}
变量a的地址与4个字节的值如下:
经过 *p = 0;的语句后,4个字节的值只有一个字节改为0,如下:
结论:指针的类型决定了,解引用操作时,决定可以操作多少个字节
比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节
(二)指针 + - 整数时,向前/向后走多大的区别
如下代码演示:
#include <stdio.h>
int main()
{int n = 10;char *pc = (char*)&n;int *pi = &n;printf("%p\n", &n);printf("%p\n", pc);printf("%p\n", pc+1);printf("%p\n", pi);printf("%p\n", pi+1);return 0;
}
代码结果如下:
从结果可以得出:char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节;
结论:指针的类型决定了指针向前或者向后走一步有多大(距离)
补充:
int* pa;
pa+1——> +1 * sizeof (int)
pa+n——> +n * sizeof (int)char* pa;
pa+1——> +1 * sizeof (char)
pa+n——> +n * sizeof (char)总结:
类型* 变量名;
变量名 + 1 ——> +1 * sizeof(指针指向的变量类型)
(三)void* 指针
void* ——无具体类型的指针(泛型指针)
可以接收任何类型的地址,但是正因为他是泛型指针,所以没有特定类型指针的用法,即无法解引用和进行指针的 + - 操作;
作用:⼀般 void* 类型的指针是使用在函数参数的部分,用来接收不同类型数据的地址,这样的设计可以实现泛型编程的效果,使得⼀个函数来处理多种类型的数据
四、const修饰指针
(一)const修饰变量
const修饰变量的时候,叫:常变量;
本质还是变量,只是不能被修改;
变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量,若不想变量被直接修改,就使用const修饰变量起限制作用
#include <stdio.h>
int main()
{int m = 0;m = 20;//m是可以修改的const int n = 0;n = 20;//n是不能被修改的return 0;
}
上述代码中n是不能被修改的,其实n本质是变量(无法在数组长度中使用),只不过被const修饰后,在语法上加了限制,只要我们在代码中对n进行修改,就不符合语法规则,就报错,致使没法直接修改n
但是可以拿到n的地址,通过指针对它进行修改,但这是在打破语法规则
int main()
{const int n = 0;printf("n = %d\n", n);int*p = &n;*p = 20;printf("n = %d\n", n);return 0;
}
结果如下:
这里的初衷是不让变量改变,但是通过指针还是能打破const的限制,接下来就要对这一象限改进,直接对指针变量做const限制
(二)const修饰指针变量
⼀般来讲const修饰指针变量,可以放在 * 的左边,也可以放在 * 的右边,意义是不⼀样的
int * p;//没有const修饰
int const * p;//const 放在*的左边做修饰
int * const p;//const 放在*的右边做修饰
如下代码演示:
代码一:
int a = 10;
int b = 20;
int const * p = &a;*p = 200;err
p = &b;√
代码一分析:
这个const限制的是 *p,即p指向的变量a不能改变;但是并没有限制p,所以可以修改p所指向的变量;
放在*的左边,限制的是指针指向的内容,也就是不能通过指针变量来修改它所指的内容;但是指针变量本身可以改变的
代码二:
int a = 10;
int b = 20;
int * const p = &a;*p = 200;√
p = &b;err
代码二分析:
放在*的右边,限制的是指针变量本身,也就是指针变量本身不可以改变,但可以通过指针变量来修改它所指的内容
结论:const修饰指针变量的时候
const如果放在 * 的左边,修饰的是【指针指向的内容 *p】,保证指针指向的内容不能通过指针来改变,但是【指针变量本身 p】的内容可变;
const如果放在*的右边,修饰的是【指针变量本身 p】,保证了指针变量的地址指向不能修改,但是【指针指向的内容*p】,可以通过指针改变
五、指针的运算
指针的基本运算有三种,分别是:
• 指针 + 或 - 整数
• 指针 - 指针
• 指针的关系运算(指针的比较)
(一)指针 + 或 - 整数
因为数组在内存中是连续存放的,只要知道第⼀个元素的地址,顺腾摸瓜就能找到后⾯的所有元素
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
#include <stdio.h>
//指针+- 整数
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};int *p = &arr[0];int sz = sizeof(arr)/sizeof(arr[0]);for(int i = 0; i < sz; i++){printf("%d ", *(p+i));//p+i 这⾥就是指针+整数}return 0;
}
注意:指针运算是指对 p 进行运算,而不是对*p,若对 *p 运算,就是对变量a运算了
在数组中,指针能够“顺腾摸瓜”的原因是:
①指针类型决定了【指针+1】的步长,和指针解引用之后的权限;
②数组在内存中的地址是连续的
错误演示代码:
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};char *p = &arr[0];int sz = sizeof(arr)/sizeof(arr[0]);for(int i = 0; i < sz; i++){printf("%d ", *p);p += 4;}return 0;
}
代码分析:
每次打印时,都让p += 4,在打印1~10时恰好正确,
每次访问都只会访问第一个字节,后面三个字节是直接跳过的,所以两位数的时候是正确的,但是数字大一些就会忽略掉第二个字节的数字,就会出错
(二)指针 - 指针
【指针 - 指针】的运算前提条件是两个指针指向的是同一个空间,否则运算无意义;
指针 - 指针的【绝对值】,是指针和指针之间【元素的个数】
应用:求字符串长度 ,如下代码演示:
#include <stdio.h>int my_strlen(char *s)
{char *p = s;//设置尾指针while(*p != '\0' )p++;return p-s;
}int main()
{printf("%d\n", my_strlen("abc"));return 0;
}
拓展:指针 + 指针?
答:无意义,类似于 【日期 +- 天数(计算日期)】、【日期 - 日期(算的是两个日期之间差多少天)】有意义,而【日期 + 日期】无意义
(三)指针的关系运算(指针的比较)
应用:做判断条件使用,数组中,若一个地址小于另一个地址,则执行语句
#include <stdio.h>int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};int *p = &arr[0];int sz = sizeof(arr)/sizeof(arr[0]);while(p<arr+sz) //指针的⼤⼩⽐较{printf("%d ", *p);p++;}return 0;
}
六、野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
(一)野指针造成的原因
1、指针未初始化
指针变量也是局部变量,不初始化就会给随机值;
如果将未初始化的指针变量的值作为地址来进行解引用操作,就会形成非法访问
#include <stdio.h>int main()
{ int *p;//局部变量指针未初始化,默认为随机值*p = 20;return 0;
}
2、指针越界访问
#include <stdio.h>int main()
{int arr[10] = {0};int *p = &arr[0];int i = 0;for(i=0; i<=11; i++){//当指针指向的范围超出数组arr的范围时,p就是野指针*(p++) = i;}return 0;
}
3、指针指向的空间释放
#include <stdio.h>int* test()
{int n = 100;return &n;
}int main()
{int* p = test();printf("%d\n", *p);return 0;
}
(二)如何规避野指针
1、指针初始化
如果明确知道指针指向哪里就直接赋值地址,如果不知道指针应该指向哪里,可以给指针赋值NULL,NULL 是C语言中定义的⼀个标识符常量,值是0(这个0在C语言中会被强制转化为void*类型),0也是地址,这个地址是无法使用的,读写该地址会报错
演示代码如下:
#include <stdio.h>
int main()
{int num = 10;int* p1 = #int* p2 = NULL;return 0;
}
2、小心指针越界
⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问
3、指针变量不再使用时,及时置NULL,指针使用之前检查有效性
当指针变量指向⼀块区域的时候,我们可以通过指针访问该区域,后期不再使用这个指针访问空间的时候,我们可以把该指针置为NULL;因为约定俗成的⼀个规则就是:只要是NULL指针就不去访问,同时使用指针之前可以判断指针是否为NULL
演示代码如下:
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};int *p = &arr[0];for(int i = 0; i<10; i++){*(p++) = i;}//此时p已经越界了,可以把p置为NULLp = NULL;//下次使⽤的时候,判断p不为NULL的时候再使⽤//...p = &arr[0];//重新让p获得地址if(p != NULL) //判断{//...}return 0;
}
4、避免返回局部变量的地址
不要返回局部变量的地址
七、assert断言
assert.h 头文件定义了宏 assert ( ) ,用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行,这个宏常常被称为“断言”
使用:#include <assert.h>;assert(表达式)
作用:判断是否符合指定条件,如果不符合就会终止运行;【通常用来判断指针变量的有效性】
判断:判断为真则程序继续向下走,判断为假则报错
int* p = NULL;
...
assert(p != NULL);
此处经过一些列的代码后,若 p 不等于NULL则正常运行下去,若还是等于NULL,则程序报错,终止运行
若想取消assert断言,则在#include <assert.h>上面 #define NDEBUG;
assert断言只在Debug版本中有效,在Release版本中会被优化掉
缺点:引入了额外的检查,增加了程序的运行时间
八、指针的使用和传址调用
(一)指针的使用:strlen的模拟实现
库函数strlen的功能是求字符串⻓度,统计的是字符串中 \0 之前的字符的个数
函数原型如下:
size_t strlen ( const char * str );
参数str接收⼀个字符串的起始地址,然后开始统计字符串中 \0 之前的字符个数,最终返回长度;
如果要模拟实现只要从起始地址开始向后逐个字符的遍历,只要不是 \0 字符,计数器就+1,这样直到 \0 就停止,代码如下:
size_t my_strlen(const char * str)
{int count = 0;assert(str);//为了保险,判断传来的是不是空地址while(*str){count++;str++;}return count;
}int main()
{size_t len = my_strlen("abcdef");printf("%zd\n", len);return 0;
}
注:代码中的 const(不希望原值被修改)和 assert(保险判断)来加强代码使用时的健壮性(鲁棒性)
(二)传值调用和传址调用
传址调用,可以让函数和主调函数之间建立真正的联系,在函数内部可以修改主调函数中的变量;所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采用传值调用,如果函数内部要修改主调函数中的变量的值,就需要传址调用
以上内容仅供分析,若有错误,请多多指正
相关文章:

13 学习总结:指针 · 其一
目录 一、内存和地址 (一)内存 (二)内存单元 (三)地址 (四)拓展:CPU与内存的联系 二、指针变量和地址 (一)创建变量的本质 (二…...

golang 项目打包部署环境变量设置
最近将 golang 项目打包部署在不同环境,总结一下自己的心得体会,供大家参考。 1、首先要明确自己目标服务器的系统类型(例如 windows 或者Linux) ,如果是Linux 还需要注意目标服务器的CPU架构(amd或者arm) 目标服务器的CPU架构可执行命令&…...

【Linux进程】进程优先级 Linux 2.6内核进程的调度
目录 前言 1. 进程优先级 2. 并发 3. Linux kernel 2.6 内核调度队列与调度原理 总结 前言 进程是资源分配的基本单位, 在OS中存在这很多的进程, 那么就必然存在着资源竞争的问题, 操作系统是如何进行资源分配的? 对于多个进程同时运行, 操作系统又是如何调度达到并发呢?…...

Linux中的粘滞位及mysql日期函数
只要用户具有目录的写权限, 用户就可以删除目录中的文件, 而不论这个用户是否有这个文件的写 权限. 为了解决这个不科学的问题, Linux引入了粘滞位的概念. 粘滞位 当一个目录被设置为"粘滞位"(用chmod t),则该目录下的文件只能由 一、超级管理员删除 二、该目录…...

BP神经网络的实践经验
目录 一、BP神经网络基础知识 1.BP神经网络 2.隐含层选取 3.激活函数 4.正向传递 5.反向传播 6.不拟合与过拟合 二、BP神经网络设计流程 1.数据处理 2.网络搭建 3.网络运行过程 三、BP神经网络优缺点与改进方案 1.BP神经网络的优缺点 2.改进方案 一、BP神经网络基…...

PCL 点云FPFH特征描述子
点云FPFH特征描述子 一、概述1.1 FPFH概念1.2 基本原理1.3 PFH和FPFH的区别二、代码实现三、结果示例一、概述 1.1 FPFH概念 快速点特征直方图(FPFH)描述子:计算 PFH 特征的效率其实是十分低的,这样的算法复杂度无法实现实时或接近实时的应用。因此,这篇文章将介绍 PFH 的简…...
基于golang的文章信息抓取
基于golang的文章信息抓取 学习golang爬虫,实现广度爬取,抓取特定的网页地址:测试站点新笔趣阁(https://www.xsbiquge.com/) 主要学习golang的goroutine和channel之间的协作,无限爬取站点小说的地址仅限书目…...

【手撕数据结构】卸甲时/空间复杂度
目录 前言时间复杂度概念⼤O的渐进表⽰法小试牛刀 空间复杂度 前言 要想知道什么是空/时间复杂度,就得知道什么是数据结构。 这得分两层来理解。我们生活中处处存在数据,什么抖音热点上的国际大事,什么懂的都懂的雍正卸甲等等一系列我们用户看得到的&a…...

消防认证-防火窗
一、消防认证 消防认证是指消防产品符合国家相关技术要求和标准,且通过了国家认证认可监督管理委员会审批,获得消防认证资质的认证机构颁发的证书,消防产品具有完好的防火功能,是住房和城乡建设领域验收的重要指标。 二、认证依据…...

C++进阶-二叉树进阶(二叉搜索树)
1. 二叉搜索树 1.1 二叉搜索树概念 二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树: 1.若它的左子树不为空,则左子树上所有节点的值都小于根节点的值2.若它的右子树不为空,则右子树上所有节点的值都大于…...

【Unity小知识】UnityEngine.UI程序集丢失的问题
问题表现 先来说一下问题的表现,今天在开发的时候工程突然出现了报错,编辑器提示UnityEngine.UI缺少程序集引用。 问题分析与解决(一) 既然是程序集缺失,我们首先查看一下工程项目是否引用了程序集。在项目引用中查找一…...

CentOS 离线安装部署 MySQL 8详细教程
1、简介 MySQL是一个流行的开源关系型数据库管理系统(RDBMS),它基于SQL(Structured Query Language,结构化查询语言)进行操作。MySQL最初由瑞典的MySQL AB公司开发,后来被Sun Microsystems公司…...

云计算【第一阶段(28)】DNS域名解析服务
一、DNS解析的定义与作用 1.1、DNS解析的定义 DNS解析(Domain Name System Resolution)是互联网服务中的一个核心环节,它负责将用户容易记住的域名转换成网络设备能够识别和使用的IP地址。一般来讲域名比 IP 地址更加的有含义、也更容易记住…...

pygame 音乐粒子特效
代码 import pygame import numpy as np import pymunk from pymunk import Vec2d import random import librosa import pydub# 初始化pygame pygame.init()# 创建屏幕 screen pygame.display.set_mode((1920*2-10, 1080*2-10)) clock pygame.time.Clock()# 加载音乐文件 a…...

Leetcode 295.数据流的中位数
295.数据流的中位数 问题描述 中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。 例如 arr [2,3,4] 的中位数是 3 。例如 arr [2,3] 的中位数是 (2 3) / 2 2.5 。 实现 MedianFinder 类: Media…...
A59 STM32_HAL库函数 之 TIM扩展驱动 -- A -- 所有函数的介绍及使用
A59 STM32_HAL库函数 之 TIM扩展驱动 -- A -- 所有函数的介绍及使用 1 该驱动函数预览1.1 HAL_TIMEx_HallSensor_Init1.2 HAL_TIMEx_HallSensor_DeInit1.3 HAL_TIMEx_HallSensor_MspInit1.4 HAL_TIMEx_HallSensor_MspDeInit1.5 HAL_TIMEx_HallSensor_Start1.6 HAL_TIMEx_HallSe…...

【Unity】UGUI的基本介绍
Unity的UGUI(Unity User Interface)是Unity引擎内自带的UI系统,官方称之为UnityUI,是目前Unity商业游戏开发中使用最广泛的UI系统开发解决方案。以下是关于Unity的UGUI的详细介绍: 一、UGUI的特点 灵活性:…...
MySQL 9.0新特性:向量存储
MySQL 9.0 正式版已经发布,其中一个亮点就是向量(VECTOR)数据类型的支持,本文给大家详细介绍一下这个新功能。 向量类型 MySQL 9.0 增加了一个新的向量数据类型:VECTOR。它是一种可以存储 N 个数据项的数据结构&…...
ruoyi实用性改造--(四)选择数据源及非标准使用数据库
一、实用型数据直接访问/** 使用Druid中 application-druid.yml 中定义的副数据源Connection con=null; //手工调用Druid的配置访问Connection con2=null;try {//DruidDataSource ds = SpringUtils.getBean("masterDataSource");DruidDataSource ds = Spring…...

HMI 的 UI 风格创造奇迹
HMI 的 UI 风格创造奇迹...

XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...