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

深入理解指针1

深入理解指针1

一.内存和地址以及指针间的关系

举一个生活中的例子,假如你去找你的朋友玩,你的朋友告诉了你酒店的名字,但是没有告诉告诉你他具体住哪一件房间,于是你为了找到你的朋友,只好一间房间一间房间的去找,但是如果告诉了你房间号,那你就可以很快的找到你的朋友,所以在生活中有了具体的地址,就可以很快的提高效率。


那么在计算机中也是一样的,计算机的 cpu 处理的数据都是在内存中获取的,处理好的数据,也会放到内存里,那么内存是如何管理自己的数据的?其实,内存里会划分很多个内存单元,每一个内存单元占一个字节空间,也就是8个比特位,每个内存单元都会有自己的编号,而这个内存单元编号就相当于酒店的房间号,也就是地址,有了地址,我们就可以访问 cpu里的数据。我们可以把内存看成一个酒店,内存单元


总结:

我们可以把内存看成一个酒店,内存单元看成酒店里的每个房间,房间号看成内存单元的编号,这个房间号就是一个地址,而房间号=内存编号=地址=指针,所以指针就是地址

1.内存中常见的单位:

bit ——  比特位                
byte —— 字节                  
KB                          
MB							
GB
TB
PB
 1byte=8bit1KB=1024B1MB=1024KB1GB=1024MB1TB=1024GB1PB=1024TB

2.内存究竟是如何编址的呢?

cpu 访问内存中的数据前,必须要知道它的地址,因为内存中很多字节,所以要给内存进行编址,就相当于酒店里有很多间房间,为了方便管理,我们需要给房间编上号码。那么,内存是如何进行编址的呢?
计算机中的编址是通过硬件设备完成的。我们都知道计算机中有很多硬件设备,它们的通信就是用线连接起来的,有数据总线,地址总线,控制总线,但是今天我们只讨论地址总线,因为地址总线是用来传输内存单元编号的,假设你的电脑是32位的,那么就有32根地址总线,每一根地址总线传输一个比特位,每根线表示两种含义,0或1,那么两根地址总线就可以表示22种含义,那么32根地址总线就可以表示232种含义,

地址总线将地址传给内存,内存找到该地所对应的数据,再通过数据总线将数据传给 cpu 内寄存器。

二.指针变量和地址

1. 取地址操作符&

c语言中创建变量的本质就是在内存中申请一块内存空间,就相当于你去酒店住房,需要找前台给你开一个房间,比如int a=10;,就是向内存中申请4个字节的空间,用来存放10,每一个字节都有对应的地址,

注意!!!&取地址操作符要与按位与操作符&区分开来,按位与操作符是双目符,也就是有2个操作数,而取地址操作符是单目操作符,他的意思是取内存单元中的编号,也就是地址,即指针。

2.指针变量

&取地址操作符取出来的地址也是1个数值,有的时候我们想把这个数值存储起来,方便后期使用,那么我们就可以存到指针变量里,这个指针变量就是专门用来存放地址的,换句话说,存在指针变量里的值,都会被当作地址使用。

3.如何创建指针变量

int main()
{int a = 10;int *p = &a; //把a的内存单元编号,即地址取出来,然后存放到指针变量p里面//int*是变量p的类型//int表示指针变量所指向的地址里面的数据是整型//*解引用操作符,表示p是指针变量//总结:创建指针变量的时候要说明  指针变量类型*  变量名return 0;
}
char ch='a';
char* p=&a;//把a的地址取出来,然后赋值给指针变量p
printf("%p",p);
//char*是变量p的类型,*表示p是一个指针变量,char表示指针指向一个字符类型的数据

4.解引用操作符*(间接访问操作符)

我们将地址保存了起来,以后如果想使用这个地址访问到他里面存放的数据,怎么操作呢?就好比你找到了酒店房间的地址,你想进这个房间拿里面的物品,或打扫卫生,但是你没有钥匙。所以接下来我们需要了解一个操作符,解引用操作符(‘*’),它就相当于一把钥匙,有了他,我们只要拿到了地址,就可以通过地址,找到地址指向的对象。

int main()
{int a = 10;int* p = &a;//创建指针变量p,存放a的地址*p = 0;//*p的意思是找到p所指向的内存空间,然后访问里面的数据,把a的值修改成0printf("%d", a);return 0;
}

