深度刨析程序中的指针

前面我们已经学习过了指针的一下性质:

  • 指针就是个变量,用来存放地址,地址唯一标识的一块内存空间
  • 指针的大小是固定的4/8个字节(32位平台/64位平台)
  • 指针是有类型,指针的类型决定了指针的加减整数的步长,指针解引用操作时的权限。
  • 两个指针相减返回的是两指针间元素的个数(同类型指针)

文章目录

  • 1.字符指针
  • 2.指针数组
  • 3.数组指针
    • 3.1 数组指针的定义
    • 3.2 &数组名与数组名
    • 3.3 数组指针的使用
  • 4.数组传参、指针参数
    • 4.1 一维数组传参
    • 4.2 二维数组传参
    • 4.3 一级指针传参
    • 4.4 二级指针传参
  • 5.函数指针
  • 6.函数指针数组
  • 7.指向函数指针数组的指针
  • 8.回调函数

1.字符指针

在指针的类型中我们知道有一种指针类型为字符指针char*
一般使用情况

#include <stdio.h>
int main()
{char c = 'a';char* pc = &c;*pc = 'y';return 0;
}

还有一种情况

#include <stdio.h>
int main()
{const char* str = "hello world";//把"hello world"的首元素的地址给了str//但是不能单纯的理解为数组,这里的"hello world"是存放代码区中的不可修改,是常量字符串,所以我们在前面加了const修饰printf("%s\n",str);return 0;
}

指针

本质就是把常量字符串hello world的首元素的地址放到了str当中,也就是将常量字符串的首元素h的地址放到str中
练习

#include <stdio.h>
int main()
{char str1[] = "hello world";char str2[] = "hello world";const char* str3 = "hello bit";const char* str4 = "hello bit";if(str1==str2)printf("same\n");elseprintf("not same\n");if(str3==str4)printf("same\n");elseprintf("not same\n");return 0;
}//打印结果:
/*
not same
same
*/

这里比较的都是地址。
str1和str2都是数组,当用相同的常量字符串去初始化不同的数组的时候就会开辟不同的空间。而str3和str4指向的同一个常量字符串。在c/c++中会把常量字符串单独存储在一个内存区域(代码段),当我们用几个指针去指向同一个字符串时,它们实际会指向同一块内存的。
可以这么理解:str1和str2是可以修改数组中的元素的,如果不同数组间的修改会相互影响,那岂不是乱遭了。而str3和str4是不可以被修改的,那么让它们两指向同一块空间也是完全没有问题的。

2.指针数组

指针数组就是存放指针的数组。
我们可以进行类比:
整型数组是存放整型的数组,字符数组是存放字符的数组。那么指针数组肯定就是存放指针的数组咯。

int* arr1[10];//整型指针的数组
char* arr2[4];//一级字符指针数组
char** arr3[10];//二级字符指针数组

3.数组指针

3.1 数组指针的定义

我们知道整型指针是指向整型的指针(存放整型变量的地址的指针变量)
还有字符指针是指向字符的指针(存放字符变量的地址的指针变量)
如此类比的话
数组指针就是指向数组的指针(存放数组变量的地址的指针变量)
数组指针的正确写法

int *p1[10];//错
int (*p2)[10];//对
int (*p)[10];
//p与*结合,说明p是一个指针变量,然后指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
//加()的原因是因为,根据操作符的优先性,[]的优先级是要高于*的,为了保证*与p的结合需要添加括号

3.2 &数组名与数组名

int arr[10];

arr&arr分别是什么呢?
arr是数组名,数组名又表示数组首元素的地址。
&arr表示的整个数组的地址。

#include <stdio.h>
int main()
{int arr[10];printf("%p\n",arr);printf("%p\n",&arr);return 0;
}
//打印结果:
/*
00DCFBDC
00DCFBDC
*/

打印它们的地址可以发现是一样的。但其实又不完全一样。

#include <stdio.h>
int main()
{int arr[10];printf("%p\n",arr);printf("%p\n",arr+1);printf("%p\n",&arr);printf("%p\n",&arr+1);return 0;
}
//打印结果:
/*
004FF970
004FF974
004FF970
004FF998
*/

