【C语言】深度理解指针(上)
前言🌊
谈到指针,想必大家都不陌生。它不仅是C语言的重难点,还是不少C初学者的噩梦。本期我们将深度探讨一些较为复杂的指针以及指针的妙用,带领大家感受指针的魅力😝。
首先,我们先来复习复习指针的概念:
1. 指针就是地址,而指针变量是个变量,用来存放地址(指针),地址标识着一块唯一的内存空间。例如有一张纸,上面写着湖滨东路12号,那湖滨东路12号是什么呢?它就是一个地址,一个指针;而这个地址的载体纸便是指针变量,修改纸上的内容就相当于修改了指针变量存放的内容,即修改了指针。
2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
3. 指针是有类型,指针的类型决定了指针的+-整数的步长和指针解引用操作的时候的权限。
4.指针和指针不能进行加法运算,这是没有意义的;但可以进行减法运算,运算的结果表示间隔的元素个数。
下面,让我们怀着激动的心情,正式进入进入今天的主题✨
字符指针🌠
我们知道,指针有一种类型叫字符指针,我们一般有以下方法使用它:
//指向一个字符变量
int main()
{char ch = 'c';char* pch = &ch;*pch = 'w';return 0;
}//指向一个字符串
int main()
{const char* str= "abcdef";printf("%s\n", str);return 0;
}
对于第一种,毫无疑问,pch存放的是字符变量ch的地址。但是对于第二种,我们是将整个字符串放到str里面吗?实则不然,我们是将"abcdef"的首元素地址存放到str中。如下:
上述代码实际上是将"abcdef"中'a'的地址放到指针变量str中。有了这层理解,我们来看看下面一道面试题:
int main()
{char str1[] = "hello bit.";char str2[] = "hello bit.";const char* str3 = "hello bit.";const char* str4 = "hello bit.";if (str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;}
代码运行的结果如下:
我们来分析以下:
对于str1和str2想必大家没有问题,这两个数组都用"abcdef"来进行初始化,它们的存放的内容一样。但是毕竟是两个不同的数组,它们在内存中的地址是不同的,因此它们首元素的地址也是不同的,并且数组名代表首元素地址,因此str1不等于str2。就好比定义变量a=10,变量b=10,虽然a == b,但是&a != &b。
而有疑问的可能是str3和str4,这里需要注意的是,它们存放的都是字符串"abcdef"首元素的地址,而在C/C++中,一个常量字符串通常会单独存放在地址空间中的字符常量区,因此str3和str4指向的其实是同一块内存空间。如下:
指针数组与数组指针✈
3.1 引入
这两兄弟长得很像,很容易让初学者记混。我们需要记住指针数组是一个数组,它的每一个元素是指针;而数组指针是一个指针,它指向一个数组。
个人认为:无论名称多么复杂,后缀一般就是其类型。
试问下面几条语句表达什么:
int* arr[10];
int (*arr)[10];
int (*arr[10])[10];
对于第一条,arr毋庸置疑是变量名。首先arr会先和[]结合,因此arr是一个数组,一个每个元素是整形指针的指针数组。
对于第二条,arr与*号结合,因此arr是一个指针,指向一个整形数组,arr是一个数组指针。
对于第三条,同样由于[]的优先级比*号高,因此arr先与[]结合,arr是一个数组,每个元素的类型是int (*)[10],即是一个整形数组指针。综上,arr是一个存放整形数组指针的数组。
如果你想,你甚至可以无限套娃下去,这里就留给读者自己尝试啦💪
3.2 使用方法
对于数组指针,既然它是指向数组的,那数组指针中存放的应该是数组的地址,我们来看如下代码:
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int(*p1)[10] = &arr;//正确?错误? int(*p2)[10] = arr; //正确?错误?return 0;
}
我们知道数组名arr代表首元素地址,但也有两个例外,分别是&数组名和sizeof(数组名),这两种情况下arr代表的就不是首元素地址了,而是整个数组。因此,p1才是正确的使用方法。
我们可以通过数组指针将一个数组的地址保存起来,不过我们很少这样写代码,数组指针一般用在数组传参上,如下:
void print_arr1(int arr[3][5], int row, int col) //形参用数组的形式接收
{for (int i = 0 ; i < row; i++){for (int j = 0; j < col; j++){printf("%d ", arr[i][j]);}printf("\n");}
}
void print_arr2(int(*arr)[5], int row, int col) //形参用指针的方式接收
{for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){printf("%d ", arr[i][j]);}printf("\n");}
}
int main()
{int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };print_arr1(arr,3,5);print_arr2(arr, 3, 5);return 0;
}
首先,将一个二维数组当作参数传递,用一个二维数组来接收想必都没有异议。有疑问的可能是print_arr2,为什么我们可以用一个一维数组指针来接收呢?
事实上,数组传参是会发生降维的,降维成指向其内部元素类型的指针。如果不降维,就要发生数组拷贝,数组空间过大,函数调用效率降低,因此需要降维。如二维数组传参,就会降维成指向一维数组的指针。
而我们可以把二维数组看作一个一维数组,数组中的每一个元素都为一个一维整形数组。那么二维数组arr传参,发生降维后传的就是首个整形数组的地址,因此可以用一维整形数组指针来接收。
而对于指针数组,其存放的就为一个个指针变量,如下:
int main()
{char* s1 = "hello";char* s2 = "world";char* arr[2] = { s1,s2 };for (int i = 0; i < 2; i++){printf("%s ", arr[i]);}return 0;
}
4. 数组/指针传参🌟
我们在写代码的时候难免要将数组或者指针作为参数传递给函数,下面我们就来介绍以下如何正确地设计函数参数。
4.1一维数组传参
void test01(int arr[10]) {}; //1 ok?void test01(int arr[]) {}; //2 ok?void test01(int* arr) {}; //3 ok?void test02(int* arr[10]) {}; //1 ok?void test02(int** arr) {}; //2 ok?int main()
{int arr1[10] = {0};int* arr2[10] = { 0 };tesst01(arr1);test02(arr2);return 0;
}
对于tesst01(),arr1是一个一维整形数组,传参时降维为整形指针,传入首元素地址,因此可以用一个整形指针或整形数组接收。由于是整形指针,形参中一维数组的元素个数写不写就无所谓了,三种写法均正确。
对于tesst02(),arr2为整形指针数组,每一个元素是整形指针,传入的即为指向整形指针的指针,即二级指针,因此两种写法均正确。
4.2二维数组传参
void test(int arr[3][5]) {}; //1 ok?void test(int arr[][]) {}; //2 ok?void test(int arr[][5]) {}; //3 ok?void test(int* arr) {}; //4 ok?void test(int* arr[5]) {}; //5 ok?void test(int(*arr)[5]) {}; //6 ok?void test(int** arr) {}; //7 ok?int main()
{int arr[3][5] = { 0 };test(arr);
}
首先,我们函数可以用二维数组作为参数,由于传参时降维为指向一维数组的指针,因此我们不能省略数组的列,否则会导致形参数组指针的类型不明确,数组指针指向的数组的元素个数也是数组指针类型的一部分,省略了就无法确定指向多少元素的数组。而由于降维为一维数组指针,行的个数我们就可以省略掉了。因此1,3是正确的写法;2的写法是错误的。
其次我们来看以指针的形式接收,二维数组传参降维为指向一维数组的指针,所以我们也需用一维整形数组指针来接收。而4是整形指针,5是指针数组,7是二级指针,因此只有6是正确的写法。
4.3一级指针传参
当实参是一级指针时,很简单,不要想的太复杂,函数形参就使用一级指针接收。并没有用一维数组接收的做法。如下:
#include <stdio.h>
void print(int* p, int sz) //用整形指针接收
{int i = 0;for (i = 0; i < sz; i++){printf("%d\n", *(p + i));}
}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9 };int* p = arr; //arr:首元素地址,整形指针接收int sz = sizeof(arr) / sizeof(arr[0]);//一级指针p,传给函数print(p, sz);return 0;
}
下面我们可以思考以下,当函数形参是一级指针时,函数可以接收什么参数呢?
Sush as:
void test01(int* p); //test01()能接收什么参数
void test02(char* p); //test02()能接收什么参数
是不是会感觉有点别扭。正常,逆向思维往往是大部分人所欠缺的,下面我们逐一分析。
对于test01(),形参是整形指针,因此毋庸置疑可以接收一个整形指针;并且由于一维整形数组传参时传入的也是整形指针,因此也可以接收一维整形数组:
void test(int* p) {};
int main()
{int n = 10;int* pn = &n;int arr[10] = { 10 };//传入一级整形指针test(&n);test(pn);//传入整形数组test(arr);return 0;
}
对于test02(),形参是字符指针,同理,可以接收字符指针或字符数组。并且,由于C语言中字符串常量的值是其第一个字符的地址,因此test02()还可以接收一个字符串常量:
void test(char* p) {};
int main()
{char c = 'b';char* pc = &c;char arr[10] = { 'b'};//传入一级字符指针test(&c);test(pc);//传入字符数组test(arr);return 0;
}
4.4二级指针传参
同理,二级指针传参,函数形参用二级指针接收:
void print(int** p) //形参用二级指针
{printf("num = %d\n", **p);
}
int main()
{int n = 10;int* p = &n;int** pp = &p; //二级指针print(pp); //二级指针传参return 0;
}
那么,当函数形参是二级指针,函数又可以接收什么参数呢?
void test01(int** p) //test01()接收什么参数
void test02(char** p) //test02()接收什么参数
对于test01(),形参是二级整形指针,因此函数可以接收一个二级整形指针,并且由于一级整形指针数组传参时传入的是一级整形指针的地址,即二级整形指针,因此函数也可以接收一个一级整形指针数组:
void test(int** p) {};
int main()
{int n = 10;int* pn = &n;int** ppn = &pn;int* arr[10] = { pn };//传入二级整形指针test(&pn);test(ppn);//传入一级整形指针数组test(arr);return 0;
}
对于test02(),形参是二级字符指针,同理可以接收一个二级字符指针或者一级字符指针数组。然后由于字符串常量的值是首字符的地址,所以一个字符串数组传参时传入的即为二级字符指针,因此还可以接收一个字符串数组:
void test(char** p) {};
int main()
{char c = 'b';char* pc = &c;char** ppc = &pc;char* arr[10] = {pc};char* arr1[10] = { "hello","world" };//传入二级字符指针test(&pc);test(ppc);//传入一级字符指针数组test(arr);//传入字符串数组test(arr1);return 0;
}
5. 函数指针⛄
5.1 引入
在C语言中,函数也是有地址的,函数是代码的一部分,程序在运行时,要先将代码加载到内存中以便后续访问,而代码就保存在C程序地址空间中的代码区中。我们来看以下代码:
int main()
{printf("%p\n", main);printf("%p\n", &main);return 0;
}
输出的地址即为main函数在代码区的地址。既然是地址,我们能不能将它保存起来呢?这就要谈谈我们的主角了:函数指针(Function pointer)
5.2 函数指针的使用
int test(int x)
{return x + 10;
}
int main()
{int (*p1)(int x) = test;int (*p2)(int x) = &test;return 0;
}
其实使用起来很简单,类似于数组指针,*号与p1结合说明p1是一个指针,右边的(int x)代表p1指向函数有一个形参,形参类型为int;左边的int表示函数的返回类型为int。
上述p1和p2其实都是指向test函数的指针,这是因为与数组不同的是,函数名和&函数名等价,都为函数的地址。因此,我们可以用以下两种方法调用函数:
int test(int x)
{return x + 10;
}
int main()
{int (*p1)(int x) = test;printf("p1: %d\n", (*p1)(10)); //1printf("p1: %d\n", p1(10)); //2return 0;
}
通过函数指针来调用函数时,*号其实就是个摆设,有没有都不影响。但是需要注意的是,如果使用*号需要加上括号,不然p1会先与括号结合,p1就变成一个函数了。
5.3两个有趣的代码
我们来分析一下下面两条语句:
//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);
遇到这种代码不要慌,先抓主体。对于代码1:主体就是0,然后左边的(void (*)())就是强制类型转化,将0强制类型转化为函数指针,指向的函数无参,返回类型为void。最后对其解引用、调用函数。 因此代码1是一条函数调用语句。
而对于代码2:主体就为signal,signal先于括号结合,因此为一个函数,有两个参数,一个为整形,另一个为函数指针。然后剩下的就为signal的返回值,为函数指针。因此代码2就是声明一个signal函数,函数有两个参数:整形和函数指针,返回值为函数指针。
6. 函数指针数组🌷
6.1引入
我们之前学过了指针数组,其存放的每个元素类型是指针。那当我们把函数的地址保存到数组中,这个数组就叫函数指针数组。写法如下:
int(*pa[10])()
pa先与[10]结合,说明pa是个数组,数组的内容是什么呢?是int(*)()类型的函数指针。
6.2 应用-转移表
假设我们需要实现一个整数计算器,具有加减乘除的功能,我们可能会这样写代码:
int add(int x,int y)
{return x + y;
}
int sub(int x, int y)
{return x - y;
}
int mul(int x, int y)
{return x * y;
}
int div(int x, int y)
{return x / y;
}
void menu()
{printf("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:div \n");printf(" 0:exit \n");printf("*************************\n");
}
int main()
{int x, y;int input = 0;int ret = 0;do{menu();printf("请选择:> ");scanf("%d", &input);switch (input){case 0:break;case 1:printf("请输入两个操作数:> ");scanf("%d %d", &x, &y);ret = add(x, y);printf("ret = %d\n", ret);break;case 2:printf("请输入两个操作数:> ");scanf("%d %d", &x, &y);ret = sub(x, y);printf("ret = %d\n", ret);break;case 3:printf("请输入两个操作数:> ");scanf("%d %d", &x, &y);ret = mul(x, y);printf("ret = %d\n", ret);break;case 4:printf("请输入两个操作数:> ");scanf("%d %d", &x, &y);ret = div(x, y);printf("ret = %d\n", ret);break;default:printf("输入错误,请重新输入\n");break;}} while(input);return 0;
}
当我们计算器的选项越来越多,switch-case语句也会越来越长,并且我们发现switch-case语句中每个分支我们做的操作都非常相似,显得代码十分冗长。那我们要如何优化这个代码呢?嘿嘿,这就要问问神奇的函数指针数组了:
int add(int x, int y)
{return x + y;
}
int sub(int x, int y)
{return x - y;
}
int mul(int x, int y)
{return x * y;
}
int div(int x, int y)
{return x / y;
}
void menu()
{printf("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:div \n");printf(" 0:exit \n");printf("*************************\n");
}
int main()
{int x, y;int input = 0;int ret = 0;int (*pa[5])(int, int) = { NULL,add,sub,mul,div }; //转移表int sz = sizeof(pa) / sizeof(pa[0]);do{menu();printf("请选择:> ");scanf("%d", &input);if (input > 0 && input < sz){printf("请输入两个操作数:> ");scanf("%d %d", &x, &y);ret = pa[input](x, y);printf("%d\n", ret);}else if(input!=0){printf("输入有误,请重新输入\n");}} while (input);return 0;
}
首先,我们将加减乘除四个函数用一个函数指针数组存放起来,其中NULL作用是占位,使得后续函数的下标与我们的输入对应起来,方便后续代码的编写。
其次,当input为函数指针数组pa的有效下标时,就通过对应下标的函数指针调用函数,这种通过下标来实现跳转到相应函数的函数指针数组我们就称作转移表。简单来说,转移表就是一个函数指针数组,数组的内容是自定义函数,我们通过数组下标访问自定义函数。
最后,当我们后续需要添加函数时,我们只需要在转移表(函数指针数组)中添加对应函数即可,十分方便 。
注意❗
由于数组每个元素类型相同,因此转移表中函数指针的类型必须严格一致,即指向的自定义函数参数列表和返回值都要一样,否则转移表就不适用了。
例如:上面将计算器的除法函数返回值改为double,我们就不能单纯的使用一个转移表来编写代码了。
7. 指向函数指针数组的指针🌵
指向函数指针数组的指针是一个指针,指针指向一个数组,数组的每一个元素都是指针,那我们应该如何定义它呢?来看以下代码:
void test(const char* str)
{printf("%s", str);
}
int main()
{//定义一个函数指针void(*pf)(const char*) = test;//定义一个函数指针数组void(*arr[5])(const char*);//给函数指针数组第一个元素赋值arr[0] = test;//定义一个指向函数指针数组的指针,并将其指向函数指针数组void(*(*parr)[5])(const char*) = &arr;return 0;
}
void(*(*parr)[5])(const char*) = &arr;
我们来看这条语句,首先parr先于*号结合,说明它是一个指针;然后与[]结合,说明它指向了一个数组。然后剩余的部分就是数组的元素类型,是一个函数指针。综上,parr是一个指向函数指针数组的指针变量,存放着arr这个函数指针数组的地址。
🌸以上,就是本期的全部内容啦
🍜像指针这种高档食材,我们当然要细细品味,剩下的部分我们就留到下期不见不散啦^-^
制作不易,能否点个赞再走呢🙏
相关文章:
【C语言】深度理解指针(上)
前言🌊谈到指针,想必大家都不陌生。它不仅是C语言的重难点,还是不少C初学者的噩梦。本期我们将深度探讨一些较为复杂的指针以及指针的妙用,带领大家感受指针的魅力😝。首先,我们先来复习复习指针的概念&…...
最近我的视频播放浅学总结
因为想做一个类似苹果的同播共享功能,这一段时间对音视频做了一些浅浅的学习,现简单总结记录。 我的需求是找到一个尽可能简单的方案来两人播放一段视频,并且能够进度和操作同步,所以基本不能有延迟,同时能够显示WebV…...
【C/C++基础知识点】输出n位斐波那契数列
目录 前言什么是斐波那契数列兔子的故事小知识点收尾前言 在软件行业已经有快十年,技术虽然一般般,但是足够应付额解决编程入门的相关问题! 都说十年磨一剑,积累到一定经验,是时候发挥自己的价值,给予入门的同行些许的帮助! 为什么要写收费专栏,其实原因很简单,时间就…...
C语言拔高知识——指针的进阶(万字大文超详细)
在之前的文章中,我已经讲解过了初阶指针的内容,今天就来讲一讲指针的进阶! 上篇指针地址:保姆式指针讲解,超详细,适合初学者_指针详解_陈大大陈的博客-CSDN博客 目录 1. 字符指针 2. 指针数组 3. 数组指…...
程序员推荐的良心网站合集!(第二期)
今天来给大家推荐几个程序员必看的国外良心网站合集第二期合集。 Semantic Schoolar 由微软联合创始人Paul Allen开发的免费学术搜索引擎,不仅可以通过时间线快速定位想要的文献,还有强大的筛选功能可以精准的找到自己想要的文献,想要什么搜…...
【Java核心知识】spring boot整合Mybatis plus + Phoenix 访问Hbase与使用注意
为了Phoenix能让开发者通过SQL访问Hbase而不必使用原生的方式?引用Phoenix官网上的一句话:SQL is just a way of expressing what you want to get not how you want to get it. 即SQL不是一种数据操作技术,而是一种特殊的表达方式。只是表示…...
lua实现游戏全局鼠标点击效果
前言 最近在优化项目,策划提了一个需求,需要实现一个通用点击特效。 尝试1 首先想到的是改变鼠标指针样式,这个以前学过,还有点印象,以前刚开始学unity的时候,记得看到过一个方法可以改变游戏中鼠标指针样式。 方法如下:选择“Edit”——>“Project Setting”,打…...
MyBatis源码分析(二、续)SqlSource创建流程,SQL如何解析?如何将#{id}变成?的
文章目录实例一、SqlSource处理入口二、SqlSource处理逻辑1、XMLScriptBuilder 构造方法2、解析动态sql3、DynamicSqlSource4、RawSqlSource解析sql(1)parse方法解析sql写在后面实例 此处我们分析的sql: <select id"selectBlog&quo…...
用 C 语言开发一门编程语言 — 函数库的设计与实现
目录 文章目录目录前言前文列表基础功能演示数字运算变量与代数运算列表处理Lambda 函数条件分支字符串源文件加载函数库列表处理函数库条件分支函数库数学库前言 通过开发一门类 Lisp 的编程语言来理解编程语言的设计思想,本实践来自著名的《Build Your Own Lisp》…...
网络层IP协议与数据链路层以太网协议
文章目录一、IP协议IP地址地址管理路由选择DNS二、以太网协议以太网帧MTU一、IP协议 IP协议是我们网络层的代表协议,今天我们就来一起学习一下吧,我们这里介绍的主要是IPv4协议。 版本:指定IP协议的版本,版本的取值只有4&#x…...
JDK动态代理详解
1.什么是动态代理 可能很多小伙伴首次接触动态代理这个名词的时候,或者是在面试过程中被问到动态代理的时候,不能很好的描述出来,动态代理到底是个什么高大上的技术。不方,其实动态代理的使用非常广泛,例如我们平常使用…...
实时的软件生成 —— Prompt 编程打通低代码的最后一公里?
PS:这也是一篇畅想,虽然经过了一番试验,依旧有一些不足,但是大体上站得住脚。传统的软件生成方式需要程序员编写大量的代码,然后进行测试、发布等一系列繁琐的流程。而实时生成技术则是借助人工智能技术,让…...
互联网工程师 1480 道 Java 面试题及答案整理 ( 2023 年 整理版)
最近很多粉丝朋友私信我说:熬过了去年的寒冬却没熬过现在的内卷;打开 Boss 直拒一排已读不回,回的基本都是外包,薪资还给的不高,对技术水平要求也远超从前;感觉 Java 一个初中级岗位有上千人同时竞争&#…...
Spark开发
第一步:创建RDD Spark提供三种创建RDD方式:** 集合、本地文件、HDFS文件** 使用程序中的集合创建RDD,主要用于进行测试,可以在实际部署到集群运行之前,自己使用集合构造一些测试数据,来测试后面的spark应…...
Tornado异步框架
简介: tornado是Python的web框架。tornado和主流的web服务器框架有明显的区别:它是非阻塞式服务器,而且速度非常快,得力于其非阻塞的方式和epoll的运用tornado可以每秒处理数以千计的连接(号称) 基本配置 …...
openpnp - error - 吸嘴没下降到板子上, 就将元件松开
文章目录openpnp - error - 吸嘴没下降到板子上, 就将元件松开概述笔记ENDopenpnp - error - 吸嘴没下降到板子上, 就将元件松开 概述 以前用过国内一家openpnp厂家出的设备, 他们家的openpnp是自己改过的. 贴片流程已经走过一遍. 这次还是按照以前记录的笔记, 按照国内那家的…...
【Java】yyyy-MM-dd HH:mm:ss 时间格式 时间戳 全面解读超详细
时间格式 时间格式(协议)描述gg时期或纪元。y不包含纪元的年份。不具有前导零。yy不包含纪元的年份。具有前导零。yyyy包含纪元的四位数的年份。M月份数字。一位数的月份没有前导零。MM月份数字。一位数的月份有一个前导零。MMM月份的缩写名称,在AbbreviatedMonthN…...
快鲸SCRM发布口腔企业私域运营解决方案
口腔企业普遍面临着以下几方面运营痛点问题 1、获客成本居高不下,恶性竞争严重 2、管理系统落后,人员流失严重 3、客户顾虑多、决策时间长 4、老客户易流失,粘性差 以上这些痛点,不得不倒逼口腔企业向精细化运营客户迈进。 …...
Verilog实现组合逻辑电路
在verilog 中可以实现的数字电路主要分为两类----组合逻辑电路和时序逻辑电路。组合逻辑电路比较简单,仅由基本逻辑门组成---如与门、或门和非门等。当电路的输入发生变化时,输出几乎(信号在电路中传递时会有一小段延迟)立即就发生…...
2023前端菜鸟笔试血泪史html5-one--找到工作前都更新
1.说说对html语义化的理解 什么的HTML语义化,顾名思义,HTML语义化就是可以不通过了解HTML的内容,就可以知道这个部分所代表的的意义。 HTML语义化的意义:在使用HTML标签构建页面时,避免大篇幅的使用无语义的标签。 …...
蓝牙调试工具集合汇总
BLE 该部分主要分享一下常用的蓝牙调试工具,方便后续蓝牙抓包及分析。 目录 1 hciconfig 2 hcitool 3 hcidump 4 hciattach 5 btmon 6 bluetoothd 7 bluetoothctl 1 hciconfig 工具介绍:hciconfig,HCI 设备配置工具 命令格式&…...
Java 获取文件后缀名【一文总结所有方法】
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
UML常见图的总结
一、概述 UML:Unified Modeling Language,统一建模语言,支持从需求分析开始的软件开发的全过程。是一个支持模型化和软件系统开发的图形化语言、为软件开发的所有阶段提供模型化和可视化支持,包括由需求分析到规格,到…...
WebRTC系列-工具系列之音频相关工具
文章目录 1. audio_util数据格式转换类2. WavFile文件读写类2.1 读取wav文件2.2 写入wav文件这篇文章主要介绍WebRTC中一些音频工具这些,大部分都在 common_audio目录下,这个文件夹下提供音频的大量算法,包括sinc重采样算法,音频数据格式的转换:例如 float转int16_t格式等…...
7 线性回归及Python实现
1 统计指标 随机变量XXX的理论平均值称为期望: μE(X)\mu E(X)μE(X)但现实中通常不知道μ\muμ, 因此使用已知样本来获取均值 X‾1n∑i1nXi.\overline{X} \frac{1}{n} \sum_{i 1}^n X_i. Xn1i1∑nXi.方差variance定义为: σ2E(∣X−μ∣2).\sigma^2 E(|…...
适合小团队协作、任务管理、计划和进度跟踪的项目任务管理工具有哪些?
适合小团队协作、任务管理、计划和进度跟踪的项目任务管理工具有哪些? 大家可以参考这个模板:http://s.fanruan.com/irhj8管理项目归根结底在管理人、物,扩展来说便是: 人:员工能力、组织机制; 物:项目内…...
从100%进口到自主可控,从600块降到10块,中科院攻克重要芯片
前言 2月28日,“20多位中科院专家把芯片价格打到10块”冲上微博热搜,据河南省官媒大象新闻报道,热搜中提到的中科院专家所在企业为全球最大的PLC分路器芯片制造商仕佳光子,坐落于河南鹤壁。 为实现芯片技术自主可控自立自强&#…...
关于git的一些基本点总结
1.什么是git? git是一个常用的分布式版本管理工具。 2.git 的常用命令: clone(克隆): 从远程仓库中克隆代码到本地仓库 checkout (检出):从本地仓库中检出一个仓库分支然后进行修订 add(添加): 在提交前…...
PyTorch保姆级安装教程
1 安装CUDA1.1 查找Nvidia适用的CUDA版本桌面右键,【打开 NVIDIA控制面板】查看【系统信息】查看NVIDIA的支持的CUDA的版本,下图可知支持的版本是 10.11.2 下载CUDACUDA下载官方网址https://developer.nvidia.com/cuda-toolkit-archive找到适合的版本下载…...
MySQL 上亿大表如何优化?
背景XX 实例(一主一从)xxx 告警中每天凌晨在报 SLA 报警,该报警的意思是存在一定的主从延迟。(若在此时发生主从切换,需要长时间才可以完成切换,要追延迟来保证主从数据的一致性)XX 实例的慢查询…...
多语种网站制作/平台软件定制开发
一、压缩与解压缩1、compress [-rcv] 文件或目录 <压缩uncompress 文件.Z <解压缩-r:可以连同目录下的文件也同时进行压缩-c:将压缩数据输出成standard output(输出到屏幕)-v:可以显示出压缩后的文件信息以及压缩过程中的一些文件名变化(这个命令是非常老旧的一款)2、gzi…...
管理学精品课程网站/抖音代运营公司
为什么80%的码农都做不了架构师?>>> Docker数据管理:Named volume Docker中可以使用Named volume和data container来进行数据的管理。 单一Container的使用Helloworld 来源:http://www.youruncloud.com/docker/1_71.html 转载于:…...
不用源码做网站/人教版优化设计电子书
【题目来源】https://www.acwing.com/problem/content/879/【问题描述】 给定 n 对正整数 ai,bi,对于每对数,求出一组 xi,yi,使其满足 aixibiyigcd(ai,bi)。【输入格式】 第一行包含整数 n。 接下来 n 行,每行包含两个整数 ai,bi。…...
wordpress 主题分享/app推广
一、硬件材料 1*Arduino UNO开发板 1*GSP30 1*0.96寸OLED液晶显示屏 二、硬件接线图 CSDN 赤鱼科技...
让别人做网站应注意什么/营销策略都有哪些
7:30−9:307:30 - 9:307:30−9:30 哭了哭了 , T1T1T1真的给我写崩了QWQ 。 我的思路是这样的 : 根据数据范围 : 4<k<84 < k < 84<k<8 , k4k 4k4这一组数据 , 跑个状压就可以轻松拿到 , …...
北京免费网站建设模板下载/谷歌seo靠谱吗
经常遇到的面试题,一个类继承另一个类然后问输出的结果是什么。个人觉得就是考类的输出问题(亲测) 第一:单一的类执行顺序 package Test; public class person { static{ System.out.println("父类的静态块"); } per…...