5.指针变量大小

int类型的变量是4个字节,char类型的变量是1个字节,那指针变量占内存多少个字节呢?

sizeof() 操作符计算变量或类型的大小,即在内存中占多少个字节。

printf("%d\n",sizeof(int));
printf("%d\n",sizeof(float));
sizeof("%d\n",sizeof(double));
printf("%d\n",sizeof(long));
printf("%d\n",sizeof(char));

判断下面两个指针变量所占内存大小,

int a=10;
char ch='a';
int*p=&a;
char*arr=&ch;
printf("%d\n",sizeof(p));
printf("%d\n",sizeof(arr));
printf("%d\n",sizeof(int*));
printf("%d\n",sizeof(char*));
//指针变量p和arr的大小都是一样的,指针变量大小却决于地址大小,如果是32位环境,那么指针变量的大小都是4个字节,如果是64位环境,指针变量大小就是8个字节

指针变量大小与类型无关,取决于地址大小

printf("%d\n",sizeof(int*));
printf("%d\n",sizeof(char*));
printf("%d\n",sizeof(float*));
printf("%d\n",sizeof(double*));
printf("%d\n",sizeof(long*));

总结:

//指针变量的大小取决于地址的大小,32位平台下,指针大小是4个字节
//64位环境下,指针大小是8个字节,在相同的平台下,指针大小是相同的

6.指针变量类型的意义

既然在相同的平台下,指针大小都是一样的,与指针的类型无关,那么为什么还要有各种类型的指针呢?
其实指针的类型是有意义的,指针变量的类型决定了指针在解引用的时,就是访问指针指向的对象的时候,一次访问几个字节,

1.指针变量的类型决定了指针解引用时的权限
//对比下面两组代码,并用调试观察他们在内存中的变化
int a=0x11223344;//0x开头表示十六进制
int*p=&a;
*p=0;
printf("%d",a);
int a=0x11223344;
char* p2=&a;
*p2=0;
printf("%d",a);

如果是int*类型的,那么指针访问的时候,就会访问4个字节

如果是char *,那么指针访问的时候,就会访问1个字节所以通过解引用操作符将a的值修改成0的时候,因为只拿到第一个字节里的数据,所以只修改第一个字节里的数据。

2.指针变量类型决定了指针的步长

指针变量的类型决定了指针==+1/-1==时,一次跳过几个字节,相当于指针的步长;

或者说指针变量的类型==+n/-n==时,一次跳过了n个值,如果是char,一次跳过n个字符,如果是int*;一次跳过n个整型。*

7.指针加减整数

int main()
{int a = 10;char* pc = &a;int* pi = &a;printf("%p\n", &a);printf("%p\n", pc);printf("%p\n", pi);printf("char*类型:%p\n", pc+1);printf("int*类型:%p\n", pi+1);return 0;
}
//这是上面代码的运行结果,我们发现char*类型的指针+1,一次跳过了一个字节,而int*类型指针+1,一次跳过了4个字节

总结:

指针变量的类型决定了指针+n/-n时,一次跳过几个整型或几个字符。

如果是char*类型+1,一次跳过1个字节,跳过一个字符;

如果是int*类型+1,一次跳过4个字节,跳过1个整型

8.指针变量类型的使用

竟然知道了指针类型具有特殊的意义,那么怎么使用呢?
之前遍历数组元素是用的下标的方式,现在学了指针类型的特殊意义,我们可以用指针来访问数组元素
在写代码之前我们要想清楚是一个整型一个整型的访问呢,还是一个字符一个字符的访问呢

//访问数组元素之下标方式
int arr[]={1,2,3,4,5,6,7,8,9,10};
int i=0;
int sz=sizeof(arr)/sizeof(arr[0]);
for(i=0;i<sz;i++)
{printf("%d ",arr[i]);}
//访问数组元素之指针方式,因为数组在内存中是连续存放的,知道了首元素的地址,再通过指针加减整数的方式就可以顺藤摸瓜拿到后面的元素
int arr[]={1,2,3,4,5,6,7,8,9,10};
int*p=&arr[0];//指针变量p存储的是数组首元素的地址
int i=0;
int sz=sizeof(arr)/sizeof(arr[0]);
for(i=0;i<sz;i++)
{// printf("%d ",*(p+i));//指针加减整数得到的还是地址,要想拿到里面的数据,需要解引用//另一种写法:printf("%d ",*p);p=p+1;//p+1的结果是个地址,不能写成*p=p+1,*p他里面存储的是一个数据,你不能把地址赋值给他,这样写是错的//
}