arr+1跳过的4个字节的地址。而&arr跳过的是40个字节的地址。
正如前面所说&arr是整个数组的地址,整个数组大小就是40个字节。
本例中&arr的类型就是int(*)[10],是一种数组指针类型。
数组地址+1,跳过整个数组的大小,所以&arr+1相对于&arr的差值就是40.

3.3 数组指针的使用

了解到数组指针指向的数组,那么数组指针中存放的就是数组的地址。

#include <stdio.h>
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,0};int(*p)[10] = &arr;//把整个数组的地址存放在数组指针变量当中//但是很少这么写return 0;
}

数组指针的使用

#include <stdio.h>
void print1(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 print2(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,6,7,8,9,0};print1(arr,3,5);//arr是数组的数组名,表示数组首元素的地址。而这又是一个二维数组,二维数组的首元素地址就是第一行的地址,所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址,可以利用数组指针接收。print2(arr,3,5);return 0;
}

区分

int arr[5];//整型数组
int* parr1[10];//整型指针数组
int (*parr2)[10];//数组指针
int (*parr3[10])[5];//数组指针数组

4.数组传参、指针参数

在写代码时不可避免的要把【数组】或者【指针】传递给函数,那么函数的参数设计要怎么做呢?

4.1 一维数组传参

#include <stdio.h>
void test(int arr[])//可行,最容易理解的写法([]内的数字可以随便写,不影响系统的判断)该传参的本质就是int* arr
{}
void test(int arr[10])//可行,最容易理解的写法。([]内的数字可以随便写,不影响系统的判断)该传参的本质就是int* arr
{}
void test(int* arr)//可行,实参传递的是arr代表数组首元素的地址,利用整型指针接收合情合理
{}
void test2(int *arr[20])//可行,本质是int** arr
{}
void test2(int **arr)//可行,实参传递的是arr2也代表首元素的地址,因为arr2是指针数组,一维指针的地址要有二级指针接收,合情合理
{}int main()
{int arr[10] = {0};int* arr2[20] = {0};test(arr);test2(arr2);return 0;
}

4.2 二维数组传参

#include <stdio.h>void test(int arr[3][5])//可行,最容易理解的写法
{}
void test(int arr[][])//不可行,
{}
void test(int arr[][5])//可行
{}
//二维数组传参,函数形参的设计只能省略第一个[]的数字。
//对一个二维数组,可以不知道又多少行,但是必须知道要有多少列。
//因为在内存的二维数组的存放也是线性的,全存一行。知道列数才能知道有多少行。
void test(int* arr)//不可行,二维数组的数组名代表的是数组第一行的地址,是数组指针。要存放这个数组指针是无法用整型指针存放
{}
void test(int* arr[5])//不可行,二维数组的数组名代表的是数组第一行的地址,是数组指针。而这个表示的是指针数组
{}
void test(int (*arr)[5])//可行
{}
void test(int** arr)//不可行,二维数组的数组名代表的是数组第一行的地址,是数组指针.这里是二级指针,不一致。
{}
int main()
{int arr[3][5] = {0};test(arr);return 0;
}

4.3 一级指针传参

#include <stdio.h>
void print(int* arr,int sz)
{for(int i = 0;i<sz;++i){printf("%d ",*(arr+i));}
}
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,0};int sz = 10;int* p = arr;print(arr,sz);return 0;
}
//打印结果:1 2 3 4 5 6 7 8 9 0

当函数的参数部分为1级指针的时候,函数能接受的的参数为该一级指针对应类型的地址。

4.4 二级指针传参

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

当函数的参数部分为二级指针的时候,函数能接受的的参数为该二级指针对应一级指针类型的地址。

5.函数指针

其实函数也是有地址的。

#include <stdio.h>
void test()
{printf("hello\n");
}
int main()
{printf("%p\n",test);printf("%p\n",&test);return 0;
}
//打印结果
/*
00A41267
00A41267
*/

