【C语言进阶】字符串函数与内存函数的学习与模拟实现
📝个人主页:@Sherry的成长之路
🏠学习社区:Sherry的成长之路(个人社区)
📖专栏链接:C语言进阶
🎯长路漫漫浩浩,万事皆有期待
文章目录
- 1.字符串处理函数介绍
- 1.1 strlen 函数:
- 1.2 strcpy 函数:
- 1.3 strcat 函数:
- 1.4 strcmp 函数:
- 1.5 strncpy 函数:
- 1.6 strncat 函数:
- 1.7 strncmp 函数:
- 1.8 strstr 函数:
- 1.9 strtok 函数:
- 1.10 strerror 函数:
- 2.内存函数介绍
- 2.1 memcpy 函数:
- 2.2 memmove 函数:
- 2.3 memcmp 函数:
- 3.模拟库函数的实现
- 3.1 模拟实现strlen 函数:
- 3.2 模拟实现strcpy 函数:
- 3.3 模拟实现strcat 函数:
- 3.4 模拟实现strcmp 函数:
- 3.5 模拟实现strstr 函数:
- 3.6 模拟实现memcpy 函数:
- 3.7 模拟实现memmove 函数:
- 4.总结:
1.字符串处理函数介绍
我们在C语言的从程序代码编写中,对字符和字符串的处理相当频繁,但是C语言本身并没有字符串类型。而字符串通常放在【常量字符串】或者【字符数组】中。其中,字符串常量适用于那些对它不做修改的字符串函数。
1.1 strlen 函数:
strlen 函数(string length)的作用是计算返回字符串中结束标识符 ’ \0 ’ 之前出现的的字符个数,因此,strlen函数所处理的字符串必须是以结束标识符 ’ \0 ’ 结尾的字符串。其·返回值类型为size_t 类型
,该类型为无符号类型
。
strlen 函数的基本使用方式:
#include<stdio.h>
#include<string.h>int main()
{//两中可行的初始化方式:char arr1[]="Hello!";char arr2[] = { 'H','e','l','l','o','!','\0' };int ret1 = strlen(arr1);int ret2 = strlen(arr2);printf("The length of arr1 is %d\n", ret1);printf("The length of arr2 is %d\n", ret2);return 0;
}
我们使用字符型数组将字符串储存起来,接着使用 strlen 函数计算字符串 " Hello! " 中所有字符的数量使用双引号初始化字符串时,编译器将会自动在最后添加上结束标识符,并可以使用一个整型变量接收 strlen 函数的返回值,进行打印。
并且我们还说到,strlen 函数的返回类型为无符号数,因此 strlen 函数不可以直接
用来比较两个字符串的大小
。例如:
#include<stdio.h>
#include<string.h>
int main()
{const char* str1 = "abcdef";const char* str2 = "bbb";
if (strlen(str2) - strlen(str1) > 0){printf("str2 > str1\n");}else{printf("srt1 < str2\n");}return 0;
}
我们可能会认为计算出的结果为 3 - 6 为 -3,结果会执行 else 语句。但事实上,因为 strlen 函数的返回类型为无符号类型,得出的结果也是无符号类型,于是原本第一位即符号位上的数字也将被作为数据的一部分,因而实际得出的结果为一个非常大的整数,执行了 if 语句。
于是如果我们想要使用 strlen 函数来判断字符串的大小,可以通过让它的返回值由无符号数变为有符号数即可,即强制类型转换
来实现:
#include<stdio.h>
#include<string.h>
int main()
{const char* str1 = "abcdef";const char* str2 = "bbb";if ((int)strlen(str2) - (int)strlen(str1) > 0){printf("str2 > str1\n");}else{printf("srt1 < str2\n");}return 0;
}
1.2 strcpy 函数:
在之前的学习中,strcpy 函数也是我们经常使用的字符串处理函数之一。
strcpy 函数(string copy)的作用是,可以将字符串从源地址复制至目的地址并且它会将源地址内的结束标识符 ’ \0 ’ 一并拷贝过去,因此源地址必须以 ’ \0 ’ 结尾,且目的地址也将以结束标识符结尾,通俗来讲就是用来实现字符串的复制和拷贝。并且,因为其作用为拷贝字符串,因此目标地址内的空间必须足够大
,要有足够的空间容纳下源地址内的字符串,同时目的地址的空间必须是可变、可修改的。
strcpy 函数的基本使用方式:
#include<stdio.h>
#include<string.h>
int main()
{const char arr1[] = "Hellow!";const char arr2[10] = { 0 };printf("Before copy , the char inside arr1 are %s\n", arr1);printf("Before copy , the char inside arr2 are %s\n", arr2);printf("\n");strcpy(arr2, arr1);//strcpy函数使用格式为:strcpy(目的地址, 源地址)printf("After copy , the char inside arr1 are %s\n", arr1);printf("After copy , the char inside arr2 are %s\n", arr2);return 0;
}
我们要特别注意的是 strcpy 函数返回的是目标空间的起始地址,该函数设置返回值值类型的目的是为了实现链式访问
1.3 strcat 函数:
strcat 函数(string catenate)的作用是,将源地址的字符串追加补充至目的地址处。与字符串拷贝函数相同,它在进行补充追加时是从目的地址的结束标识符处 ’ \0 ’ 开始追加的,追加至源地址的结束标识符处停止。且它同样要求目标地址内的空间必须足够大,要有足够的空间容纳下源地址内的字符串
,同时目的地址的空间必须是可变、可修改的。
strcat 函数的基本使用方式:
#include<stdio.h>
#include<string.h>
int main()
{const char arr1[20] = "Hello!";const char arr2[20] = "Welcome!";printf("Before catenate , the char inside arr1 are %s\n", arr1);printf("Before catenate , the char inside arr2 are %s\n", arr2);printf("\n");strcat(arr2, arr1);printf("After catenate , the char inside arr1 are %s\n", arr1);printf("After catenate , the char inside arr2 are %s\n", arr2);return 0;
}
注意
strcat 函数无法追加自己。原因很好理解,我们在定义时就已经固定了字符数组的储存空间了,当追加自己时,相当于将自己与和与自己相同大小的字符数组,即两倍自身大小的数据放入自己的储存空间中,可想而知一定是不可行的。
1.4 strcmp 函数:
strcmp 函数(string compare)的作用为按照顺序依次比较两字符串对应位置字符的 ASCII 码值(注意不是比较两字符串的长度),直到结束标识符 ’ \0 ’ 或对应位置的字符不同。若比较至结束标识符都没有不同则两字符串相等,若两字符串对应位置字符有不同,对应位置上字符 ASCII 码值小的字符串小于另一个字符串。
我们来看 strcmp 函数的基本使用方式:
#include<stdio.h>
#include<string.h>
int main()
{const char arr1[] = "abcd";const char arr2[] = "abz";int ret = strcmp(arr1, arr2);//若arr1大于arr2,则返回大于零的数//若arr1等于arr2,则返回等于零的数//若arr1小于arr2,则返回小于零的数if (ret > 0){printf("arr1 > arr2\n");}else if (ret = 0){printf("arr1 = arr2\n");}else{printf("arr1 < arr2\n");}return 0;
}
定义并初始化两个字符数组,接着对两个数组进行比较,根据 strcmp 函数的比较结果得到返回值,再根据返回值反馈我们想要的字符串比较结果。
注意
并不是比较字符串的长短,而是比较对应位置字符 ASCII 码值的大小
。
1.5 strncpy 函数:
strncpy函数(string number copy)的作用为将指定长度的字符串复制到字符数组中,即表示把源地址中字符串开始的前n个字符拷贝到目的地址中。与 strcpy 相同,它同样会将源地址内的结束标识符 ’ \0 ’ 一并拷贝过去,因此源地址必须以 ’ \0 ’ 结尾,且目的地址也将以结束标识符结尾。并且,因为其作用为拷贝字符串,因此目标地址内的空间必须足够大
,要有足够的空间容纳下源地址内的字符串,同时目的地址的空间必须是可变、可修改的。
strncpy 函数的基本使用方式:
#include<stdio.h>
#include<string.h>//strncpy函数的使用:
int main()
{const char arr1[] = "Hellow!Welcome!";const char arr2[10] = { 0 };printf("Before copy , the char inside arr1 are %s\n", arr1);printf("Before copy , the char inside arr2 are %s\n", arr2);strncpy(arr2, arr1, 7);printf("After catenate , the char inside arr1 are %s\n", arr1);printf("After catenate , the char inside arr2 are %s\n", arr2);return 0;
}
我们看到,使用该函数,我们成功的将字符数组 arr1 中的前七个字符拷贝到了字符数组 arr2 中。
注意
:若源字符串的长度小于我们传递过去的参数,则拷贝完源字符串之后,将会在目标后追加字符 ’ 0 ',直到拷贝至参数规定个数。
1.6 strncat 函数:
strncat 函数(string num catenate)的作用为从源地址处将指定长度的字符串追加补充到目的地址中与 strcat 函数类似,它在进行补充追加时也是从目的地址的结束标识符处 ’ \0 ’ 开始追加的,不同的是追加至参数限制的字符数处停止
。但它同样要求目标地址内的空间必须足够大,要有足够的空间容纳下出家补充的字符串 ,同时目的地址的空间必须是可变、可修改的。
strncat 函数的基本使用方式:
#include<stdio.h>
#include<string.h>int main()
{const char arr1[] = "Hellow!Welcome!";const char arr2[20] = "Welcome!";printf("Before copy , the char inside arr1 are %s\n", arr1);printf("Before copy , the char inside arr2 are %s\n", arr2);strncat(arr2, arr1, 7);printf("After catenate , the char inside arr1 are %s\n", arr1);printf("After catenate , the char inside arr2 are %s\n", arr2);return 0;
}
我们可以看到,首先定义并初始化两个字符数组,接着打印追加补充之前它们各自空间内的内容进行确认,然后我们使用了 strncat 函数有限制的从数组 arr1 向 arr2 中追加补充了七个字符。
通过限制长度我们就可以实现字符数组对自己的追加补充
了。
1.7 strncmp 函数:
strncmp 函数(string number compare)的作用为有限制的按照顺序依次比较两字符串对应位置字符的 ASCII 码值(注意不是比较两字符串的长度),直到参数限制位数位置上全部比较结束或对应位置的字符不同。若参数限制位数位置上的字符都比较结束且都没有不同则两字符串相等,若两字符串对应位置字符有不同,对应位置上字符 ASCII 码值小的字符串小于另一个字符串。
strncmp 函数的基本使用方式:
#include<stdio.h>
#include<string.h>
int main()
{const char arr1[] = "abcd";const char arr2[] = "abz";int ret = strncmp(arr1, arr2, 3);//比较前 3 个字符//若arr1大于arr2,则返回大于零的数//若arr1等于arr2,则返回等于零的数//若arr1小于arr2,则返回小于零的数if (ret > 0){printf("arr1 > arr2\n");}else if (ret = 0){printf("arr1 = arr2\n");}else{printf("arr1 < arr2\n");}return 0;
}
其作用原理与作用过程,与 strncmp 函数十分类似,唯一不同便是限制了字符比较的位数
注意
该函数的作用也不是比较字符串的长短,而是比较对应位置字符 ASCII 码值的大小
1.8 strstr 函数:
strstr 函数(string string)的作用为从一个字符串中寻找其字串,通俗来讲就是从一个字符串中寻找另一个字符串。若找到目标字串则返回指向目标字串的指针,若没有找到目标字串则返回空指针(不返回)
strstr 函数的基本使用方式:
#include<stdio.h>
#include<string.h>
int main()
{const char arr1[] = "abcdefg";const char arr2[] = "cde";char* ret = strstr(arr1, arr2);//从ar1中寻找arr2if (ret == NULL){printf("找不到该字符串!\n");}else{printf("成功找到该字符串'%s'!\n", ret);}return 0;
}
我们定义并初始化了两个字符数组,并使用 strstr 函数进行字串寻找处理,并使用字符型指针接收汉函数的返回值,最后,对接收了返回值的指针 ret 进行判断,若为空则确定为没有从数组 arr1 中找到字串 arr2,否则便通过返回值,使用指针对字串地址内的数据进行访问。
该函数数有返回值,支持进行函数的链式访问
1.9 strtok 函数:
strtok 函数(string token)的作用为将字符串分解为一组字符串。该函数有两个数组作为参数,它的实际作用便是将其中一个数组为分割数组,在另一个数组中寻找这些“分割符”,并在分割符处将这个数组内的字符串加上结束标识符 ’ \0 ’ ,将其分割成一组(多个)字符串。若第一个参数不为 NULL ,将找到字符数组中的第一个标记并保存它在字符串中的位置;若第一个参数为 NULL ,将在同一个字符串中被保存的位置开始,查找下一个标记
strtok 函数的基本使用方式:
#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "1254594572@QQ.COM";char arr2[30] = { 0 };strcpy(arr2, arr1);const char* arr3 = "@.";printf("账号:%s\n", strtok(arr2, arr3));//找到第一个标记停止printf("服务商:%s\n", strtok(NULL, arr3));//从保存好的位置开始往后找printf("网址后缀:%s\n", strtok(NULL, arr3));//从保存好的位置开始往后找return 0;
}
1.strtok 函数是会对数组本身进行操作的,所以我们为了保护原始数据,在定义并初始化好字符数组之后,又定义了一个新的数组并将原始数据拷贝过去,作为临时拷贝供我们进行操作。
2.接着我们定义并初始化了分割符数组,函数将根据分割符数组 arr3 对 临时拷贝 arr2 进行分割。第一次执行函数时之前没有标记,于是直接进行操作找到第一个标记并分割打印。
3.此时就已经存在标记了,再连续两次找到前一次执行作下的标记按照分割符将数组分割完毕并打印。
我们可以将上面段代码优化为:
#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "1254594572@QQ.COM";char arr2[30] = { 0 };strcpy(arr2, arr1);const char* arr3 = "@.";char* str = NULL;for (str = strtok(arr2, arr3); str != NULL; str = strtok(NULL, arr3)){printf("%s\n", str);}return 0;
}
注意
:若字符串中不存在更多的标记,则返回 NULL 指针。
1.10 strerror 函数:
strerror函数(string error)的作用为返回错误码对应的信息。即根据接收到的错误码(错误码 errno 为全局变量),返回错误原因的详细信息
。
我们来看 strerror 函数的基本使用方式:
#include<stdio.h>
#include<string.h>
int main()
{int i = 0;for (i = 0; i <= 4; i++){printf("错误原因为:%s\n", strerror(i));}return 0;
}
2.内存函数介绍
2.1 memcpy 函数:
memcpy函数(memory copy)的作用为从源内存空间向目的内存空间拷贝限制数量(单位是字节)的数据。它与 strcpy 函数类似,作用均为拷贝数据,不同的是 strcpy 仅仅只操作字符串故会在结束标识符 ’ \0 ’ 处停止,而 memcpy 函数操作的是内存,内存中的数据是相邻的,故不会在结束标识符处停止
。
memcpy 函数的基本使用方式:
#include<stdio.h>
#include<string.h>
int main()
{int arr1[10] = { 1,2,3,4,5,6,7,8,9 };int arr2[5] = { 0 };printf("Before copy , the char inside arr2 are :>");int i = 0;for (i = 0; i < 5; i++){printf(" %d", arr2[i]);}printf("\n");memcpy(arr2, arr1, 20);printf("After copy , the char inside arr2 are :>");for (i = 0; i < 5; i++){printf(" %d", arr2[i]);}printf("\n");return 0;
}
首先创建并初始化了两个整形数组,接着打印出数组 arr2 内数据的存放情况。通过内存拷贝函数,将数组 arr1 内的前20个字节的数据拷贝给了 arr2数组。整型数组内每个数据元素所占的内存空间为4个字节,20个字节即将数组 arr1 中的前五个数据元素拷贝给了数组 arr2。最后再次打印数组 arr2 中的数据,验证拷贝结果。
注意
:如果源内存空间和目标内存空间有任何的重叠,复制的结果都是未定义的。
2.2 memmove 函数:
memmove函数(memory move)的作用为弥补 memcpy 函数的不足,主要用于处理内存的重叠部分。即如果源空间和目标空间出现重叠
,就得使用 memmove 函数来进行处理。
memcpy 函数的基本使用方式:
#include<stdio.h>
#include<string.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9 };printf("Before copy , the char inside arr are :>");int i = 0;for (i = 0; i < 5; i++){printf(" %d", arr[i]);}printf("\n");memmove(arr+2, arr, 20);printf("After copy , the char inside arr are :>");for (i = 0; i < 5; i++){printf(" %d", arr[i]);}printf("\n");return 0;
}
2.3 memcmp 函数:
memcmp 函数(memory compare)的作用与 strcmp 函数的作用类似,不过 memcmp 函数是从内存的角度以字节为单位进行处理,故 memcmp 函数同样需要第三个参数进行限制
,而不会在结束标识符 ’ \0 ’ 处停止比较。
memcmp 函数的基本使用方式:
#include<stdio.h>
#include<string.h>int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9 };int arr2[] = { 1,2,3,4,5,9,6,7,8 };int ret = memcmp(arr2, arr1, 24);if (ret > 0){printf("arr1 < arr2\n");}else if (ret == 0){printf("arr1 = arr2\n");}else{printf("arr1 > arr2");}return 0;
}
3.模拟库函数的实现
3.1 模拟实现strlen 函数:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>size_t my_strlen(const char* p)
{int count = 0;assert(p != NULL);//进行空指针校验,防止出现空指针while (*p != '\0'){count++;p++;}return count;
}int main()
{const char arr[] = "Welcome!";int ret = my_strlen(arr);printf("The length of arr is %d\n", ret);return 0;
}
3.2 模拟实现strcpy 函数:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>char* my_strcpy(char* p2, const char* p1)
{assert(p2 != NULL);assert(p1 != NULL);//等价于assert(p2 && p1);char* ret = p2;//将目的地址作为返回值while (*p2++ = *p1++){;}//返回目的地址的作用是为了实现链式访问return ret;
}int main()
{const char arr1[] = "Welcome!";const char arr2[10] = { 0 };printf("Before copy , the char inside arr1 are %s\n", arr1);printf("Before copy , the char inside arr2 are %s\n", arr2);printf("\n");my_strcpy(arr2, arr1);printf("After copy , the char inside arr1 are %s\n", arr1);printf("After copy , the char inside arr2 are %s\n", arr2);return 0;
}
3.3 模拟实现strcat 函数:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>char* my_strcat(char* p2, char* p1)
{assert(p2 && p1);char* ret = p2;//查找目标空间中的结束标识符,找到后停止while (*p2){p2++;}//从结束标识符开始追加//追加:while (*p2++ = *p1++){;}return ret;
}
int main()
{const char arr1[20] = "Hellow!";const char arr2[20] = "Welcome!";printf("Before catenate , the char inside arr1 are %s\n", arr1);printf("Before catenate , the char inside arr2 are %s\n", arr2);printf("\n");my_strcat(arr2, arr1);printf("After catenate , the char inside arr1 are %s\n", arr1);printf("After catenate , the char inside arr2 are %s\n", arr2);return 0;
}
3.4 模拟实现strcmp 函数:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
//模拟实现strcmp函数:
int my_strcmp(const char* p1, const char* p2)
{assert(p1 && p2);//找到字符不同的对应位:while (*p1 == *p2){if (*p1 == '\0'){return 0;}p1++;p2++;}if (*p1 > *p2){return 1;}else{return -1;}
}int main()
{const char arr1[] = "abcd";const char arr2[] = "abz";int ret = my_strcmp(arr1, arr2);//若arr1大于arr2,则返回大于零的数//若arr1等于arr2,则返回等于零的数//若arr1小于arr2,则返回小于零的数if (ret = 1){printf("arr1 > arr2\n");}else if (ret = 0){printf("arr1 = arr2\n");}else{printf("arr1 < arr2\n");}return 0;
}
3.5 模拟实现strstr 函数:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>char* my_strstr(char* p1, char* p2)
{assert(p1 && p2); char* pp1 = p1;char* pp2 = p2;char* cur = p1;while (*cur){pp1 = cur;pp2 = p2;while (*pp1 && *pp2 && (*pp1 == *pp2)){pp1++;pp2++;}if (*pp2 == '\0'){return cur;}cur++;}return NULL;
}//模拟strstr函数的实现:
int main()
{const char arr1[] = "abcdefg";const char arr2[] = "cde";char* ret = my_strstr(arr1, arr2);//从arr1中寻找arr2if (ret == NULL){printf("找不到该字符串!\n");}else{printf("成功找到该字符串'%s'!\n", ret);}return 0;
}
3.6 模拟实现memcpy 函数:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>void* my_memcpy(void* p2, const void* p1, size_t count)
{assert(p2 && p1);void* ret = p2; while (count--){*(char*)p2 = *(char*)p1;p2 = (char*)p2 + 1;p1 = (char*)p1 + 1;}return ret;
}int main()
{int arr1[10] = { 1,2,3,4,5,6,7,8,9 };int arr2[5] = { 0 };printf("Before copy , the char inside arr2 are :>");int i = 0;for (i = 0; i < 5; i++){printf(" %d", arr2[i]);}printf("\n");my_memcpy(arr2, arr1, 20);printf("After copy , the char inside arr2 are :>");for (i = 0; i < 5; i++){printf(" %d", arr2[i]);}printf("\n");return 0;
}
3.7 模拟实现memmove 函数:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>void* my_memmove(void* p2, const void* p1, size_t count)
{assert(p2 && p1);void* ret = p2;if (p2 < p1){while (count--){*(char*)p2 = *(char*)p1;p2 = (char*)p2 + 1;p1 = (char*)p1 + 1;}}else{while (count--){*((char*)p2 + count) = *((char*)p1 + count);}}return ret;
}int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9 };printf("Before copy , the char inside arr are :>");int i = 0;for (i = 0; i < 5; i++){printf(" %d", arr[i]);}printf("\n");my_memmove(arr + 2, arr, 20);printf("After copy , the char inside arr are :>");for (i = 0; i < 5; i++){printf(" %d", arr[i]);}printf("\n");return 0;
}
4.总结:
今天我们对字符串函数与内存函数的相关知识又有了新的了解,学习了10个常用字符串操作函数以及3个常用内存函数的相关知识,完成了其中部分库函数的自定义模拟实现,希望我的文章和讲解能对大家的学习提供一些帮助。
当然,本文仍有许多不足之处,欢迎各位小伙伴们随时私信交流、批评指正!我们下期见~
相关文章:

