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

C语言:深入理解指针

一.内存和地址

我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中,那我们买电脑的时候,电脑上内存是 8GB/16GB/32GB 等,那这些内存空间如何高效的管理呢?其实也是把内存划分为⼀个个的内存单元,每个内存单元的大小取1个字节。
在这里稍微补充一下:一个比特位(bit)可以存储⼀个2进制的位1或者0,8个比特位构成一个字节。
1Byte = 8bit
1KB = 1024Byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1PB = 1024TB
每个内存单元也都有一个编号(这个编号就相当于宿舍房间的门牌号),有了这个内存单元的编号,CPU就可以快速找到⼀个内存空间。生活中我们把门牌号也叫地址,在计算机中我们把内存单元的编号也称为地址。C语言中给地址起了新的名字叫:指针。
所以我们可以理解为:
内存单元的编号 = 地址 = 指针
理解编址:CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,这就需要我们刚才所说的指针了。在计算机中的编址,并不是把每个字节的地址记录下来,而是通过硬件设计完成的。硬件与硬件之间是互相独立的,那么如何通信呢?答案很简单,用"线"连起来。而CPU和内存之间也是有大量的数据交互的,所以,两者必须也用线连起来。不过,我们今天关心一组线,叫做地址总线。硬件编址也是如此我们可以简单理解,32位机器有32根地址总线,每根线只有两态,表示1,0【电脉冲有无】,那么一根线,就能表示2种含义,2根线就能表示4种含义,依次类推。32根地址线,就能表示2^32种含义,每⼀种含义都代表一个地址。地址信息被下达给内存,在内存上,就可以找到该地址对应的数据,将数据在通过数据总线传入CPU内的寄存器。
03c1a51d81a44f1296408552cd92c9fd.png
二.指针变量和地址
1.取地址操作符
我们使用C语言创建变量时,其实是在向内存申请空间。在这里我们向内存申请4个字节(整型)的空间,用来存放1这个数值。这4个字节,每个字节都有编号(地址)。变量的名字仅仅是给程序员看的,编译器不看名字,编译器是通过地址找内存单元的。
982d5678976042b994e67b5730358675.png
那我们如何能得到a的地址呢?我们可以使用一个操作符(&)-取地址操作符。
f064d92306b54594aaa3c7de72777fef.png
这样我们就可以将a的地址取出来了。当然,&a取出的是a所占4个字节中地址较小的字节的地址。不过没关系,知道了第一个地址也可以顺藤摸瓜地知道另外几个地址了。
2.指针变量和解引用操作符(*)
我们先来看什么是指针变量,那我们通过取地址操作符(&)拿到的地址是⼀个数值,比如:0x006FFD70,这个数值有时候也是需要存储起来,方便后期再使用的,那我们把这样的地址值存放在哪里呢?答案是:指针变量中。 6f357b4f660a4307b400a079a109c08b.png
指针变量也是⼀种变量,这种变量就是用来存放地址的,存放在指针变量中的值都会理解为地址。在上面的代码中,int *是该指针变量的类型,int表示的是该指针指向的对象是整型类型的,而*表示这个变量为指针变量。
接下来,我们来看解引用操作符。我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针)指向的对象。这里就需要用到我们刚才说的解引用操作符(*)。
afc3d364a6f84494a716c2ff01d765b3.png
上⾯代码中第7行就使用了解引用操作符,*pa 的意思就是通过pa中存放的地址,找到指向的空间,*pa其实就是a变量了;所以*pa = 0,这个表达式就是把a改成了0。有人会想这里如果目的就是把a改成0的话,写成 a = 0; 不就完了,为啥非要使用指针呢? 其实这里是把a的修改交给了pa来操作,这样对a的修改,就多了一种的途径,写代码就会更加灵活,后期慢慢就能理解了。
3.指针变量的大小
在前面我们了解到,32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或者0,那我们把32根地址线产生的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4 个字节才能存储。 如果指针变量是用来存放地址的,那么指针变的大小就得是4个字节的空间才可以。同理64位机器,假设有64根地址线,一个地址就是64个二进制位组成的⼆进制序列,存储起来就需要 8个字节的空间,指针变量的大小就是8个字节。
#include<stdio.h>
int main()
{printf("%zd\n", sizeof(char *));printf("%zd\n", sizeof(int *));printf("%zd\n", sizeof(float *));printf("%zd\n", sizeof(double *));return 0;
}

