C语言----深入理解指针(1)

1.内存地址

内存单元的编号 == 地址 == 指针

cpu访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,而因为内存中字节很多,所以需要给内存进行编址

/int main()
//{
//    int a = 20;//创建变量的本质其实是在内存中申请空间
//    //向内存申请4个字节的空间,用来存放20这个数字
//    //这4个字节,每个字节都有编号(地址)
//    //变量的名字仅仅给程序员看,编译器不看名字,
//    // 编译器是通过地址找内存单元的
//    //
//    return 0;
//}

2.指针变量和地址

& --取地址--拿到地址

  • --解引用--通过地址找回去

    通过地址来找回a *pa

    解释:

    1.*表示pa是指针变量

    2.int 表示pa指向的变量a的类型是int

    char ch ='w'

    char *pc =&ch;

/*int main()
{int a = 20;&a;//&----取地址操作符,拿到变量a的地址printf("%p",&a);//打印出来的地址是00B3FD40int* pa = &a;//将a的地址存在变量pa里面//这个变量是用来存放地址(指针)的//所以pa叫指针变量//int*来是pa的类型return 0;
}*/
/*
解释:
1.*表示pa是指针变量
2.int 表示pa指向的变量a的类型是int
char ch ='w'
char *pc =&ch;*/int main()
{int a = 20; int* pa = &a;*pa=30;//* -解引用操作符(间接访问操作符)//a被改成30//通过*pa找到a//*pa其实就是aprintf("%d", a);return 0;
}
/*
& --取地址--拿到地址
* --解引用--通过地址找回去
通过地址来找回a    *pa
*/
//int main()
//{
//    int a = 10;
//    int* p = &a;
//    //1.指针变量是用来存放地址的,
//    // 地址的存放需要多大空间
//    //那么指针变量的大小就是多大
//
///*    
//指针变量的大小取决于地址的大小
32位平台下地址是32个bit位(即4个字节)
64位平台下地址是64个bit位(即8个字节)
//x86是32位的环境,x64是64位的环境*/
//    printf("%zd", sizeof(p));//输出结果是4
//    return 0;
//}
//int main()
//{
//    char ch = 'w';
//    char* pc = &ch;
//
//
//    printf("%zd", sizeof(pc));//输出结果是4
//    return 0;
//}
//指针变量的大小跟类型是无关的
int main()
{printf("%zd\n", sizeof(char*));printf("%zd\n", sizeof(short*));printf("%zd\n", sizeof(int*));printf("%zd\n", sizeof(float*));printf("%zd\n", sizeof(double*));//输出结果都是4个字节return 0;
}
//指针变量的大小跟类型是无关的
//只要是指针类型的变量,只要在同一个平台下,大小就都是相同的//指针类型有什么意义?为什么存在那么多的指针类型?

3.指针变量类型的意义

指针的类型决定了,对指针解引用的时候有多大权限(一次能操作几个字节)

比如:char的指针解引用就只能访问一个字节,而int的指针解引用就能访问四个字节

//指针类型有什么意义?为什么存在那么多的指针类型?
int main()
{int a = 20;int* pa = &a;//取地址a放到pa里面char* pc = &a;printf("&a=%p\n", &a);printf("pa=%p\n", pa);printf("pc=%p\n", pc);printf("&a+1=%p\n", &a + 1);printf("pa+1=%p\n", pa + 1);printf("pc+1=%p\n", pc + 1);return 0;
}
/*打印结果
&a = 004FF77C
pa = 004FF77C
pc = 004FF77C
& a + 1 = 004FF780
pa + 1 = 004FF780
pc + 1 = 004FF77D
*/
/*
char*类型的指针变量+1跳过1个字节,int类型的指针+1跳过4个字节
。这就是指针类型的差异带来的变化
int *pa
pa+1--->+1*sizeof(int)
pa+n--->+n*sizeof(int)char*pc
pc+1--->+1*sizeof(char)
pc+n--->+n*sizeof(char)
*/
//对应int访问4个字节,+1跳过四个字节
//void* 指针----无具体类型的指针
//这种类型的指针可以用来接收任意类型的地址
//但是这种指针存在局限性,void*类型的指针不能直接进行指针的+-整数和解引用运算
//void*指针不能进行指针运算,可以接收不同类型的地址//一般void*类型的指针是使用函数参数的部分,用来接收不同类型的地址
//这样的设计可以实现泛型函数的效果,使得一个函数来处理多种类型的数据