【C语言进阶】字符串函数与内存函数的学习与模拟实现
📝个人主页:Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:C语言进阶 🎯长路漫漫浩浩,万事皆有期待 文章目录1.字符串处理函数介…...

【JavaEE初阶】第一节.多线程(进阶篇 ) 常见的锁策略、CAS及它的ABA问题
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、常见的锁策略 1.1 乐观锁 vs 悲观锁 1.2 普通的互斥锁 vs 读写锁 1.3 重量级锁 vs 轻量级锁 1.4 自旋锁 vs 挂起等待锁 1.5 公平…...

Linux基础命令-pstree树状显示进程信息
Linux基础命令-uname显示系统内核信息 Linux基础命令-lsof查看进程打开的文件 Linux基础命令-uptime查看系统负载 文章目录 前言 一 命令介绍 二 语法及参数 2.1 使用man查看命令语法 2.2 常用参数 三 参考实例 3.1 以树状图的形式显示所有进程 3.2 以树状图显示进程号…...

keepalived+LVS配置详解
keepalivedLVS配置详解keepalived简介keepalived的应用场景keepalived工作原理VRRP协议核心组件分层工作工作状态LVS简介LVS三种模式NAT模式(网络地址映射)IPTUN模式(IP隧道)DR模式(直接路由)三种模式对比keepalivedLVS配置1.master配置2. keepalived配置文件3 修改keepalived配…...
Unity之C#端使用protobuf
什么是protobuf protobuf全称Protocol Buffers,由Google推出的一种平台、语言无关的数据交互格式,目前使用最广泛的一种数据格式,尤其在网络传输过程中,有很强的安全性,而且数据量比json和xml要小很多。 最主要的是pr…...