从这里可以看出,函数不仅有地址,而且函数的函数名就代表了函数的地址,&函数名同样也表示函数的地址。
既然函数有地址,那么也就说明可以利用变量来存储。这个存储函数地址的变量就是函数指针。
函数指针的正确写法

#include <stdio.h>
void test()
{printf("hello\n");
}
int main()
{void (*pf1)() = test;//正确写法void *pf2() = test;//错误写法return 0;
}

pf1可以存储,和数组指针类似,这里的*要先和pf1结合,确定pf1是一个指针,()的优先级有比较高。因此需要用()将*pf1括起来。pf1指向的是一个函数,指向函数无参数,返回类型为void。

#include <stdio.h>
int Add(int x,int y)
{return x+y;
}
int main()
{int (*pf)(int int) = Add//指向有参数的函数指针return 0;
}

练习

//代码1
(*(void(*)())0)();
/*
解释:
先看void(*)()这是一个函数指针类型。再往外看,这个函数指针类型被括号括住了(void(*)())
一个类型被()住就是表示强制类型转换的意思。也是说明0被强制类型转换成了函数指针类型。然后*表示对一个函数指针类型进行解引用取出指向的函数*(void(*)())0,最后再调用这个函数。
总结:调用0地址处的函数(实际是无法调用的)
*///代码2
void (*signal(int,void(*)(int)))(int);//signal为函数名
/*
解释:signal是函数名,那signal()中的就是函数的参数类型,类型分别为整型和函数指针类型,现在一个函数有了函数名和函数的参数,就差函数的返回类型,如果我们把signal(int,void(*)(int))删除就得到了void (*)(int)这不就是函数指针类型吗,那也就是说signal的函数的返回类型就是void(*)(int)
总结:这是一个函数的声明,找到其函数参数和函数返回类型就可以了。
*/

简化代码2

typedef void (*pf)(int);
pf signal (int,pf);//利用typedef将类型重命名,来简化代码

6.函数指针数组

数组是存放相同类型数据的存储空间
前面我们已经学习了指针数组

int* arr[10];
//数组的每个元素类型是int*

同样的我们也可以把函数指针存放进数组,就叫做函数指针数组,那函数指针数组的是如何定义的呢?

int (*pf)();//这是一个函数指针
//我们将[]添加到变量名后面就可以了
int (*pf[10])();//这就是函数指针数组

pf[]结合说明pf是一个数组,然后数组存放的类型就是int(*)()
函数指针数组的运用
下面以实现一个简单的计算器为例

#include <stdio.h>
void menu()
{printf("***************************\n");printf("**** 1.Add   2.Sub     ****\n");printf("**** 3.Mul   4.Div     ****\n");printf("**** 0.exit            ****\n");printf("***************************\n");
}
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;
}
int main()
{int input = 0;int a = 0,b = 0;int ret = 0;do{menu();printf("选择你所要用到的功能>\n");scanf("%d",&input);switch(input){case 1:printf("请输入两数>");scanf("%d %d",&a,&b);ret = Add(a,b);printf("ret = %d\n",ret);break;case 2:printf("请输入两数>");scanf("%d %d",&a,&b);ret = Sub(a,b);printf("ret = %d\n",ret);break;case 3:printf("请输入两数>");scanf("%d %d",&a,&b);ret = Mul(a,b);printf("ret = %d\n",ret);break;case 4:printf("请输入两数>");scanf("%d %d",&a,&b);ret = Div(a,b);printf("ret = %d\n",ret);break;case 0:printf("退出\n");break;default:printf("输入错误\n");break;}}while(input);return 0;
}

这样写的话其实是很繁琐的。实现这种简单的功能都写了这么长的代码,而且如果后续在添加什么函数功能的话,代码又要增加好的。所以我们要简化。
通过观察,这4个函数的参数和返回类型都是相同的,那么不就说明了可以写成函数指针数组吗。数组中存放这个函数指针类型就可以了。