根据我们在上面得出的结论,上图的代码在两种环境中得出的结果就不相同。8302ff95b736442f9243c10c109a59c6.png

三.指针变量类型的意义

指针变量的大小和类型无关,只要是指针变量,在同一个平台下,大小都是⼀样的,为什么还要有各种各样的指针类型呢?我们接下来慢慢分析。

1.指针的解引用

#include<stdio.h>
int main()
{int n = 0x11223344;char* pc = (char *) & n;*pc = 0;return 0;
}
#include<stdio.h>
int main()
{int n = 0x11223344;int* pc = & n;*pc = 0;return 0;
}
通过调试,我们可以看到,代码2会将n的4个字节全部改为0,但是代码1只是将n的第一个字节改为0。结论:指针的类型决定了,对指针解引用的时候有多大的权限(一次能操作几个字节)。
比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。
2.指针加减整数
443835666527472097b381c51003b57d.png我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。 这就是指针变量的类型差异带来的变化。指针+1,其实跳过1个指针指向的元素。指针可以+1,那也可以-1。
结论:指针的类型决定了指针向前或者向后走⼀步有多大(距离)。
3.void*指针
在指针类型中有⼀种特殊的类型是 void * 类型的,可以理解为无具体类型的指针(或者叫泛型指针),这种类型的指针可以用来接受任意类型地址。但是也有局限性, void* 类型的指针不能直接进行指针的+-整数和解引用的运算。 7a3da95f6283491cb5dc736eda396ecd.png
上图将该指针变量的类型本应该是int*,却使用了char*。所以下面就会报错误——类型不匹配。我们如果使用void*就不会出现这种情况。
四、const 修饰指针
1.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,使用n的地址,去修改n就能做到了,虽然这样做是在打破语法规则。 dbead977e4204c29bcda493724f68946.png
我们可以看到这里一个确实修改了,但是我们还是要思考⼀下,为什么n要被const修饰呢?就是为了不能被修改,如果p拿到n的地址就能修改n,这样就打破了const的限制,这是不合理的,所以应该让p拿到n的地址也不能修改n,那接下来怎么做呢?
2.const修饰指针变量

一般来讲const修饰指针变量,可以放在*的左边,也可以放在*的右边,意义是不一样的。

#include<stdio.h>
int main()
{int n = 10;int m = 20;int* pn = &n;*pn = 30;pn = &m;printf("%d", *pn);return 0;
}

这是没有const修饰下的效果。

#include<stdio.h>
int main()
{int n = 10;int m = 20;int const * pn = &n;*pn = 30;pn = &m;printf("%d", *pn);return 0;
}

518af7871e434d18a64ac98f7e94571c.png

当我们将const修饰指针变量放在*左边时,*pn就不能被改变了。

#include<stdio.h>
int main()
{int n = 10;int m = 20;int * const pn = &n;*pn = 30;pn = &m;printf("%d", *pn);return 0;
}

8a8af52bdb0c44ff9f9574a626773da6.png

当我们将const修饰指针变量放在*右边时,pn就不能被改变了。

#include<stdio.h>
int main()
{int n = 10;int m = 20;int const* const pn = &n;*pn = 30;pn = &m;printf("%d", *pn);return 0;
}

14166a811bc346f98b963c62e98c9d02.png

当我们将const修饰指针变量放在*两边边时,*pn和pn就都不能被改变了。

所以总的来说:const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。但是指针变量本⾝的内容可变。const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。
五、指针运算
1.指针+- 整数
指针+- 整数其实我们在前面已经说起过了。因为数组在内存中是连续存放的,只要知道第⼀个元素的地址,顺藤摸瓜就能找到后面的所有元素。 3d166c9013834081b513a36f90246482.png
2.指针-指针
#include<stdio.h>
int my_strlen(char * s)
{char* p = s;while (*p != '\0')p++;return p - s;
}
int main()
{printf("%d", my_strlen("abc"));return 0;
}

在上述代码中,我们就使用了指针-指针实现了strlen函数的效果。

3.指针的关系运算

975b1f2fd703448286e8ab0dd79bf61d.png

