13 学习总结:指针 · 其一

news2024/11/25 22:41:49

目录

一、内存和地址

(一)内存

(二)内存单元

(三)地址

(四)拓展: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 = &num;
    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置为NULL
    p = 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(保险判断)来加强代码使用时的健壮性(鲁棒性)
 

(二)传值调用和传址调用

        传址调用,可以让函数和主调函数之间建立真正的联系,在函数内部可以修改主调函数中的变量;所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采用传值调用,如果函数内部要修改主调函数中的变量的值,就需要传址调用


        以上内容仅供分析,若有错误,请多多指正

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1903948.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MySQL】逻辑架构与存储引擎

一、逻辑架构 1、MySQL逻辑架构 我们可以根据上图来对sql的执行过程进行分析 第一步&#xff1a;客户端与服务器建立一个连接&#xff0c;从连接池中分配一个线程处理SQL语句第二步&#xff1a;SQL接口接受SQL指令第三步&#xff1a;如果是5.7版本&#xff0c;就会先去缓存中…

SpringMVC(2)——controller方法参数与html表单对应

controller方法参数与html表单对应 0. User实体类 import org.springframework.format.annotation.DateTimeFormat;import java.io.Serializable; import java.util.Date; import java.util.List; import java.util.Map;public class User implements Serializable {private …

期末考试结束,老师该如何私发成绩?

随着期末考试的落幕&#xff0c;校园里又恢复了往日的宁静。然而&#xff0c;对于老师们来说&#xff0c;这并不意味着工作的结束&#xff0c;相反&#xff0c;一系列繁琐的任务才刚刚开始。 成绩单的发放&#xff0c;就是其中一项让人头疼的工作。家长们焦急地等待着孩子的考试…

【全面讲解如何安装Jupyter Notebook!】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

springboot三层架构详细讲解

目录 springBoot三层架构0.简介1.各层架构1.1 Controller层1.2 Service层1.3 ServiceImpl1.4 Mapper1.5 Entity1.6 Mapper.xml 2.各层之间的联系2.1 Controller 与 Service2.2 Service 与 ServiceImpl2.3 Service 与 Mapper2.4 Mapper 与 Mapper.xml2.5 Service 与 Entity2.6 C…

【Spring Boot】关系映射开发(三):多对多映射

关系映射开发&#xff08;三&#xff09;&#xff1a;多对多映射 1.创建实体1.1 创建 Student 实体1.2 创建 Teacher 实体 2.创建测试 在 多对多 关联关系中&#xff0c;只能通过 中间表 的方式进行映射&#xff0c;不能通过增加外键来实现。 注解 ManyToMany 用于关系的发出端…

【React Native优质开源项目】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

Nacos架构设计

Nacos1.X架构设计 Nacos2.X架构修改

Gitlab代码管理工具安装配置

前言&#xff1a; 没有真正的证书与域名建议使用httpip的方式在内网使用&#xff0c;不建议使用假的域名地址 一、安装前配置 #更改主机域名 hostnamectl set-hostname gitlab.dome.com bash #配置hosts 底部添加下面内容 vim /etc/hosts ############################ ip gi…

昇思25天学习打卡营第19天|Diffusion扩散模型

学AI还能赢奖品&#xff1f;每天30分钟&#xff0c;25天打通AI任督二脉 (qq.com) Diffusion扩散模型 本文基于Hugging Face&#xff1a;The Annotated Diffusion Model一文翻译迁移而来&#xff0c;同时参考了由浅入深了解Diffusion Model一文。 本教程在Jupyter Notebook上成…

【嵌入式DIY实例-ESP8266篇】-LCD ST7735显示BMP280传感器数据

LCD ST7735显示BMP280传感器数据 文章目录 LCD ST7735显示BMP280传感器数据1、硬件准备与接线2、代码实现本文介绍如何将 ESP8266 NodeMCU 板 (ESP-12E) 与 Bosch Sensortec 的 BMP280 气压和温度传感器连接。 NodeMCU 微控制器 (ESP8266EX) 从 BMP280 传感器读取温度和压力值,…

人工智能在病理组学虚拟染色中的应用|文献精析·24-07-07

小罗碎碎念 本期文献精析&#xff0c;分享的是一篇关于深度学习在虚拟染色技术中应用于组织学研究的综述。 角色姓名单位&#xff08;中文&#xff09;第一作者Leena Latonen东芬兰大学&#xff08;QS-552&#xff09;生物医学研究所通讯作者Pekka Ruusuvuori图尔库大学&#…

Raw Socket(一)实现TCP三次握手

实验环境&#xff1a; Windows物理机&#xff1a;192.168.1.4 WSL Ubuntu 20.04.6 LTS&#xff1a;172.19.32.196 Windows下的一个http服务器&#xff1a;HFS&#xff0c;大概长这个样子&#xff1a; 客户端就是Ubuntu&#xff0c;服务端就是这个…

2024年【危险化学品生产单位安全生产管理人员】考试总结及危险化学品生产单位安全生产管理人员考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 危险化学品生产单位安全生产管理人员考试总结是安全生产模拟考试一点通总题库中生成的一套危险化学品生产单位安全生产管理人员考试试题&#xff0c;安全生产模拟考试一点通上危险化学品生产单位安全生产管理人员作业…

6、Redis系统-数据结构-05-整数

五、整数集合&#xff08;Intset&#xff09; 整数集合是 Redis 中 Set 对象的底层实现之一。当一个 Set 对象只包含整数值元素&#xff0c;并且元素数量不大时&#xff0c;就会使用整数集合这个数据结构作为底层实现。整数集合通过紧凑的内存布局和升级机制&#xff0c;实现了…

保存在FinalShell服务器登录密码忘记了,如何快速获取到

一、从FinalShell获取服务器基本信息 如图操作会导出一个json文件&#xff0c;可以直接保存在桌面&#xff0c;或者其他位置 json格式如下&#xff1a; {"forwarding_auto_reconnect":false ,"custom_size":false ,"delete_time":0 ,"sec…

python读取指定文件夹下的图片(glob获取)

python读取指定文件夹下的图片&#xff08;glob获取&#xff09; 定义traverse_images函数&#xff0c;仅需要改变下根路径即可 glob是python中用来查找符合特定规则的文件路径名的函数 import os from glob import globdef traverse_images (folder_path):image_formats …

ComfyUI如何高效率使用多Lora

Efficient 工作流 {"last_node_id": 29,"last_link_id": 56,"nodes": [{"id": 26,"type": "LoRA Stacker","pos": [540,270],"size": {"0": 320,"1": 322},"flag…

增强安全防护,解读智慧校园系统的登录日志功能

在构建智慧校园系统时&#xff0c;登录日志功能扮演着不可或缺的角色&#xff0c;它不仅是系统安全的守护者&#xff0c;也是提升管理效率和确保合规性的有力工具。这一机制详细记录每次登录尝试的方方面面&#xff0c;涵盖了时间戳、用户身份、登录来源的IP地址乃至使用的设备…

python脚本“文档”撰写——“诱骗”ai撰写“火火的动态”python“自动”脚本文档

“火火的动态”python“自动”脚本文档&#xff0c;又从ai学习搭子那儿“套”来&#xff0c;可谓良心质量&#x1f44d;&#x1f44d;。 (笔记模板由python脚本于2024年07月07日 15:15:33创建&#xff0c;本篇笔记适合喜欢钻研python和页面源码的coder翻阅) 【学习的细节是欢悦…