4.const修饰指针

int main()
{const int n = 10;//n=20;int *p=&n;//把n的地址取出来给p*p = 200;//通过n的地址来改变n的大小printf("%d", n);return 0;
}
//一般的const修饰指针变量,
// 可以放在*左边,也可以放在*右边
//int main()
//{
//    int const* p;//const在*左边
//    int* const p;//const在*右边
//    return 0;
//}//int main()
//{
//    int n = 10;
//    int m = 100;
//    int* p = &n;
//    *p = 20;//通过p找到n,因为p指向的是n
//    p = &m;//用m的地址将n的地址覆盖了
///*关于指针有3个相关值
//* 1.p,p里面存放着一个地址
//* 2.*p,p指向的对象
//* 3.&p,表示的是p变量的地址
//*/    
//    return 0;
//}//int main()
//{
//    int n = 10;
//    int m = 100;
//    int const* p = &n;//等同于const int * p = &n;
//    
//    //const放在左边的时候,限制的是*p,也就是p指向的对象
//    //const修饰指针变量
//    //放在*左边,限制的是指针指向的内容,
//    // 也就是不能通过指针变量修改它所指向的内容
//    //在这里面还是限制上了n,n的数字不能被修改
//
//    //但是指针变量本身是可以改变的,里面的地址是可以改变的
//    *p = 20;//err
//    p = &m;//ok
//    return 0;
//}//int main()
//{
//    int n = 10;
//    int m = 100;
//    int *const p = &n;
//    
//    //如果要去改变p是不行的,但是没有限制*p
//    //这里是可以改变p指向的对象的
//     
//    // 
//    // //将const放在*右边,此时限制的是指针变量p本身
//    // //指针不能改变它的指向,
//    // 但可以通过指针变量修改它所指的内容
//    //指针指向的是n,但是目前已经被const固定住,不能改变指向
//    *p = 20;//err
//    p = &m;//ok
//    return 0;
//}//不想让const修改p就把p放在*右边
//不想让你通过p修改p的指向的内容就把const放在左边,将*p固定住
#include <stdio.h>// 假设使用3个数据位和2个校验位
// 总位数为 3(数据位)+ 2(校验位)= 5位// 汉明码编码函数
void hamming_encode(int data[], int *encoded) {int p = 0; // 校验位计数器for (int i = 0; i < 3; i++) {// 计算每个校验位if (i == 0) {// 第一个校验位encoded[p++] = (data[1] + data[2]) % 2;} else if (i == 1) {// 第二个校验位encoded[p++] = (data[0] + data[2]) % 2;} else {// 第三个校验位encoded[p++] = (data[0] + data[1]) % 2;}// 将数据位放入编码后的数据中encoded[p++] = data[i];}
}// 汉明码解码和错误纠正函数
void hamming_decode(int received[], int *data) {int p = 0; // 校验位计数器int error检测 = 0;for (int i = 0; i < 3; i++) {// 计算每个校验位if (i == 0) {error检测 = (received[1] + received[2]) % 2;} else if (i == 1) {error检测 = (received[0] + received[2]) % 2;} else {error检测 = (received[0] + received[1]) % 2;}// 检查是否有错误if (error检测 != received[p]) {// 找到错误位并纠正for (int j = 0; j < 5; j++) {if (received[j] != (error检测 ^ received[j])) {received[j] ^= 1; // 翻转错误位break;}}}// 将数据位放入解码后的数据中data[i] = received[p++];}
}int main() {int data[] = {1, 0, 1}; // 原始数据int encoded[5]; // 编码后的数据int received[5]; // 模拟接收到的数据int decoded[3]; // 解码后的数据// 编码hamming_encode(data, encoded);printf("Encoded Data: ");for (int i = 0; i < 5; i++) {printf("%d ", encoded[i]);}printf("\n");// 模拟接收到的数据(包含一个错误)for (int i = 0; i < 5; i++) {received[i] = encoded[i];}received[2] ^= 1; // 故意引入一个错误// 解码和错误纠正hamming_decode(received, decoded);printf("Decoded Data: ");for (int i = 0; i < 3; i++) {printf("%d ", decoded[i]);}printf("\n");return 0;
}