六、野指针

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。
什么情况会出现野指针呢,我们来分析一下。
1.指针未初始化
#include<stdio.h>
int main()
{int* p;*p = 10;return 0;
}

2.指针越界访问

#include<stdio.h>
int main()
{int arr[5] = { 0 };int* p = &arr[0];int sz = sizeof(arr) / sizeof(arr[0]);for (int i = 0; i <= sz; i++){*p = i;p++;}return 0;
}

3.指针指向的空间释放

#include<stdio.h>
int* test()
{int n = 10;return &n;
}
int main()
{int* p=test();printf("%d", *p);return 0;
}

当test()函数运行完时,n作为局部变量,内存将被回收。

七、如何规避野指针

1.指针初始化

如果明确知道指针指向哪里就直接赋值地址,如果不知道指针应该指向哪里,可以给指针赋值NULL。NULL 是C语言中定义的⼀个标识符常量,值是0,0也是地址,这个地址是无法使用的,读写该地址会报错。
#include<stdio.h>
int main()
{int* p= NULL;return 0;
}

2.小心指针越界

⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问。
3.指针变量不再使用时,及时置NULL,指针使用之前检查有效性
当指针变量指向⼀块区域的时候,我们可以通过指针访问该区域,后期不再使用这个指针访问空间的时候,我们可以把该指针置为NULL。因为约定俗成的⼀个规则就是:只要是NULL指针就不去访问,同时使用指针之前可以判断指针是否为NULL。
#include<stdio.h>
int main()
{int arr[5] = { 0 };int* p= &arr[0];for (int i = 0; i < 5; i++){*(p + i) = i;}//此时p已经越界了,可以把p置为NULLp = NULL;p = &arr[3];//重新让p获得地址//下次使⽤的时候,判断p不为NULL的时候再使⽤if (p != NULL){//......}return 0;
}

4.避免返回局部变量的地址

如造成野指针的第3个例子,不要返回局部变量的地址。
八、assert 断言
assert.h 头文件定义了宏 assert() ,用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行。这个宏常常被称为“断言”。
assert(p != NULL);
上面代码在程序运行到这一行语句时,验证变量 p 是否等于 NULL 。如果确实不等于 NULL ,程序继续运用,否则就会终止运行,并且给出报错信息提示。assert() 宏接受⼀个表达式作为参数。如果该表达式为真(返回值非零), assert() 不会产生任何作用,程序继续运行。如果该表达式为假(返回值为零), assert() 就会报错,在标准错误流 stderr 中写入一条错误信息,显示没有通过的表达式,以及包含这个表达式的文件名和行号。
使用 assert() 有几个好处:它不仅能自动标识文件和出问题的行号,还有⼀种无需更改代码就能开启或关闭 assert() 的机制。如果已经确认程序没有问题,不需要再做断言,就在 #include<assert.h> 语句的前面,定义⼀个宏 NDEBUG
#define NDEBUG
#include <assert.h>
 
然后,重新编译程序,编译器就会禁用文件中所有的 assert() 语句。如果程序有出现问题,可以移除这条 #define NDEBUG 指令(或者把它注释掉),再次编译,这样就重新启用了 assert() 语句。assert() 的缺点是,因为引⼊了额外的检查,增加了程序的运用时间。一般我们可以在 Debug 中使用,在 Release 版本中选择禁用 assert 就行,在 VS 这样的集成开发环境中,在 Release 版本中,直接就是优化掉了。这样在debug版本写有利于程序员排查问题,在 Release 版本不影响用户使用时程序的效率。
九、指针的使用和传址调用
1.strlen的模拟实现
库函数strlen的功能是求字符串长度,统计的是字符串中 \0 之前的字符的个数。
函数原型如下:
size_t strlen ( const char * str );
参数str接收⼀个字符串的起始地址,然后开始统计字符串中 \0 之前的字符个数,最终返回长度。
如果要模拟实现只要从起始地址开始向后逐个字符的遍历,只要不是 \0 字符,计数器就+1,这样直到 \0 就停止。
int length(const char* s)//防止*s在函数中被改变
{int count = 0;assert(s);//通过断言防止野指针while (*s){count++;s++;}return count;
}

2.传值调用和传址调用

a7c49d1b43a34ce0bb2673b894d68b5c.png