C++设计模式(18)——模板方法模式
亦称: Template Method 意图 模板方法模式是一种行为设计模式, 它在超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。 问题 假如你正在开发一款分析公司文档的数据挖掘程序。 用户需要向程序输入各种格式…...
SQLserver 索引碎片
Oracle 不需要整理碎片,原因? 1. rowid 默认的索引是B-树索引。索引建立在表中的一个或多个列或者是表的表达式上,将列值和行编号一起存储。行编号是唯一标记表中行的伪列。 行编号是物理表中的行数据的内部地址&am…...

【Storm】【二】安装
1 准备 1.1 准备linux服务器 本文搭建的是3节点的集群,需要3台linux服务器,我这里使用的是centos7版本的linux虚拟机,虚拟机网络配置如下: 主节点: master 192.168.92.90 从节点: slave1 192.168.92.…...
Android ConditionVariable
Android ConditionVariable 线程操作经常用到wait和notify,用起来稍显繁琐,而Android给我们封装好了一个ConditionVariable类,用于线程同步。提供了三个方法block()、open()、close()。 void block() //阻塞当前线程,直到条件为…...

Action Segmentation数据集介绍——Breakfast
文章目录简介细节Cooking actibitiesillustration of the actions论文讲解Breakfast(The Breakfast Action Dataset)简介 早餐动作数据集包括与早餐准备相关的10个动作,由18个不同厨房的52个不同的人执行。该数据集是最大的完全带注释的数据…...