不想让const修改p就把p放在*右边-- *const p--一直指向一个数 不想让你通过p修改p的指向的内容就把const放在左边,将*p固定住-conat *p

p被const固定住,p指向内容的大小不得改变

5.指针运算

指针的基本运算有三种,分别是:

指针+-指数

指针-指针

指针的关系运算

//循环打印数组的内容
//int main()
//{
//    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//    int sz = sizeof(arr) / sizeof(arr[0]);
//    for (int i = 0; i < sz; i++)
//    {
//        printf("%d", arr[i]);
//    }
//
//
//    return 0;
//
//}//采用指针来获取数组元素的地址
//int main()
//{
//    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//    int sz = sizeof(arr) / sizeof(arr[0]);
//    int *p = &arr[0];//将arr[0]的地址存在*p中
//    for (int i = 0; i < sz; i++)
//    {
//        printf("%d ", *p);//解引用来打印arr[0]
//        p++;//打印完p++往后走一步,整型指针加一就是向后挪了一个整型
//        //循环十次就能把这个数组的内容打印出来
//    }
//    return 0;
//}
//获取数组第一个数字的地址赋值给p,再利用*p解引用,打印*p所指的数
//p+1就是*(p+1),打印数组下一个数字//1.指针类型决定了指针+1的步长,决定了指针解引用的权限
//2.数组在内存中是连续存放的//int main()
//{
//    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//    int sz = sizeof(arr) / sizeof(arr[0]);
//    int* p = &arr[0];//将arr[0]的地址存在*p中
//    for (int i = 0; i < sz; i++)
//    {
//        printf("%d ", *(p + i));//直接解引用*(p+i),当i=0时,就是*p,打印的就是数组第一个数
//        
//    }
//    return 0;
//}
p+i  是跳过i*sizeof(int)个字节
//指针-整数,从10开始打印
//int main()
//{
//    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//    int sz = sizeof(arr) / sizeof(arr[0]);
//    int* p = &arr[sz-1];//数组中最后一位的下标是sz-1
//    for (int i = 0; i < sz; i++)
//    {
//        printf("%d ", *p );
//        p--;
//    }
//    return 0;
//}
//指针-指针的绝对值是指针和指针之间元素的个数
//指针-指针,计算的前提条件是两个指针指向的是同一块空间//int main()
//{
//    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//    printf("%d\n", &arr[9] - &arr[0]);//输出结果是9
//    printf("%d\n",  &arr[0]-&arr[9] );//输出结果是-9
//    return 0;
//}
//size_t my_strlen(char* p)//传过来的是数组名,用字符串指针来接收,*p指向的就是数组第一个元素
//{//size_t是无符号返回值
//    size_t count = 0;
//    while (*p != '\0')
//    {
//        count++;
//        p++;//往后走一位
//    }
//    return count;
//}
//int main()
//{
//    char arr[] = "abcdef";
//    size_t len = my_strlen(arr);//数组名其实是数组首元素的地址,arr==&arr[0]
//    //传过去数组名,就是传过去首元素的地址
//    printf("%zd\n", len);//打印结果是6
//
//    return 0;
//}//另一种写法
size_t my_strlen(char* p)//传过来的是数组名,用字符串指针来接收,*p指向的就是数组第一个元素
{//size_t是无符号返回值char* star = p;//指向的是数组第一个数字char* end = p;while (*end!= '\0')//如果end不等于\0,就让end++{//这个while的循环条件可以是while(*end),因为到了\0的时候,\0的ASCLL值就是0,不满足循环条件就停下来了end++;//直到enf走到\0不满足条件就不进行循环了,此时的*end指向的就是\0}return end-star;//两个指针相减得到的就是指针之间元素的个数
}//数组中最后一个元素的指针减去第一个指针的元素得到的 就是这个数组的数量
int main()
{char arr[] = "abcdef";size_t len = my_strlen(arr);//数组名其实是数组首元素的地址,arr==&arr[0]//传过去数组名,就是传过去首元素的地址printf("%zd\n", len);//打印结果是6return 0;
}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };//数组中随着下标的增长,地址由低到高变化的int sz = sizeof(arr) / sizeof(arr[0]);int* p = arr[0];//*p指向数组首个元素while (p < &arr[sz])//数组下标为10的数不在数组之内,所以在arr[sz]前面的就是数组所有的元素{//p的地址大小小于arr[sz]的地址,所以只要地址一直小于arr[sz]的地址就一直可以循环打印printf("%d ", *p);p++;}return 0;
}
//这里就运用到指针关系大大小p < &arr[sz]