#include <stdio.h>
void menu()
{printf("***************************\n");printf("**** 1.Add   2.Sub     ****\n");printf("**** 3.Mul   4.Div     ****\n");printf("**** 0.exit            ****\n");printf("***************************\n");
}
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;
}
int main()
{int input = 0;int a = 0,b = 0;int ret = 0;do{menu();printf("选择你所要用到的功能>\n");scanf("%d",&input);int(*pf[5])(int,int) = {NULL,Add,Sub,Mul,Div};//存入NULL是为了可以和菜单对应if(input>0&&input<5){printf("请输入两数>");scanf("%d %d",&a,&b);ret = pf[input](a,b);printf("ret = %d\n",ret);}else if(input == 0)printf("退出\n");elseprintf("输入有误\n");}while(input);return 0;
}

利用函数指针数组我们将该程序充分简化,而且如果后续还要添加类似的函数功能的话,我们只需要将新写的函数添加进数组,在改变一下判断条件即可。

7.指向函数指针数组的指针

指向函数指针数组的指针是一个指针。
指针指向一个数组,数组的元素都是函数指针;

void test(const char* str)
{printf("%s\n",str);
}
int main()
{//函数指针pfunvoid(*pfun)(const char*) = test;//函数指针的数组pfunarrvoid(*pfunarr[5])(const char* str);//指向函数指针数组pfunarr的指针ppfunarrvoid(*(*ppfunarr)[5])(const char*) = &pfunarr;
}

还可以再绕下去的

8.回调函数

回调函数就是一个通过函数指针调用的函数,如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数,我们就说这是回调函数。回调函数不是由该函数的实现直接调用,而是再特定的事件或条件发生时由另一方的调用,用于对该事件或条件进行响应。

首先演示一下qsort函数的使用:

//对整型数组进行排序
#include<stdio.h>
int int_cmp(const void* a,const void* b)
{return (*(int*)a) - (*(int*)b);
}
int main()
{int arr[10] = {1,3,5,7,9,2,4,6,8,0};qsort(arr,10,sizeof(int),int_cmp);for(int i = 0;i<10;++i){printf("%d ",arr[i]);}printf("\n");return 0;
}
//打印结果:
//0 1 2 3 4 5 6 7 8 9//对结构体数组进行排序
#include <stdio.h>
#include <string.h>
struct stu
{int age;char name[10];
};
int struct_cmp_age(const void* a, const void* b)//利用年龄排序
{return ((struct stu*)a)->age - ((struct stu*)b)->age;
}
int struct_cmp_name(const void* a, const void* b)//利用名字排序,因为字符串无法相减
//所以这里利用了strcmp进行字符串的比较
{return strcmp(((struct stu*)a)->name,((struct stu*)b)->name);
}
int main()
{struct stu s[3] = { {17,"yui"},{14,"anna"},{20,"hua"} };qsort(s, 3, sizeof(s[0]), struct_cmp_age);printf("调用struct_cmp_age\n");for (int i = 0; i < 3; ++i){printf("%d %s\n", s[i].age, s[i].name);}qsort(s, 3, sizeof(s[0]), struct_cmp_name);printf("调用struct_cmp_name\n");for (int i = 0; i < 3; ++i){printf("%d %s\n", s[i].age, s[i].name);}return 0;
}
//打印结果:
/*
调用struct_cmp_age
14 anna
17 yui
20 hua
调用struct_cmp_name
14 anna
20 hua
17 yui*/

qsort
打开cplusplus网站->qsort
qsort

void qsort(void* base,//需要排序的数组首元素地址size_t num,//需要排序的数组的元素个数size_t size,//需要排序数组的单个元素的大小int (*compar)(const void*,const void*)//传递函数指针,需要自己写}

可以看到的时,这里接受数组首元素的地址是用void*来接收。
提问:为什么呢?
回答:Void*指针 是无具体类型的指针。Void* 类型的指针可以接任意类型的地址(这种类型的指针是不能直接解引用操作的,也不能直接进行指针运算的)。
所以用void*接收是没问题的。然后,这个qsort函数不仅可以对整型数组排序,还可以对字符数组,浮点型数组,甚至是结构体数组。这也就造成了不能使用特定类型指针来接收的情况,如果使用了特定的类型,那其它类型就不能被接收了,所以才会选择使用void*来接收。
模拟实现qsort,但是因为目前还没有学快速排序,所以这里我们利用冒泡排序替代。

//主要逻辑
void swap(char* a, char* b, int size)
{char tmp = 0;for (int i = 0; i < size; ++i)//交换的实质其实就是指针所指向内容的交换.//因为char只能指向一个字节,所以我们需要传递size了解到要交换的字节大小,然后一个字节一个字节的交换。{tmp = *a;*a = *b;*b = tmp;a += 1;b += 1;}
}
void bubble_sort(void* base, int num, int size, int(*cmp)(const void*, const void*))
{for (int i = 0; i < num - 1; ++i){for (int j = 0; j < num - i - 1; ++j){if (cmp((char*)base + j * size, (char*)base + (j + 1) * size)>0)//因为void类型的指针是不能直接解引用操作的,也不//能直接进行指针运算的。为了拿到比较位置的地址,我们需要将base强转为(char*),因为char*的加减整数时只会跳过一个字节,//这是最小的位移距离了。所以我们可以通过强转后的base拿到base[j]和base[j+1]的地址进行比较。{swap((char*)base + j * size, (char*)base + (j + 1) * size, size);//开始交换}}}
}

测试

#include <stdio.h>
#include <string.h>
struct stu
{int age;char name[10];
};
int int_cmp(const void* a, const void* b)
{return (*(int*)a) - (*(int*)b);
}
int struct_cmp_age(const void* a, const void* b)//利用年龄排序
{return ((struct stu*)a)->age - ((struct stu*)b)->age;
}
int struct_cmp_name(const void* a, const void* b)//利用名字排序,因为字符串无法相减
//所以这里利用了strcmp进行字符串的比较
{return strcmp(((struct stu*)a)->name,((struct stu*)b)->name);
}
void swap(char* a, char* b, int size)
{char tmp = 0;for (int i = 0; i < size; ++i)//交换的实质其实就是指针所指向内容的交换,因为char只能指向一个字节,所以我们需要传递size了解到要交换的字节大小,然后一个字节一个字节的交换。{tmp = *a;*a = *b;*b = tmp;a += 1;b += 1;}
}
void bubble_sort(void* base, int num, int size, int(*cmp)(const void*, const void*))
{for (int i = 0; i < num - 1; ++i){for (int j = 0; j < num - i - 1; ++j){if (cmp((char*)base + j * size, (char*)base + (j + 1) * size)>0)//因为void类型的指针是不能直接解引用操作的,也不能直接进行指针运算的。为了拿到比较位置的地址,我们需要将base强转为(char*),因为char*的加减整数时只会跳过一个字节,这是最小的位移距离了。所以我们可以通过强转后的base拿到base[j]和base[j+1]的地址进行比较。{swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}
}
int main()
{int arr[10] = { 1,3,5,7,9,2,4,6,8,0 };bubble_sort(arr, 10, sizeof(int), int_cmp);//冒泡排序printf("对arr排序:\n");for (int i = 0; i < 10; ++i){printf("%d ", arr[i]);}printf("\n");struct stu s[3] = { {17,"yui"},{14,"anna"},{20,"hua"} };bubble_sort(s, 3, sizeof(s[0]), struct_cmp_age);printf("调用struct_cmp_age:\n");for (int i = 0; i < 3; ++i){printf("%d %s\n", s[i].age, s[i].name);}bubble_sort(s, 3, sizeof(s[0]), struct_cmp_name);printf("调用struct_cmp_name:\n");for (int i = 0; i < 3; ++i){printf("%d %s\n", s[i].age, s[i].name);}return 0;
}
//打印结果:
/*
对arr排序:
0 1 2 3 4 5 6 7 8 9
调用struct_cmp_age:
14 anna
17 yui
20 hua
调用struct_cmp_name:
14 anna
20 hua
17 yui
*/

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

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

相关文章

SQL 中的 EXISTS 子句:探究其用途与应用

目录 EXISTS 子句简介语法 EXISTS 与 NOT EXISTSEXISTS 子句的工作原理实际应用场景场景一&#xff1a;筛选存在关联数据的记录场景二&#xff1a;优化查询性能 EXISTS 与其他 SQL 结构的比较EXISTS vs. JOINEXISTS vs. IN 多重 EXISTS 条件在 UPDATE 语句中使用 EXISTS常见问题…

Maven学习—如何在IDEA中配置Maven?又如何创建Maven工程?(详细攻略)

目录 前言 1.在IDEA中配置Maven 2.创建Maven项目 &#xff08;1&#xff09;Maven&#xff1a;创建普通Maven工程 &#xff08;2&#xff09;Maven Archetype&#xff1a;创建Maven模板工程 前言 本篇博客将详细的介绍在IDEA中如何配置Maven&#xff0c;以及如何创建一个Ma…

分布式 I/O 系统 BL200 Modbus TCP 耦合器

BL200 耦合器是一个数据采集和控制系统&#xff0c;基于强大的 32 位微处理器设计&#xff0c;采用 Linux 操作系统&#xff0c;支持 Modbus 协议&#xff0c;可以快速接入现场 PLC、SCADA 以及 ERP 系统&#xff0c; 内置逻辑控制、边缘计算应用&#xff0c;适用于 IIoT 和工业…

园区道路车辆智能管控视频解决方案,打造安全畅通的园区交通环境

一、背景需求分析 随着企业园区的快速发展和扩张&#xff0c;道路车辆管理成为了保障园区秩序、提升运营效率及确保员工安全的重要任务。针对这一需求&#xff0c;旭帆科技TSINGSEE青犀提出了一种企业园区道路车辆管控的解决方案&#xff0c;通过整合视频监控、智能识别等技术…

Qt6 OpenCV4视频监控系统项目源码解析——附源码及编译运行步骤

很多刚毕业&#xff0c;或者想着转行到C Qt方向的小伙伴&#xff0c;平时可能拿不出比较像样的项目。这里你可要好好收藏啦。自己拿回去好好改改&#xff0c;就可以成为自己的项目经历了。祝各位找工作顺利呀。 好了。废话不多说。 这个项目架构采用的是MVC架构&#xff0c;结…

Porfinet从转DeviceNet从总线协议转换网关

产品功能 1. 远创智控YC-DNT-PN型是Porfinet从转Devicenet从工业级Porfinet 网关。‌这种网关设备允许将Porfinet网络中的设备连接到Devicenet网络中&#xff0c;‌从而实现不同工业通信协议之间的互操作性。‌这些网关设备通常具有两个以太网接口&#xff0c;‌分别用于连接Po…

STM32智能楼宇照明系统教程

目录 引言环境准备智能楼宇照明系统基础代码实现&#xff1a;实现智能楼宇照明系统 4.1 数据采集模块 4.2 数据处理与控制模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;楼宇照明管理与优化问题解决方案与优化收尾与总结 1. 引言 智能楼宇照明系…

【verilog语法】$clog2(IEEE Std 1364-2005)

一、前言 在进行Verilog设计时&#xff0c;有时需要根据内存大小来计算地址的位宽&#xff0c;或者根据变量的值来确定变量的位宽&#xff0c;这时可以自己编写相关的可综合函数&#xff0c;或者verilog-2005起开始支持的$clog2系统函数。 二、自己编写的function function i…

代码规范性

代码规范性 命名规范代码格式注释代码结构异常处理代码复用代码审查空格的用法代码示例 命名规范 ​ 1、变量和函数名&#xff1a;使用驼峰命名法&#xff08;camelCase&#xff09;&#xff0c;如userName、getUserInfo。 ​ 2、常量&#xff1a;使用全大写字母&#xff0c;…

VSCode安装以及安装你需要的插件的保姆级教程

文章目录 前言vscode的简介安装vscode下载安装包 安装中文插件安装C/C插件总结 前言 Visual Studio Code&#xff08;VSCode&#xff09;是一款免费、开源且强大的代码编辑器&#xff0c;它具有易于使用的界面和丰富的功能&#xff0c;包括高级代码编辑、导航、调试等功能。VS…

远程访问及控制(ssh)

目录 一、OpenSSH服务器 1.1 SSH&#xff08;Secure Shell&#xff09;协议 1.2 OpenSSH 2.1 SSH原理 2.1 1 公钥传输原理 2.1.2 加密原理 2.1.3 SSHD服务的两种验证方式 二、SSH命令中的基本操作 2.1 构建密钥对验证的SSH 2.1.1 公钥和密钥的关系 2.1.2 构建密钥对…

找不到d3dx9_43.dll怎么办,总结5种解决d3dx9_43.dll丢失的方法

很多人经常使用电脑的时候可能遇到过电脑缺失d3dx9_43.dll的情况。这种情况通常是由于不当操作导致病毒感染或软件误删等原因引起的。今天&#xff0c;我将为大家详细讲解电脑缺失d3dx9_43.dll的原因以及几种解决方法。 一、了解d3dx9_43.dll是什么及作用 d3dx9_43.dll 是一个…

腾讯开源 tlbs-map 地图组件库

腾讯宣布开源 tlbs-map&#xff0c;一个基于腾讯位置服务地图 API 开发的 web 端地图组件库&#xff0c;支持在网页绘制地图并在地图上绘制点、线、面、热力图等效果&#xff0c;支持 Vue2、Vue3、React 等业界主流技术栈&#xff0c;旨在帮助开发者低成本开发地图业务 Javascr…

【Blockly图形化积木编程二次开发学习笔记】1.工具箱的实现

文章目录 Blockly 版本选择上手 Blockly 版本选择 在【兰州大学】Blockly创意趣味编程【全36讲】主讲教师&#xff1a;崔向平 周庆国中提到&#xff0c;在18年6月份之前的版本中&#xff0c;可以通过安装依赖库的方式&#xff0c;打开开发者工具的离线版本&#xff0c;但是新版…

框架设计MVVM

重点&#xff1a; 1.viewmodel 包含model 2.view包含viewmodel,通过驱动viewmodel去控制model的数据和业务逻辑 // Test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 //#include <iostream> #include <vector>using namespace std;#p…

iSCSI 网络存储服务部署

一、介绍 iSCSI &#xff08;Internet Small Computer System Interface&#xff09;&#xff0c;互联网小型计算机系统接口&#xff1b;iSCSI 是SCSI接口 与以太网技术相结合的新型存储技术&#xff0c;属于ip san的一种&#xff0c;可以用来在网络中传输 SCSI 接口的命令和数…

逆向案例二十一——sm2加密,某信登录密码加密

网址&#xff1a;电信账号登录 这个登陆页面&#xff0c;会需要过滑块验证码&#xff0c;先不管他&#xff0c;直接找到登陆包&#xff0c;在登陆包之前&#xff0c;有一个这个包返回一串不知道啥作用的先记着。 找到登陆包&#xff0c;密码进行了加密 用xhr断点调试&#xff…

Jmeter常用组件及执行顺序

一 常用组件 1.线程组 Thread Group 线程组是一系列线程的集合&#xff0c;每一个线程代表着一个正在使用应用程序的用户。在 jmeter 中&#xff0c;每个线程意味着模拟一个真实用户向服务器发起请求。 在 jmeter 中&#xff0c;线程组组件运行用户设置线程数量、初始化方式等…

JuiceFS、Ceph 和 MinIO 结合使用

1. 流程图 将 JuiceFS、Ceph 和 MinIO 结合使用&#xff0c;可以充分利用 Ceph 的分布式存储能力、JuiceFS 的高性能文件系统特性&#xff0c;以及 MinIO 提供的对象存储接口。以下是一个方案&#xff0c;介绍如何配置和部署 JuiceFS 使用 Ceph 作为其底层存储&#xff0c;并通…

Linux进程——进程优先级与僵尸进程孤儿进程

文章目录 僵尸进程变成僵尸状态的过程 孤儿进程进程优先级如何修改进程优先级为什么优先级有范围 僵尸进程 僵尸状态进程本质上就是死亡状态 在进程死亡之后&#xff0c;不会直接对进程进行释放&#xff0c;而是先会处理一些后事 进程在结束退出的时候&#xff0c;也会有一些…