四.const修饰指针

1.const修饰变量

const是c语言的关键字,当const用来修饰变量的时候,在定义的时候必须进行初始化,该变量就变成了一个常量,不能再对其进行赋值操作,修改

#include <stdio.h>int main() {const int num = 10;// 下面这行代码会导致编译错误,因为 num 是只读的// num = 20; printf("num 的值是: %d\n", num);return 0;
}
//变量a用const修饰后,不能修改了,但是我们可以用指针中的解引用来修改a的值
int main()
{
const int a=10;int*p=&a;*p=20;printf("%d",a);
return 0;}

但是这样做显然是不合理的,我们用const修饰指针a的目的就是为了让a的值不发生修改,如果拿到了a的地址,就可以修改,这就会有很大的风险,那么怎么才能即使拿到了a的地址,也不让a的值被修改呢?

2.const修饰指针变量

const修饰指针时,分两种情况讨论,const在*的左边和右边的代表意义是不同的,

const*的左边的时候
int a=10;
int b=20;
const int *p=&a;
int const *p=&a;//这两种写法都对
*p=20;//会报错,const在*左边时,不能修改指针变量指向的内容
p=&b;//但是可以修改指针变量本身,即指针变量的指向
const*右边的时候
int a=10;
int b=30;
int* const p=&a;
*p=20;//const在*右边的时候,可以修改指针指针变量指向的内容
p=&b;//会报错,但是不能修改指针变量本身,就是里面存的地址

当然,还有一种情况就是*左右两边都有const,这种情况就是即不能修改指针指针变量本身,也不能修改指针指向的内容

int main()
{int a = 110;int b = 220;const int* const p = &a;*p = 20;p = &b;return 0;
}

总结:

1.当const在*左边的时候,指针指向的内容不能被修改,但是指针变量本身可以被修改

2.当const在*左边的时候,指针指向的内容可以被修改,但是指针变量本身不能被修改

3.当*左右两边都有const的时候,指针*变量本身和指针指向的内容都不能被修改

五.指针运算

1.指针加减整数

在数学里,如果500+1就等于501,因为500是一个int类型的,而1也是1个int 类型的,所以结果也是一个int类型的 ,但是int*类型的指针变量+1 ,得到的是地址。指针加减整数其实在前面已经举过例子了,就是用指针的方式访问数组的元素,前提条件是数组在内存中是连续存放的,地址由低到高变化,不光数组这个例子,以后只要遇到连续的的空间,都可以用指针加减整数的方法

//前面我们举得是整型数组的例子,下面用字符数组举例
#include <stdio.h>
#include <string.h>
int main()
{char arr[]="abcdef";int i=0;int sz=strlen(arr);//统计字符串长度,并不会包括\0char*p=&arr[0];for(i=0;i<sz;i++){printf("%c",*p);//对指针变量解引用,就可以拿到指针指向的内容p++;//指针变量跳过一个字节,即1个字符,指向下一个字符的地址//另一种写法// printf("%c",*(p+i));}return 0;
}

2.指针- 指针

指针减指针其实就是地址减地址,得到的是一个整型数,


int main()
{int arr[] = { 1,2,3,4,5,6 };int ret = &arr[5] - &arr[0];//指针减指针得到的指针之间的元素个数printf("%d", ret);//打印结果为5return 0;
}
//错误代码示例,指针减指针的前提是指针指向的都是同一片空间

int main()
{int arr[10]={0};char str[11]={0};int ret=&arr[9]-&str[5];//这样写就不行,首先它们是不同类型的数组,指向的不是同一片空间,其次我们也不知道str数组空间和arr数组空间之间相差多少个元素,这是不确定的printf("%d",ret);return 0;
}

总结:

1.指针减指针得到的是一个整型数,是一个绝对值,他表示指针之间相差多少个元素,大地址减小地址得到的是一个正数,小地址减大地址,得到的是一个负数。

2.指针减指针的前提是,指向的空间必须是同一片空间,不然没意义,就像日期-日期=天数,日期加天数都是有意义的,但是日期+日期就没有意义了。

3.指针的关系运算

指针和指针之间是可以比较大小的,指针比较大小就是地址与地址之间比较大小