6.野指针

概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

//1.未初始化会造成野指针
/*int main()
{//一个局部变量不初始化的话,它的值是随机的int* p;//p是局部变量,没有初始化,其值是随机值,如果将p中的值当做地址,//解引用操作就会形成非法访问*p = 20;//p就是野指针return 0;
}*///2.指针越界访问也会造成野指针的问题
/*int main()
{int arr[10] = { 0 };//数组初始化int i = 0;int* p = &arr[0];//将数组第一个元素的地址给pfor (i = 0; i <= 10; i++)//循环11次{*p = i;//当第11次循环的时候,访问到了不属于这个数组的空间,访问到数组之外的空间了p++;//此时的p就是野指针了}return 0;
}*///3.指针指向的空间释放也会造成野指针
int test()
{int n = 100;//n是局部变量,进入函数创建,出函数销毁,也就是说返回的&n的地址并不是原先存储100的地址return &n;//地址被还给操作系统了
}int main()
{int* p = test();//printf("%d\n", *p);//p一旦接受这个地址,p里面的就是野指针,造成内存非法访问,篡改内存return 0;
}
//规避野指针
//如果明确知道指针指向哪里就直接赋值地址,如果不知道指针应该指向哪里,
// 可以给指针赋值NULL
//NULL是c语言中定义的一个表示符常量,值是0,0也是地址,这个地址是无法正常使用的,读写地址会报错
/*int main()
{int a = 10;int* p = &a;//给出明确的地址,将a的地址赋值给pint* p2 = NULL;//把野狗拴在柱子上。p2没有指向的对象return 0;
}*///int main()
//{
//    int* p = NULL;
//    if (p != NULL)
//    { 
//        *P = 200;
//    }
//    return 0;
//
//}
//当指针变量指向一块区域时,我们可以通过指针访问该区域,
// 后期不再使用这个指针访问空间的时候,我们可以把该指针置为NULL
//只要是空指针我们就不去访问,类似把野狗用柱子拴起来,将野指针暂时管理起来

如何规避野指针:

1.对指针进行初始化

2.小心指针越界

3.指针变量不再使用,及时置NULL(空指针),指针使用之前检查有效性

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

7.assert断言

assert.h头文件定义了assert(),用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行,这个宏常常被称为“断言”