上面的调用就是传值调用,我们可以看出用这个方法交换数值很明显是错的,因为实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实参。那我们通过地址间接的操作main函数中的a和b,并达到交换的效果就好了。
#include<stdio.h>
void test(int* pa, int* pb)
{int item;item = *pa;*pa = *pb;*pb = item;
}
int main()
{int a = 1, b = 2;int* pa = &a;int* pb = &b;test(pa, pb);printf("%d %d", a, b);return 0;
}

我们将是a,b的地址作为实参传递给test函数,再通过解引用操作符的作用,我们可以将a,b所对应的数值进行交换。这种调用就叫传址调用。传址调用,可以让函数和主调函数之间建立真正的联系,在函数内部可以修改主调函数中的变量;所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采用传值调用。如果函数内部要修改主调函数中的变量的值,就需要传址调用。

 

 

相关文章:

C语言:深入理解指针

一.内存和地址 我们知道计算机上CPU&#xff08;中央处理器&#xff09;在处理数据的时候&#xff0c;需要的数据是在内存中读取的&#xff0c;处理后的数据也会放回内存中&#xff0c;那我们买电脑的时候&#xff0c;电脑上内存是 8GB/16GB/32GB 等&#xff0c;那这些内存空间…...

【WPF实现RichTextBox添加文本、自动滚动】

前言 使用WPF 中的RichTextBox控件实现添加文本后自动滚动末尾。因为RichTextBox无法直接绑定数据&#xff0c;所以通过引用System.Windows.Interactivity实现&#xff08;System.Windows.Interactivity.WPF&#xff09; 代码 MainWindow.xaml <Window x:Class"WPF…...

量化交易系统开发-实时行情自动化交易-8.4.MT4/MT5平台

19年创业做过一年的量化交易但没有成功&#xff0c;作为交易系统的开发人员积累了一些经验&#xff0c;最近想重新研究交易系统&#xff0c;一边整理一边写出来一些思考供大家参考&#xff0c;也希望跟做量化的朋友有更多的交流和合作。 接下来会对于MT4/MT5平台介绍。 MetaT…...

【HarmonyOS】@Observed和@ObjectLink嵌套对象属性更改UI不刷新问题

【HarmonyOS】Observed和ObjectLink嵌套对象属性更改UI不刷新问题 一、问题背景 使用了Observed和ObjectLink&#xff0c;修改嵌套对象的属性&#xff0c;UI还是不刷新&#xff0c;常见的问题有以下三种形式&#xff1a; 1.多级嵌套&#xff0c;嵌套对象的类并没有添加Observ…...

什么是默克尔树(Merkle Tree)?如何计算默克尔根?

默克尔树的概念 默克尔树(Merkle Tree)是一种特殊的二叉树&#xff0c;它的每个节点都存储了一个数据块的哈希值。哈希值是一种可以将任意长度的数据转换为固定长度的字符串的算法&#xff0c;它具有唯一性和不可逆性的特点&#xff0c;即不同的数据块会产生不同的哈希值&…...

眼部按摩仪WT2605音频蓝牙语音芯片方案 单芯片实现语音提示及控制/手机无线音频传输功能

随着科技的快速发展&#xff0c;人们的生活方式也在不断改变&#xff0c;智能化、便捷化的产品逐渐成为市场的主流。眼部按摩仪作为一种结合了现代科技与健康生活理念的产品&#xff0c;受到了广大消费者的青睐。而在众多眼部按摩仪中&#xff0c;采用WT2605音频蓝牙芯片的方案…...

python打包深度学习虚拟环境

今天师兄让我把环境打包发给他&#xff0c;我才知道可以直接打包深度学习虚拟环境&#xff0c;这样另一个人就不用辛辛苦苦的去装环境了&#xff0c;我们都知道有些论文他需要的环境很难装上。比如装Apex&#xff0c;装 DCN&#xff0c;mmcv-full 我现在把3090机子上的ppft虚拟…...