横道图时间标尺在P6软件中的设置
卷首语 由于其直观简洁且易于管理的特性,使其成为展示项目活动顺序及时间安排的最常用的进度管理工具。 甘特图 甘特图(Gantt Chart),又称为横道图或棒条图,是最早的项目进度管理工具之一。由于其直观简洁且易于管理…...

空间复杂度(超详解+例题)
全文目录引言空间复杂度例题test1test2(冒泡排序)test3(求阶乘)test4(斐波那契数列)总结引言 在上一篇文章中,我们提到判断一个算法的好坏的标准是时间复杂度与空间复杂度。 时间复杂度的作用…...

Document-Level event Extraction via human-like reading process 论文解读
Document-Level event Extraction via human-like reading process 论文:2202.03092v1.pdf (arxiv.org) 代码:无 期刊/会议:ICASSP 2022 摘要 文档级事件抽取(DEE)特别困难,因为它提出了两个挑战:论元分散和多事件。第一个挑战…...

H5盲盒抽奖系统源码
盲盒抽奖系统4.0,带推广二维码防洪炮灰功能和教程。 支持微信无限回调登录 标价就是源码价格,vuetp5框架编写,H5网页,前后端分离 此源码为正规开发,正版产品已申请软著。 开源无加密无授权,可以二开使用…...