#define NDEBUGint main()
{/*int* p = NULL;assert(p != NULL);*///会报错,assert判断后面括号的条件,为假就报错 int a = 10;int* p = &a;assert(p != NULL);//这种情况就不会报错//assert可以判断指针的有效性//#define NDEBUG,利用这句话就可以控制assert,是否产生效果//如果想产生效果就注释掉,不想产生效果就在#include <assert.h>上方添加
//只要添加了#define NDEBUG这个语句,代码中的assert就会被禁用//assert()语句的缺点就是,因为引入了额外的检查,增加了程序的运行时间/*if (p != NULL){ *P = 200;}return 0;
}*/
//在vs版本中,Debug中assert()语句是可以使用的,但是在Release版本中直接优化掉了assert()语句//这样debug版本编写代码有利于程序员排查问题,在Release版本不影响用户使用时的效率
//在Release版本选择性的优化assert()

8.指针的使用和地址调用

//strlen是求字符长度的,统计的是字符串中\0之前的字符个数
//函数求字符串长度
//参数s指向的字符串不期望被修改
size_t my_strlen(const char*s)//把字符元素的地址传过来,用char*s接收
{//添加const不希望字符串被修改,直接将每次传来的实参固定死//不加const的话原先字符串的长度就被修改了size_t count = 0;assert(s != NULL);//防止传过来的实参为空指针,检测指针s是否有效while (*s)//当s遇到\0的时候,循环就停止{count++;s++;}return count;
}int main()
{char arr[] = "abcdef";size_t len = my_strlen(arr);printf("%zd", len);return 0;
}
//写一个函数,交换两个整型变量的值
//void Swap1(int x, int y)
//{
//    int z = 0;
//    z = x;//先把x的值放到z里面,x空了
//    x = y;//把y的值放到x里面,y空了
//    y = z;//把z的值放到y里面去,在这之前放在z里面的值是x
//}
//
//
//int main()
//{
//    int a = 0;
//    int b = 0;
//    scanf("%d %d", &a, &b);
//    
//    //交换a和b的值
//    printf("交换前:a=%d b=%d\n", a, b);
//    Swap1(a, b);
//    printf("交换后:a=%d b=%d\n", a, b);
//    return 0;
//}
//改代码打印结果是:
//交换前:a=3 b=5
//交换后:a = 3 b = 5
//很明显,出问题了 //当实参传递给形参的时候,形参是实参的一份临时拷贝,
//对形参的修改不会影响实参
//那么如何修改呢?void Swap2(int *pa, int*pb)
{int z = 0;z = *pa;//z=a*pa = *pb;//a=b*pb = z;//b=z
}//脑海中把图画出来int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);//交换a和b的值printf("交换前:a=%d b=%d\n", a, b);Swap2(&a, &b);//把a、b的地址传过去printf("交换后:a=%d b=%d\n", a, b);return 0;
}//在这两个代码中,Swap1是传值调用
//Swap2是传址调用,直接将变量本身传递过去了
//当我们采用的是传值调用,形参和实参占用的是不同的空间,对形参的修改不会改变实参//完成两个整数的相加
Add(int x,int y)
{int z = x + y;return z;
}int main()
{int a = 10;int b = 20;int c=Add(a, b);printf("%d\n", c);//传值调用return 0;
}
//当使用传值调用时,实际上是将参数值复制到函数内部的一个局部变量中。
// 这意味着函数内部对参数值所做的任何修改都不会影响原始变量。
//原始数据不会被修改,传值调用通常被认为是安全的
//传址调用涉及将参数的内存地址传递给函数。这意味着函数可以直接访问和修改原始变量。

传值调用:实际上是将参数值复制到函数内部的一个局部变量中,这意味着函数内部对参数值所做的任何修改都不会影响原始变量,原始数据不会被修改

传址调用:涉及将参数的内存地址传递给函数,这意味着函数可以直接访问和修改原始变量。

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

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

相关文章