springboot358智慧社区居家养老健康管理系统(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 智慧社区居家养老健康管理系统设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&…...

复杂网络(二)

一、网络的基本静态几何特征 1.1 度分布 节点的度&#xff1a;在网络中&#xff0c;节点的邻边数称为该节点的度 对于网络中所有节点的度求平均&#xff0c;可得到网络的平均度 度分布&#xff1a;大多数实际网络中的节点的度满足一定的概率分布。定义P(k)为网络中度为k的节…...

Kubernetes 01

MESOS&#xff1a;APACHE 分布式资源管理框架 2019-5 Twitter退出&#xff0c;转向使用Kubernetes Docker Swarm 与Docker绑定&#xff0c;只对Docker的资源管理框架&#xff0c;阿里云默认Kubernetes Kubernetes&#xff1a;Google 10年的容器化基础框架&#xff0c;borg…...

node修改文件名称

node修改名称 var fs require(fs); const events require(events); var path require(path);init(); function init() {//要遍历的文件夹所在的路径const dirPath path.resolve(__dirname, "data");//遍历目录fileDisplay(dirPath); }/*** 文件遍历* param dirP…...

ArcGIS 软件中路网数据的制作

内容导读 路网数据是进行网络分析的基础&#xff0c;它是建立网络数据集的数据来源。 本文我们以OSM路网数据为例&#xff0c;详细介绍OSM路网数据从下载&#xff0c;到数据处理&#xff0c;添加属性&#xff0c;完成符合网络分析的网络数据集的全部过程。 01 数据获取 比较…...

transformers microsoft--table-transformer 表格识别

一、安装包 pip install transformers pip install torch pip install SentencePiecepip install timm pip install accelerate pip install pytesseract pillow pandas pip install tesseract 下载模型&#xff1a; https://huggingface.co/microsoft/table-transformer-s…...

【Spark源码分析】规则框架-草稿

规则批&#xff1a;规则集合序列&#xff0c;由名称、执行策略、规则列表组成。一个规则批里使用一个执行规则。 执行策略 FixedPointOnce 规则&#xff1a; #mermaid-svg-1cvqR4xkYpMuAs77 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px…...

迪米特原则的理解和实践

迪米特原则&#xff08;Law of Demeter&#xff0c;简称LoD&#xff09;&#xff0c;也被称为最少知识原则&#xff08;Least Knowledge Principle&#xff0c;LKP&#xff09;&#xff0c;是面向对象设计中的一个重要原则。其核心思想是&#xff1a;一个对象应该对其他对象有最…...

jQuery零基础入门速通(中)

大家好&#xff0c;我是小黄。 在上一篇文章中&#xff0c;我们初步了解了jQuery的基本概念、环境搭建、选择器、基本的DOM操作以及事件处理。接下来&#xff0c;我们将继续深入探讨jQuery的DOM操作和事件处理&#xff0c;以及一些实用的技巧和高级用法。 五、高级DOM操作 5…...

【设计模式系列】中介者模式(十八)

一、什么是中介者模式 中介者模式&#xff08;Mediator Pattern&#xff09;是一种行为型设计模式&#xff0c;其核心思想是通过一个中介者对象来封装一系列对象之间的交互&#xff0c;使这些对象不需要相互显式引用。中介者模式提供了一个中介层&#xff0c;用以协调各个对象…...

PDF版地形图矢量出现的问题

项目描述&#xff1a;已建风电场道路测绘项目&#xff0c;收集到的数据为PDF版本的地形图&#xff0c;图上标注了项目竣工时期的现状&#xff0c;之后项目对施工区域进行了复垦恢复地貌&#xff0c;现阶段需要准确的知道实际复垦修复之后的道路及其它临时用地的面积 解决方法&…...

小迪安全第四十二天笔记 简单的mysql注入 mysql的基础知识 用户管理数据库模式 mysql 写入与读取 跨库查询

前言 之前的安全开发我们学习了 php联动数据库的模式 &#xff0c;这个模式是现在常用的模式 这一节来学习 如何 进行数据库的注入和数据库相关知识 1、了解数据库的结构 我们使用 navicate连接数据库之后看一下 一共四层结构 库 》表》字段》数据 这个层级关系…...

11.25.2024刷华为OD

文章目录 HJ76 尼科彻斯定理&#xff08;观察题&#xff0c;不难&#xff09;HJ77 火车进站&#xff08;DFS&#xff09;HJ91 走格子方法&#xff0c;&#xff08;动态规划&#xff0c;递归&#xff0c;有代表性&#xff09;HJ93 数组分组&#xff08;递归&#xff09;语法知识…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...