//除了利用指针加减整数来遍历数组元素之外,还可以利用指针的关系运算的方式
int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9 };//当元素的地址是否小于第10个元素的地址,意味着还有元素没有访问完,大于等于第10个元素的地址,就表示数组里的元素已经全部访问完了int* p = arr;int sz = sizeof(arr) / sizeof(arr[0]);//int*p=&arr[0};while (p<arr+sz)//指针和指针之间进行比较{printf("%d ", *p);p++;//指针+1,跳过一个整型元素}return 0;
}

有个规律,

1.数组里,如果想知道某个元素的地址,首元素的地址加上元素对应的下标就可以得到地址;

2.假设数组有n个元素,那么第n+1个元素的下标就等于数组元素的个数

六.野指针

所谓的野指针就是没有没有明确的指向,你可以把他理解为一条野狗,四处为家,是危险的。

1.什么样的情况下会出现野指针

1.指针变量未初始化
int a=10;
int*p;//指针变量没有初始化会报错,且他是一个野指针
*p=20;
2.指针越界访问
//数组只有10个元素,但是你用指针访问了11个元素,越界了
int arr[]={1,2,3,4,5,6,7,8,9,10};
int sz=sizeof(arr)/sizeof(arr[0]);
int i=0;
int *p=arr;
for(i=0;i<=sz;i++)//sz=10,i<=10,意味着当下标为10的时候还可以访问,但是下标为10就已经是第11个元素了
{printf("%d "*(p+i));
}
//正常情况下说,如果代码没有越界访问的话,就是写成下面的样子
int main()
{int arr[10] = { 0 };int i = 0;int* p = arr;for (i = 0; i < 10; i++){*p = i;//将每一个元素的值改成对应的下标p++;//指针跳过1个整形元素//这样写也是对的//*(p++) = i;//第一步:*p=i,第二步:p++}for (i=0;i<10;i++){printf("%d ",arr[i]);}return 0;
}
3.指针指向的空间释放

就是返回栈空间的地址

int* test()
{int n=100;//n是局部变量,它的作用域就是test函数内return &n;//一旦出了函数体,局部变量向内存申请的空间就没有意义了,已经被回收了,
}int main()
{int*p=test();printf("%d",*p);//那么此时p就变成了野指针,因为n申请的内存空间已经销毁了return 0;
}

2.如何避免野指针

1.指针初始化

如果不知道指针指向哪里,但又必须初始化,因为如果不初始化,指针就是野指针,我们可以赋值为NULL,

NULL是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址

会报错.

举个例子,如果指针没有初始化就是一条野狗,但是现在我们将指针初始化为NULL,就相当于用绳子把野狗绑到了树上,虽然它看似安全,但是我们也不能在它旁边撒尿,得绕着他走,所以指针赋值为NULL后,就不能使用指针了,即不能访问里面的数据了,直到你为他找到新的地址

int main()
{int a = 10;int* p;//没有初始化,就是野指针//如果暂时不知道指针指向哪里,可以赋值为空指针int* p = NULL;*p = 100;//但是赋值为空指针后,就不能使用指针p了//如果确定了指针指向的地址,再重新赋值、int* p = &a;return 0;
}
2.小心指针越界
int main()
{int arr[] = { 1,2,3,4,5,6,7,8 };int sz = sizeof(arr) / sizeof(arr[0]);int* p = arr;int i = 0;for (i=0;i<sz;i++){printf("%d ", *(p + i));}//循环结束后会来到这,此时i=8,指针已经指向第9个元素了,已经越界了,所以指针属于野指针p = NULL;//为了避免p变成野指针,赋值为空指针,你可以理解把一条野狗拴在树上//赋值为空指针后,指针p就不能使用了,除非指向新的地址return 0;
}
3.避免返回局部变量的地址

返回局部变量地址其实就是返回栈空间地址,因为局部变量是存放在栈里的,

int* test()
{int arr[]={1,2,3,4,5,6};//出了函数体,数组向内存申请的空间就释放了return arr;//返回首元素地址
}
int main()
{int*p=test();//定义指针变量p接受函数返回的地址*p=200;//p此时是野指针,因为他返回的是局部变量的地址
retrun 0;}
4.使用指针前检查下是否是空指针
int a=20;
int*p=&a;
if(p!=NULL)//p如果为空指针返回的就是0,0为假;如果不是空指针,返回的就是非0,C语言中非0就是真
{*P=200;
}

其实还可以用assert断言来判断是否是空指针,更方便,后面有详细介绍

5.不使用指针的时候设置为空指针
int a=10;
int*p=&a;
*p=200;
p=NULL;//这意味着p不再指向任何有效的地址,指针p不能在使用了

七.assert断言

assert.h头文件中,包含了宏assert(),这个宏常常被称为断言,用于在运行程序的时候判断程序是否符合某个条件,如果不符合就报错,程序终止运行。

assert()的参数是一个表达式,如果表达式为真,则返回非零,则程序正常运行;如果返回的值是0,assert就会报错

如果每次在使用指针前都要用if语句判断是不是空指针,就会很蛮烦,我们可以用assert断言来判断程序中有没有空指针。如果确定程序没有问题,不需要再做断言,就可以在~#include #include <assert.h>前面定义一个宏NDEBUG, 他就会禁用程序中所有的assert语句;如果程序又出现问题,那么就可以把#define NDEBUG 这条语句注释掉,这样就重新使用了assert语句。

#include <stdio.h>
#include <assert.h>
#define NDEBUG  //开启或关闭 assert() 的机制
int main()
{int a=10;int*p=&a;assert(p!=NULL);//指针p如果等于空指针,为0,表达式就为假,程序就会报错//assert(p);这样写也是对的*p=20;
return 0;}

八.传值调用和传址调用

1.传值调用

学习指针的目的是为了解决问题,那么什么问题非指针不可呢?下面下一个函数,这个函数的功能是负责交换两个变量的值, 想一下为什么下面的代码最终没有出现交换的效果

void swap(int x,int y)//x=a  y=b
{int c=0;c=x;x=y;y=c;
}int main()
{int a=20;int b=30;printf("交换前:a=%d b=%d\n",a,b);swap(a,b); //传值调用printf("交换后:a=%d b=%d\n",a,b);
return 0;}

通过调试我们可以发现,x和y的值都发生改变了,但是a和b的值并没有发生改变,这是因为我们函数的实参传递的是a和b的值,把值传给形参后,因为形参是实参的一份临时拷贝,形参有自己的独立空间,所以在函数swap内部交换,并不会影响a和b。所以这个代码就必须用指针来解决,要把a和b的地址传给形参,也就是传址调用。

2.传址调用
void swap(int*x,int*y)//指针x存放a的地址,指针y存放b的地址
{int c=0;c=*x;//其实是把x的的值,也就是a的值赋值给c*x=*y;//把y的值赋值给x*y=c;//把x的值赋值给y
}int main()
{int a=20;int b=30;printf("交换前:a=%d b=%d\n",a,b);swap(&a,&b); //传址调用printf("交换后:a=%d b=%d\n",a,b);
return 0;}

总结:

如果传的外部值需要发生修改,那么就要传地址,如果不发生修改,那么就传值

//下面的代码就不要传地址,直接传值就可以解决
//返回最大值
int max(int a,int b)
{if(a>b)return a;elsereturn b;
}
int main()
{int a=20;int b=3;int m=max(a,b);printf("%d",m);
return 0;
}

九.练习:求字符串长度

(一共有4种方法)

方法1:

#include <stdio.h>
#include <string.h>//使用strlen函数需要引用头文件
int main()
{char arr[]="hello";printf("%d",strlen(arr));//strlen函数统计字符串长度,不包括\0
return 0;
}

方法2:

char str[]="hello";//字符串以\0结尾
int count=0;
char*p=str;//数组名即首元素地址
while(*p !='\0')//只要不是字符'\0',就一直统计,直到遇到\0,因为字符串以\0结束
{count++;p++;
}
printf("%d",count);
//还可以用函数的方法
int my_strlen(char* str)
{int count=0;while((*str)!='\0'){count++;str++;}return count;
}
int main()
{char str[]="hello";int len=my_strlen(str);printf("%d",len);return 0;}

方法3:

//利用指针-指针的方式,因为指针-指针得到是相差的元素的个数,那么我们只要知道了\0的地址,用\0的地址减去第一个元素的地址,就可以得到字符串的长度
int my_strlen(char* str)
{char* start=str;//将第一个元素的地址存储起来,后面指针减指针的时候需要用到while((*str)!='\0'){str++;}//循环结束后,指针str已经指向\0的地址了return str-start;
}
int main()
{char str[]="hello";int len=my_strlen(str);printf("%d",len);return 0;}

方法4:

#include <stdio.h>
#include <assert.h>
size_t my_strlen(const char* str)//首先我们的意图是求取字符串的长度,所以不希望str指向的内容发生改变,用const来修饰指针str,//统计的字符串的长度肯定不会是一个负数,因为strlen函数的返回类型是size_t,就是无符号整型,他表示正数,所以我们在这里把my_strlen的函数返回类型设置为size_t
{size_t count=0;//在使用指针前,需要判断下str是不是空指针assert(str!=NULL);while((*str)!='\0')
{count++;str++;}return count;}
int main()
{char str[]="hello";size_t len=my_strlen(str);printf("%zd",len);return 0;}

创作不易!多多支持

相关文章:

深入理解指针1

深入理解指针1 一.内存和地址以及指针间的关系 举一个生活中的例子&#xff0c;假如你去找你的朋友玩&#xff0c;你的朋友告诉了你酒店的名字&#xff0c;但是没有告诉告诉你他具体住哪一件房间&#xff0c;于是你为了找到你的朋友&#xff0c;只好一间房间一间房间的去找&am…...

使用 pjsua2 开发呼叫机器人,批量拨打号码并播放固定音频

如何使用 pjsua2 开发呼叫机器人,批量拨打号码并播放固定音频 声明 该播客仅提供实现思路,并非实际的方案记录,不要盲目照搬。 pjsua2库的安装会有较多问题,请参考本人之前的播客进行安装 pjsua2。 pjsua2 库具体的 api 说明请参考开源库内的 范例代码。 引言 在今天的…...

【部署优化篇四】《DeepSeek移动端优化:CoreML/TFLite实战对比》

手机里的AI助手能秒速回答你的问题,游戏人物能实时追踪你的表情变化,这些酷炫功能的背后都离不开移动端机器学习框架的支撑。今天我们就来撕开两个当红炸子鸡框架CoreML和TFLite的神秘面纱,看看它们在模型优化这件事上到底藏着哪些独门绝技。 一、移动端优化的生存法则 在…...

【分布式理论14】分布式数据库存储:分表分库、主从复制与数据扩容策略

文章目录 一、分表分库1. 数据分表的必要性与方式2. 数据分库原则与优势 二、主从复制1. 读写分离架构设计2. 数据复制方式3. MySQL实现主从复制4. MySQL主从复制实践与高可用方案 三、数据扩容 随着业务的不断发展和数据量的增长&#xff0c;传统的单机关系型数据库已经逐渐不…...

【嵌入式Linux应用开发基础】特殊进程

目录 一、守护进程&#xff08;Daemon Process&#xff09; 1.1. 概念 1.2. 特点 1.3. 守护进程的命名 1.4. 创建守护进程的步骤 1.5. 守护进程的实例 1.6. 守护进程的管理 1.7. 影响与处理 二、僵尸进程&#xff08;Zombie Process&#xff09; 2.1. 僵尸进程的定义…...

HDFS应用-后端存储cephfs-java-API

HDFS(Hadoop Distribute FileSystem)是一个适合运行在通用硬件之上,具备高度容错特性,支持高吞吐量数据访问的分布式文件系统,非常适合大规模数据集应用。 HDFS适用于如下场景: • 处理海量数据(TB或PB级别以上) • 需要很高的吞吐量 • 需要高可靠性 • 需要很好的可扩…...

复现log4j2的jndi注入成功反弹shell

在Apache Solr 中复现log4j2的jndi注入漏洞&#xff0c;获得docker容器的root权限&#xff0c;实验五步走&#xff1a; 启动容器访问web页面漏洞探测反弹shell载荷wireshark抓取log4j反弹shell流量 ailx10 1956 次咨询 4.9 网络安全优秀回答者 互联网行业 安全攻防员 去咨…...

Cursor实战:Web版背单词应用开发演示

Cursor实战&#xff1a;Web版背单词应用开发演示 需求分析自行编写需求文档借助Cursor生成需求文档 前端UI设计后端开发项目结构环境参数数据库设计安装Python依赖运行应用 前端代码修改测试前端界面 测试数据生成功能测试Bug修复 总结 在上一篇《Cursor AI编程助手不完全指南》…...

win10系统上的虚拟机安装麒麟V10系统提示找不到操作系统

目录预览 一、问题描述二、原因分析三、解决方案四、参考链接 一、问题描述 win10系统上的虚拟机安装麒麟V10系统提示找不到操作系统&#xff0c;报错&#xff1a;Operating System not found 二、原因分析 国产系统&#xff0c;需要注意的点&#xff1a; 需要看你的系统类…...

python subprocess库

subprocess subprocess 是 Python 标准库中的一个模块&#xff0c;用于创建和管理子进程。它允许你在 Python 程序中执行外部命令、调用其他程序&#xff0c;并与这些程序进行交互。subprocess 模块提供了丰富的功能&#xff0c;可以替代一些旧的模块和函数&#xff0c;如 os.…...

敏捷项目管理:适应快速变化的项目环境

一、敏捷项目管理的核心逻辑 敏捷项目管理是一种以迭代开发、快速响应变化、持续交付价值为核心的方法论&#xff0c;尤其适合需求频繁变更或市场环境不确定的项目。它的核心逻辑是**“小步快跑”**——将大目标拆解为多个短期可交付的成果&#xff0c;通过持续反馈和调整&…...

矿用机车移动逆变电源设计(论文+源码)

1总体方案设计 本课题为矿用机车移动逆变电源的硬件电路设计&#xff0c;其整个架构如图2.1所示包括了:380V三相交流电&#xff0c;逆变电路&#xff0c;高频变压器&#xff0c;24V直流输出&#xff0c;控制电路&#xff0c;驱动电路&#xff0c;保护电路等等。 在工作原理上&…...

前七章综合练习

一&#xff0c;拓扑图 二&#xff0c;实验要求 不限 三&#xff0c;实验步骤 第一步&#xff0c;搭建拓扑图 如上 注意&#xff1a; 第二步&#xff0c;配置IP trust&#xff1a; client1 client2 fw untrusrt-1&#xff1a; fw r3 电信DNS 百度web-1 untrust-2&#xf…...

二级指针略解【C语言】

以int** a为例 1.二级指针的声明 a 是一个指向 int*&#xff08;指向整型的指针&#xff09;的指针&#xff0c;即二级指针。 通俗的讲&#xff0c;a是一个指向指针的指针&#xff0c;对a解引用会是一个指针。 它可以用于操作动态分配的二维数组、指针数组或需要间接修改指针…...

环境变量2

目录 环境变量PATH 如何改变PATH 我们今天继续来学习环境变量2&#xff01;&#xff01;&#xff01; 环境变量PATH PATH的作用是知道命令的搜索路径&#xff0c;我们都知道Linux上的命令行指令&#xff0c;ll&#xff0c;pwd什么的为什么我们写出来系统就知道是什么并且运…...

GoFound 与 MySQL 集成优化方案

GoFound 与 MySQL 集成优化方案 1. 明确需求 文章信息存储在 MySQL 数据库中。使用 GoFound 实现全文搜索功能。搜索时&#xff0c;先从 GoFound 中获取匹配的文章 ID&#xff0c;然后从 MySQL 中查询完整的文章信息。 2. 优化思路 数据同步&#xff1a;将 MySQL 中的文章数…...

数据录入与处理岗位

随着人工智能技术的迅猛发展&#xff0c;DeepSeek等先进AI系统正在逐步渗透到各个行业&#xff0c;工控行业也不例外。工控行业作为工业自动化的核心领域&#xff0c;涵盖了从生产线控制到设备维护的多个环节。然而&#xff0c;随着AI技术的不断进步&#xff0c;一些传统岗位正…...

cs106x-lecture11(Autumn 2017)-SPL实现

打卡cs106x(Autumn 2017)-lecture11 (以下皆使用SPL实现&#xff0c;非STL库&#xff0c;后续课程结束会使用STL实现) 1、diceRolls Write a recursive function named diceRolls accepts an integer representing a number of 6-sided dice to roll, and output all possibl…...

基于ffmpeg+openGL ES实现的视频编辑工具(一)

在深入钻研音视频编辑开发这片技术海洋时&#xff0c;相信不少开发者都和我有同样的感受&#xff1a;网络上关于音视频编辑工具实现的资料繁多&#xff0c;理论阐释细致入微&#xff0c;代码片段也随处可见。然而&#xff0c;一个显著的缺憾是&#xff0c;缺乏一个完整成型的 A…...

全面掌握Python时间处理

全面掌握Python时间处理 flyfish datetime: 最适合处理日期和时间&#xff0c;支持创建时间对象、格式化时间、计算时间差等。time: 提供了获取时间戳、将时间戳转换为本地时间、暂停程序执行等功能。timeit: 主要用于性能测试&#xff0c;帮助开发者了解某段代码的执行效率。…...

LLM 推理中推理-时间计算技巧

25年2月来自香港科技大学广州分校的论文“Bag of Tricks for Inference-time Computation of LLM Reasoning”。 随着大语言模型 (LLM) 的进步&#xff0c;解决复杂的推理任务越来越受到关注。推理-时间计算方法&#xff08;例如 Best-of-N、波束搜索等&#xff09;特别有价值…...

matplotlib 如何是的横坐标纵向显示

在 ​​matplotlib​​​ 中&#xff0c;若要让横坐标标签纵向显示&#xff0c;可以使用 ​​plt.xticks()​​​ 或 ​​ax.set_xticklabels()​​ 方法结合旋转参数来实现。 方法一&#xff1a;使用 ​​plt.xticks()​​ 当你使用 ​​matplotlib​​​ 的 pyplot 接口&am…...

Go日期时间处理工具Carbon

**注意&#xff1a;**本文大部分内容摘抄自-https://github.com/dromara/carbon/blob/master/README.cn.md使用文档 一、简介 一个轻量级的、易于使用的、语义智能的日期时间处理库&#xff0c;支持链式调用&#xff0c;已被 awesome-go 收录&#xff0c;现已经捐赠给了 drom…...

【Spring生命周期】Bean元信息配置阶段

引言 本系列将详细讲解Spring生命周期的13个阶段&#xff0c;从源码角度帮助我们更好的理解Spring框架和bean生命周期全流程 Bean信息定义4种方式 API的方式Xml文件方式properties文件的方式注解的方式 在 Spring 框架中&#xff0c;Bean 元信息配置阶段是整个 Bean 生命周…...

linux有名管道的文件描述符3和4

在使用有名管道&#xff08;Named Pipe&#xff0c;FIFO&#xff09;时&#xff0c;返回的文件描述符 3 和 4 是通过 open() 系统调用打开有名管道后分配的文件描述符。文件描述符是进程用来访问打开的文件或管道的整数标识符。 1. 文件描述符的分配规则 文件描述符是一个非负整…...

Mobaxterm: Local port forwarding Remote port forwarding

文章目录 Remote port forwardingLocal port forwardingAppendix: Deploy clash in docker Remote port forwarding If you want to share the proxy on your local machine with the remote server, use Remote port forwarding. Consider this scenario: There is no proxy…...

DeepSeek模型快速部署教程-搭建自己的DeepSeek

前言&#xff1a;在人工智能技术飞速发展的今天&#xff0c;深度学习模型已成为推动各行各业智能化转型的核心驱动力。DeepSeek 作为一款领先的 AI 模型&#xff0c;凭借其高效的性能和灵活的部署方式&#xff0c;受到了广泛关注。无论是自然语言处理、图像识别&#xff0c;还是…...

分布式之分布式ID

目录 需求 1. 全局唯一性 2. 高性能 3. 高可用性 4. 可扩展性 5. 有序性 6. 时间相关 7. 长度适中 8. 安全性 9. 分布式一致性 10. 易于集成 常见解决方案 选择依据 数据库号段模式 核心概念 工作流程 优点 缺点 实现示例 优化策略 适用场景 Snowflake雪…...

2025/2/19机试准备

1.%c不忽略空格&#xff08; &#xff09; 2.启示 #include <stdio.h> #include <string.h> int main(){char str[100]{0};int x,y;int n1,n2,n3;int i;while(scanf("%s",str)!EOF){istrlen(str);if(i%30){//12446-2n1i/3;n3i/3;n2i/32;}else if(i%31){…...

用友U8 固定资产-批量变动单

前提&#xff1a;没有结账&#xff0c;没有结账&#xff0c;没有结账 如果已经结账&#xff0c;可反结账 1.需要先计提折旧&#xff0c;操作路径是点击【固定资产】-【计提折旧】-计提本月折旧 2.进行资产减少操作&#xff0c;点击【资产处置】-【资产减少】&#xff0c;如…...