css左右滚动互不影响

想实现左右都可以滚动&#xff0c;且互不影响。 只需要再左边的css里面 .threedlist {cursor: pointer;width: 280px;position: fixed;height: 100vh; /* 定义父容器高度 */overflow-y: auto; /* 只有在内容超过父容器高度时才出现滚动条 */} 如果想取消滚动条样式 .threedli…

PHP深度探索:从基础到实战,打造高效Web应用

PHP深度探索&#xff1a;从基础到实战&#xff0c;打造高效Web应用 PHP基础概念与环境搭建PHP简介环境搭建 PHP基础语法输出与变量控制结构条件判断循环 函数与数组 实战案例&#xff1a;简易博客系统数据库连接文章发布功能表单提交提交处理 安全性与性能优化安全实践性能优化…

【设计模式深度剖析】【4】【创建型】【建造者模式】| 类比选购汽车的过程,加深理解

&#x1f448;️上一篇:抽象工厂模式 | 下一篇:原型模式&#x1f449;️ 目录 建造者模式概览定义英文原话直译如何理解呢&#xff1f;建造者模式将对象的建造过程给抽象出来了类比选购汽车 4个角色UML类图1. 抽象建造者&#xff08;Builder&#xff09;角色2. 具体建造者…

【Docker实操】启动redis服务

一、步骤 1、获取redis镜像 执行获取redis镜像命令&#xff1a;docker pull redis。打印镜像清单&#xff0c;可以看到新拉到的redis镜像。 2、创建redis.conf配置文件 linux主机 mkdir -p /root/www/redis/conf touch /root/www/redis/conf/redis.conf cat << EOF &…

三维焊接平台在新一代机器人生产中得到广泛应用-河北北重

随着智能制造行业的不断推进&#xff0c;三维焊接平台在工业机器人领域应用现象普遍。三维焊接平台、三维柔性焊接平台工装夹具也会在新一代机器人——智能机器人在工业生产中得到广泛应用。目前&#xff0c;三维焊接平台、焊接铸铁平台在工业机器人的主要作用是应用于弧焊、电…

内网穿透--Ngrok-入门-上线

免责声明:本文仅做技术交流与学习... 目录 Ngrok: 技术实现: 前提: 命令: 详细流程及图解: 平台Ngrok: Sunny-Ngrok内网转发内网穿透 - 国内内网映射服务器 支持的协议&#xff1a;tcp、http、https 支持的类型&#xff1a;正向代理、反向代理 --隧道开通免费的 --协议…

Flink集群搭建简介

一、下载与解压 前往Flink官方网站&#xff08;https://flink.apache.org/zh/downloads.html&#xff09;下载适合你环境的Flink版本。注意&#xff0c;你需要选择与你的Scala版本相匹配的Flink版本。将下载的安装包&#xff08;通常是.tgz格式&#xff09;传输到你的主节点&a…

第三代“图即代码”低代码平台设计

低代码平台现在市面上的产品大致分为两类&#xff0c;一种是“ 搭积木”类型的 这种类型的主要是一些行业定制的saas产品&#xff0c;是在已经成型或者是平台提供的垄断性资源上来做二次“搭建”这种平台优势在于能快速上手&#xff0c;有难度的都进行了“简化”和“特定场景”…

经纬恒润第三代重载自动驾驶平板车

随着无人驾驶在封闭场地和干线道路场景的加速落地&#xff0c;港口作为无人化运营的先行者&#xff0c;其场景的复杂度、特殊性对无人化运营的技术提出了各种挑战。经纬恒润作为无人驾驶解决方案提供商&#xff0c;见证了港口在无人化运营方面的尝试及发展&#xff0c;并深度参…

html中嵌入js的2种方法 引用 引入javascript

方法1&#xff1a;引入js脚本块 页面中引入<script>标签&#xff0c;标签范围里写js代码。 <script type"text/javascript">//脚本代码function f1(){...} </script>方法2&#xff1a;引入js文件 <script type"text/javascript" s…