低代码平台和无代码平台哪个更适合开发企业管理系统?
编者按:本文分析了开发企业管理系统所需要的平台特性,并根据这些特点和低代码无代码的优劣比较,得出低代码平台更适合开发企业管理系统。关键词:私有化部署,可视化设计,源码交付,数据集成&#…...

75岁彪马再发NFT 复活美洲狮IP
在“运动品牌Web3”的潮流里,彪马(PUMA)绝对算是发烧友级别。2月22日,这家德国服装品牌的新NFT又来了,总量10000个Super PUMA NFT中,将有4000个以0.15 ETH(约为255美元)价格正式公售…...

大学生成人插画培训机构盘点
成人插画培训机构哪个好,成人学插画如何选培训班?给大家梳理了国内较好的插画培训机构排名,各有优势和特色,供大家参考! 一:国内成人插画培训机构排名 1、轻微课(五颗星) 主打课程有…...

【算法基础】一维差分 + 二维差分
👦个人主页:Weraphael ✍🏻作者简介:目前正在学习c和算法 ✈️专栏:【C/C】算法 🐋 希望大家多多支持,咱一起进步!😁 如果文章有啥瑕疵 希望大佬指点一二 如果文章对你有…...
游戏服务器框架 技能buff篇
游戏服务器框架 技能buff篇 1.状态 state 全局API 用于定义各种状态检查 bool IsDead(){ // 死亡buff if (buff->id 10001){ return true; } return false; } bool IsInvincible(){ if (buff->id 20001 || buff->id 20002){…...

网友说socket通信讲的不彻底,原来这才是Socket
关于对 Socket 的认识,大致分为下面几个主题,Socket 是什么,Socket 是如何创建的,Socket 是如何连接并收发数据的,Socket 套接字的删除等。 Socket 是什么以及创建过程 一个数据包经由应用程序产生,进入到…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...

【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...