Sentinel限流

目录 Sentinel规则持久化Sentinel与Spring Cloud Gateway整合自定义扩展部分遇到的问题解决方案 控制面板改造新增读取规则代码重写SpringCloudGatewayApiDefinitionChangeObserver类&#xff0c;注意&#xff1a;类路径要完全一致新增自定义规则读取类lua脚本&#xff08;这里…

VSCODE gcc运行多个.c文件

一、简介 很多时候&#xff0c;开发者需要使用VSCODE进行C语言算法验证。而VSCODE的gcc编译&#xff0c;默认是只编译本文件的内容&#xff0c;其他.c文件是不参与编译的。这就给开发者带来很大的困扰&#xff0c;因为开发者不可能把所有的算法都写在一个.c文件&#xff0c;特别…

如何异地组网添加摄像机?

本文将介绍如何使用天联技术实现异地组网添加摄像机&#xff0c;并保障数据的安全性。 安防摄像机的应用愈发广泛&#xff0c;无论是家庭安防还是企业监控&#xff0c;摄像机都扮演着重要角色。在一些特殊场合或者特殊需求下&#xff0c;我们需要将摄像机添加到异地网络中进行监…

什么是JML

JML&#xff08;Java Modeling Language&#xff09;是用于对Java程序进行规格化设计的一种表示语言。它是一种行为接口规格语言&#xff08;BISL&#xff09;&#xff0c;基于Larch方法构建&#xff0c;特别为Java定制。JML的基本用途是描述Java模块方法的形式规范&#xff0c…

Flink 对接 Hudi 查询数据,java代码编写

1.pom.xml文件需要引入下面包 <properties><flink.version>1.15.4</flink.version><hudi.version>0.13.1</hudi.version></properties><dependencies><dependency><groupId>org.apache.flink</groupId><artifa…

【推荐算法-特征工程】每种item单侧特征,都可产生对应user单侧特征

比如item的平均成单价格&#xff0c;可以分成10个档位&#xff0c;作为一个标签值打在item上&#xff0c; 那么对应user对item的click用户行为&#xff0c;就能产生user-click的10个档位作为特征值 作为user的标签。 比如item的平均点击率&#xff0c;也可以分成比如20个档位…

Java-Spring

为什么要使用spring 综述省去很多非业务层代码研发的工作&#xff0c;提升javaweb服务研发效率&#xff0c;降低研发难度详解1、spring非常轻量且非侵入式&#xff0c;耦合低&#xff0c;污染小2、通过非常方便的依赖注入以及面向接口编程&#xff0c;可以实现业务代码之间的松…

12、电科院FTU检测标准学习笔记-双路电源自动切换

作者简介&#xff1a; 本人从事电力系统多年&#xff0c;岗位包含研发&#xff0c;测试&#xff0c;工程等&#xff0c;具有丰富的经验 在配电自动化验收测试以及电科院测试中&#xff0c;本人全程参与&#xff0c;积累了不少现场的经验 ———————————————————…

P2. 配置MySQL和用户注册登录模块

P2. 配置MySQL和用户注册登录模块 0 概述Tips1 预备知识1.1 SpringBoot 常用模块1.2 pojo层的实现1.3 mapper层的实现1.4 controller层调试CRUD 2 Spring Security2.1 Spring Security 介绍2.2 Spring Security 对接数据库2.3 密码的加密 3 Jwt验证3.1 传统Session验证方式3.2 …

ViewFlipper常见小问题

与click事件冲突 问题描述 维护老代码&#xff0c;有一个需求&#xff0c;一个组件使用ViewFlipper开发&#xff0c;既能左右滑动&#xff0c;也可以点击内部的子view&#xff0c;这里需要区分click事件月fling事件&#xff0c;在click的时候做某些业务。 问题分析 父view点…