【C语言】字符串函数和内存函数

  1. 前言🌸

在我们编写C程序时,除了使用自定义函数,往往还会使用一些库函数,例如标准输入输出函数printf,scanf,字符串函数strlen,内存函数memset等等,使用这些系统自带的库函数可以轻松地帮我们实现预期的功能,大大提升我们编程的效率

1.本期我们将介绍库里面一些常见的 字符串函数以及 内存函数
2.本期的目标时在学会使用这些库函数的同时了解其内部原理,并学会 模拟实现 相应的函数
温馨提示:可以使用目录跳转到对应函数

2. 字符串函数🌊

C语言中对字符和字符串的处理很是频繁,但是 C语言本身是没有字符串类型的,字符串通常放在
常量字符串或者 字符数组中。
字符串常量适用于那些对它不做修改的字符串函数.
与字符串有关的函数放在 string.h头文件中,使用前要包含对应头文件。

2.1 strlen

2.1.1 基本使用

strlen想必我们都已经很熟悉了,作用是统计字符串的长度,函数原型及说明如下:

  • 字符串以'\0'作为结束标志,strlen返回的是字符串在'\0'前面出现字符的个数(不包含'\0')

  • 参数指向的字符串必须以'\0'结束,否则最后的值为随机值

  • strlen的返回值是size_t类型,是无符号整形,这也是最容易忽略的地方。


我们可以这样使用它:

#include<string.h>
int main()
{
    char arr1[] = "hello world";
    printf("%d", strlen(arr1));//求arr1数组中字符串的长度
}

程序输出11,说明其不会统计'\0',遇到'\0'结束统计:

2.1.2 模拟实现

法一:创建一个临时变量作为计数器
#include<stdio.h>
#include<assert.h>
int my_strlen(const char* str)
{
    assert(str);//保证传入的指针不为空
    int count = 0;//计数器
    while (*str++ != '\0')//统计字符,不统计'\0'
    {
        count++;
    }
    return count;
}
int main()
{
    char arr1[] = "hello world";
    printf("%d", my_strlen(arr1));
}
法二:使用递归的方式
#include<stdio.h>
#include<assert.h>
int my_strlen(const char* str)
{
    assert(str);//保证传入的指针不为空
    if (*str == '\0')//递归终点
    {
        return 0;
    }
    else
    {
        return 1 + my_strlen(str+1);
    }
}
int main()
{
    char arr1[] = "hello world";
    printf("%d", my_strlen(arr1));
}
法三: 采用指针-指针的方式,两个指针相减得到的结果即为中间的元素个数。
#include<stdio.h>
#include<assert.h>
int my_strlen(const char* str)
{
    assert(str);//保证传入的指针不为空
    const char* p = str;
    //将p指向字符串末尾的'\0'
    while (*++p)
    {
        ;
    }
    return p - str;//差值即为元素个数
}
int main()
{
    char arr1[] = "hello world";
    printf("%d", my_strlen(arr1));
}

三种方法运行结果都如下:

2.2 strcpy

2.2.1 基本使用

顾名思义,strcpy的作用就是拷贝字符串,它的函数原型及说明如下:

  • 源字符串必须以 '\0' 结束。

  • 对源字符串加上const修饰避免被意外修改

  • 函数会将源字符串中的 '\0' 拷贝到目标空间。

  • 目标空间必须足够大,以确保能存放源字符串,这是程序员所需要注意的。

  • 目标空间必须可变


因此我们可以这样使用它:

#include<string.h>
#include<stdio.h>
int main()
{
    char arr1[] = "hello world";
    char arr2[20] = { 0 };
    printf("%s", strcpy(arr2, arr1));//由于返回的是拷贝后数组的首元素地址,因此可以进行随机访问
}

2.2.2 模拟实现

我们可以通过指针将每个字符逐一拷贝到目标数组 ('\0'也要拷贝),代码即效果如下:
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
    assert(dest && src); //断言,保证代码健壮性
    char* ret = dest; //保存起始地址,用于返回
    //将src的字符逐个拷贝到dest中,包括'\0'
    while (*dest++ = *src++)
    {
        ;
    }
    return ret;
}
int main()
{
    char arr1[] = "hello world";
    char arr2[20] = { 0 };
    printf("%s", my_strcpy(arr2, arr1)); //返回拷贝后的数组,支持链式访问
}

2.3 strcat

2.3.1 基本使用

strcat的作用是追加字符串,将一个字符串追加到令一个字符串的末尾,函数原型及说明如下:

  • 源字符串必须以 '\0' 结束。否则我们不知道什么时候追加结束

  • 目标字符串也必须以'\0'结束,否则我们不知道什么时候开始追加

  • 目标空间必须有足够的大,能容纳下源字符串的内容。

  • 不能自己追加自己。这是由于开始追加会将目标空间末尾的'\0'覆盖,如果自己追加自己,相当于源字符串末尾的'\0'被覆盖了,从而失去了停止的标志,会陷入死循环。


因此我们可以这样使用它:

#include<stdio.h>
#include<string.h>
int main()
{
    char str1[20] = "hello ";
    char str2[] = "world";
    printf("%s", strcat(str1, str2));
    return 0;
}

2.3.2 模拟实现

我们可以先找到目标字符串的末尾,然后从此处开始将源字符串的字符拷贝到目标字符串中。代码如下:
#include<stdio.h>
#include<assert.h>
//模拟实现strcat
char* my_strcat(char* dest, const char* src)
{
    assert(dest && src);//断言,保证代码健壮性
    char* ret = dest;//保存起始地址,用于返回
    //找到dest的末尾
    while (*dest)
    {
        dest++;
    }
    //进行追加拷贝
    while (*dest++ = *src++)
    {
        ;
    }
    return ret;
}
int main()
{
    char str1[20] = "hello ";
    char str2[] = "world";
    printf("%s", my_strcat(str1, str2));
    return 0;
}

2.4 strcmp

2.4.1 基本使用

strcmp的作用是字符串比较,将两个字符串的字符逐一进行比较,其函数原型及说明如下:

  • 由于传入的两个字符串只参与比较,因此加上const修饰符防止被意外修改

  • strcmp返回值为int类型。当str1<str2,返回小于0的数;当str1>str2,返回大于0的数;当str1=str2,返回0。

  • 注意,这里的两个字符串的比较并不是比较两个字符串的长度,而是比较每个字符的ASCII码值


因此我们可以这样使用它:

#include<stdio.h>
#include<string.h>
int main()
{
    char str1[20] = "hello ";
    char str2[] = "world";
    char str3[20] = "bcdabc";
    char str4[] = "bcdabc";
    if (strcmp(str1,str2)==0)
    {
        printf("str1 == str2");
    }
    else if(strcmp(str1, str2) > 0)
    {
        printf("str1 > str2");
    }
    else
    {
        printf("str1 < str2\n");
    }
    if (strcmp(str3, str4) == 0)
    {
        printf("str3 == str4");
    }
    return 0;
}

2.4.2 模拟实现

很简单,我们只需逐一比较每个字符的ASCII码值大小,如果相同则继续比较下一个字符,直到遇到'\0'即可。实现代码如下:
#include<stdio.h>
#include<assert.h>
//模拟实现strcmp
int my_strcmp(const char* dest, const char* src)
{
    assert(dest && src);
    while (*dest == *src)//当前字符相等
    {
        if (*dest == '\0')
        {
            //全部字符都相等,两个字符串相等
            return 0;
        }
        //指向下一字符
        dest++;
        src++;
    }
    //不相等,返回ASCII码差值
    return *dest - *src;
}

2.5 strncpy

2.5.1 基本使用

strncpy的作用也是拷贝字符串。strncpy与strcpy不同的是,它是受指定长度限制的函数,即我们可以指定需要拷贝多少个字符,它的函数原型如下:

  • 与strcpy相比,参数只多了一个num,num代表要拷贝的字符数。从源字符串拷贝num个字符到目标空间

  • 如果num少于源字符的个数,其拷贝后不会在目标的后面加上'\0'。

  • 如果num超过源字符个数,则拷贝完源字符串之后,会在目标的后边追加'\0',直到总共拷贝num个。

  • 以上两点简单来说就是:strncpy我们指定拷贝多少个其就会拷贝多少个,不会凭空增加也不会凭空消失。


我们可以这样使用它:

#include<stdio.h>
#include<string.h>
int main()
{
    char str1[20] = "hello ";
    char str2[] = "world";
    char str3[20] = "hello ";
    char str4[] = "world";
    printf("%s\n", strncpy(str1, str2,6));//拷贝6个字符包括\0,因此会打印world
    printf("%s", strncpy(str3, str4, 3));//拷贝3个字符不包括\0,因此会打印worlo 

2.6 strncat

2.6.1 基本使用

strncat的作用也是追加字符串。同样的,和strcat不同的是,它是也是受指定长度限制的函数,即我们可以指定需要追加多少个字符,它的函数原型如下:

  • 与strncpy类似,num代表需要追加的字符数。从源字符串拷贝num个字符到目标空间

  • 如果num少于源字符个数,其追加后还会在目标空间的后面加上'\0'

  • 如果num大于源字符串的长度,超过的部分不会像strncpy一样用'\0'填充,而是直接停止。


我们可以这样使用它:

#include<stdio.h>
#include<string.h>
int main()
{
    char str1[20] = "hello ";
    char str2[] = "world";
    printf("%s\n", strncat(str1, str2,5));//将5个字符拷贝过去,并在后面补上'\0'
    return 0;
}

2.7 strncmp

2.7.1 基本使用

相同道理,strncmp也是受指定长度限制的函数,它的功能为字符串比较我们可以指定需要比较多少个字符,它的函数原型如下:

  • 同理,num代表需要比较的字符数。比较到出现某个字符不一样或者一个字符串结束或者num个字符全部比较完。

  • 返回值与strcmp一样,大于返回大于0的数,小于返回小于0的数,相等返回0。


我们可以这样使用它:

#include<stdio.h>
#include<string.h>
int main()
{
    char str1[20] = "hello ";
    char str2[] = "world";
    char str3[20] = "bcdabc";
    char str4[] = "bc";
    if (strncmp(str1, str2, 2) != 0)//比较前两个字符,he与wo
    {
        printf("str1和str2前两个字符不相等\n");
    }
    if (strncmp(str3, str4, 2) == 0)//比较前两个字符,bc与bc
    {
        printf("str3和str4前两个字符相等\n");
    }
    return 0;
}

2.8 strstr

2.8.1 基本使用

这个函数可能会有人感到陌生,它的作用是在一个字符串中查找是否存在另一个字符串,它的函数原型如下:

  • 本函数从str1中查找是否存在和str2匹配的子串,如果存在,则返回第一次匹配成功的字符串首地址


我们可以这样使用它:

#include<stdio.h>
#include<string.h>
int main()
{
    char str1[20] = "hello ";
    char str2[] = "el";
    char str3[20] = "bcdabc";
    char str4[] = "ad";
    printf("%s\n", strstr(str1, str2));//由于str1中存在字串与str2一致,因此返回第一次匹配成功的字符串地址
    printf("%s\n", strstr(str3, str4));//由于str3中不存在字串与str4一致,返回NULL
    return 0;
}

2.8.2 模拟实现

法一:暴力查找。通过遍历查找str1中与str2[0]相等的字符,找到后就向后进行匹配,如果匹配成功则返回对应地址,如果匹配失败则继续向后查找下一个与str2[0]相等的字符,循环直到匹配成功或者数组遍历完毕。动态效果及代码如下:
#include<stdio.h>
#include<string.h>
#include<assert.h>
//模拟实现strstr,暴力法
char* my_strstr(const char* dest, const char* src)
{
    assert(dest && src);
    char* p1 = NULL;
    char* p2 = NULL;
    char* cp = (char*)dest;
    while (*cp)
    {
        p1 = cp;
        p2 = (char*)src;
        //开始匹配
        while (*p1 && *p2 && *p1 == *p2)
        {
            p1++;
            p2++;
        }
        //全部字符匹配成功
        if (*p2 == '\0')
        {
            return cp;
        }
        //匹配失败,继续向后遍历查找
        cp++;
    }
    return NULL;//找不到返回空
}
法二:KMP算法。这是一种 字符串匹配算法,后续会专门用一篇博客写KMP算法的思想与实现,这里就直接上代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
int* get_next(const char*src)
{
    assert(src && *src);
    int sl = strlen(src);
    int* next=(int*)malloc((sl+1) * sizeof(int));
    //赋初值,第一个字符的最长公共前后缀为0
    next[1] = 0;
    next[0] = -1;
    //开始遍历,对next[j]赋值,j表示第j位字符
    int i = 0;
    int j = 2;
    while (j <= sl)
    {
        if (i == -1||src[i] == src[j-1]) 
        {
            next[j] = i + 1; //最长公共前后缀
            i++;
            j++;
        }
        else  //不相等,通过next回溯i
        {
            i = next[i];
        }
    }
    return next;
}
char* my_strstr_KMP(const char* dest, const char* src)
{
    int* next=get_next(src);
    int i = 0;
    int j = 0;
    while (dest[i])
    {
        if (j == -1 || dest[i] == src[j])
        {
            i++;
            j++;
        }
        else if (!src[j]) //遍历完毕,返回起始的地址
        {
            return &dest[i - j];
        }
        else  //通过next回溯j
        {
            j = next[j];
        }

    }
    return NULL;
}
int main()
{
    char str1[20] = "hello ";
    char str2[] = "el";
    char str3[20] = "bcdabc";
    char str4[] = "ad";
    printf("%s\n",my_strstr_KMP(str1,str2));
    printf("%s", my_strstr_KMP(str3, str4));
    return 0;
}

2.9 strtok

2.9.1 基本使用

这个函数的作用按照指定的分隔符来分隔字符串,返回分隔后的字符串,其函数原型及说明如下:

  • sep参数是个字符串,定义了用作分隔符的字符集合

  • 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中的一个或者多个分隔符分割的标记。

  • strtok函数会找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改,即不能是常量字符串

  • strtok函数的第一个参数不为 NULL 时,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。

  • strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记

  • 如果字符串中不存在更多的标记,则返回 NULL 指针。


根据strtok的特性,我们可以这样使用它:

#include<string.h>
int main()
{
    //分隔一个ip地址
    char str1[20] = "192.168.1.123";//源字符串
    char str2[20] = { 0 };
    char* del = ".";//分隔符
    strcpy(str2, str1);//由于strtok会修改源字符串,因此拷贝一份来操作
    //开始分隔,当返回NULL时结束
    for (char* s = strtok(str2, del); s != NULL; s=strtok(NULL, del))
    {
        printf("%s\n", s);
    }
    return 0;
}

2.10 strerror

2.10.1 基本使用

这个函数的作用是返回一个错误码所对应的错误信息,其函数原型与说明如下:

什么是错误码?

1.错误码是一组数字,它与系统的错误讯息建立关联,每一个错误码都对应着一个错误信息。
2.C语言在库函数调用失败时,会将错误码放在一个叫 errno的变量中,没有错误则置为0。errno变量在errno.h头文件中。

因此,我们可以使用strerror根据errno的值来获取错误信息,也可以将其打印出来,如下:

#include <stdio.h>
#include <string.h>
#include <errno.h>//必须包含的头文件
int main()
{
    FILE* pFile;
    pFile = fopen("unexist.ent", "r");//以r的方式打开一个不存在的文件
    if (pFile == NULL)
    {
        printf("%s\n", strerror(errno));//显示错误信息
    }
    return 0;
}

2.11 其他

除以上字符串函数之外,在C语言的 ctype.h 头文件中还有一些十分好用的 字符函数

2.11.1 字符分类函数

函数

如果参数符合下列条件就返回真,否则返回假

iscntrl()

任何控制字符

isspace()

空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'

isdigit()

十进制数字 0~9字符

isxdigit()

十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F

islower()

小写字母a~z

isupper()

大写字母A~Z

isalpha()

字母a~z或A~Z

isalnum()

字母或者数字,a~z,A~Z,0~9

ispunct()

标点符号,任何不属于数字或者字母的图形字符(可打印)

isgraph()

任何图形字符

isprint()

任何可打印字符,包括图形字符和空白字符

2.11.2 字符转换函数

int tolower ( int c );//将大写字母转换为小写字母

int toupper ( int c );//将小写字母转换为大写字母

2.11.3 示例

#include<stdio.h>
#include<string.h>
#include<ctype.h>
int main()
{
    //字符转换函数
    int i = 0;
    char str[] = "Hello WorLd";
    //将str的大写字符转换为小写字符
    while (str[i])
    {
        if (isupper(str[i]))//是大写字符
        {
            str[i] = tolower(str[i]);//转换为小写
        }
        i++;
    }
    printf("%s", str);
    return 0;
}

3. 内存函数🌟

以上我们介绍的函数都是用来操作字符串或者字符数组的。而当我们需要操作整形数组,操作结构体数组时,以上函数就失效了,那怎么办呢?下面就要请到我们的内存操作函数了,内存操作函数都定义在 stdlib.h头文件中。

3.1 memcpy

3.1.1 基本使用

memcpy的作用是以字节为单位拷贝内存块,其函数原型及说明如下:

  • 参数的指针用void*来接收是由于设计者并不知道用户将来会传入什么类型的数据

  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。

  • 这个函数在遇到 '\0' 的时候并不会停下来。

  • 如果source和destination有任何的重叠,复制的结果都是未定义的,这是由于内存重叠时进行拷贝可能会覆盖尚未拷贝的数据。在不同的编译器下,发生的效果可能会意想不到。如果真的需要拷贝,请使用memmove()函数


我们可以这样使用它:

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr2[10] = { 0 };
    memcpy(arr2, arr1,40);//从arr1拷贝40个字节到arr2
    //my_memmove(arr1, arr1+4, 16);
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", arr2[i]);//打印出arr2元素
    }
    return 0;
}

3.1.2 模拟实现

很简单,由于 char*类型的指针每次解引用访问一个字节,我们只需将传入的指针强转为char*指针,然后依次解引用拷贝num个字节到目标数组即可。代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
void* my_memcpy(void* dest, void* src, size_t num)
{
    assert(dest && src);//保证传入的指针不为空
    void* ret = dest;//用于返回
    while (num--)//拷贝num个字节
    {
        //利用char*指针的特性逐一进行拷贝
        *(char*)dest = *(char*)src;
        dest = (char*)dest + 1;
        src = (char*)src + 1;
    }
    return dest;
}

3.2 memmove

3.2.1 基本使用

memmove的作用是以字节为单位移动内存块的数据到目标空间。听起来与memcpy没有什么差别,但是memmove对内存块重叠的情况进行了特殊处理,使其可以正确进行拷贝。其函数原型及说明如下:

  • 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。

  • 如果源空间和目标空间出现重叠,就得使用memmove函数处理。


我们可以这样使用它:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
int main()
{
    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr2[10] = { 0 };
    memmove(arr2, arr1, 40);//内存块不重叠
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", arr2[i]);//打印出arr2元素
    }
    memmove(arr1+2, arr1, 16);//内存块重叠,将1,2,3,4移动到arr+2处
    printf("\n");
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", arr1[i]);//打印出arr1元素
    }
    return 0;
}

我们可以看到无论内存块有没有重叠,都能正确的拷贝:

3.2.2 模拟实现

分为以下三种情况:

  1. 内存块不重叠

这种情况最好办,就是一般情况,无论从前开始移动还是从后开始移动都不影响结果。

  1. 内存块重叠且dest在src后

这种情况我们就不能从前面开始进行移动了,而是要从后面开始移动,我们通过动图来解释:

  1. 内存块重叠且dest在src前

而对于这种情况,我们从后面开始移动就失效了,又需要从前面开始移动,动图如下:

  1. 总结

我们需要针对不同的情况选择从前开始移动还是从后开始移动。其实我们只要先记住先从重叠的部分开始移动即可。

实现的代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
void* my_memmove(void* dest, void* src, size_t num)
{
    assert(dest && src);//保证传入指针不为空
    void* ret = dest;//用于返回
    //dest<src,如果重叠,则重叠部分在src前面,从前开始移动
    if (dest < src)
    {
        while (num--)
        {
            *(char*)dest = *(char*)src;
            dest = (char*)dest + 1;
            src = (char*)src + 1;
        }
    }
    else//dest>=src,如果重叠,则重叠部分在src后面,从后开始移动。
    {
        while (num--)
        {
            *((char*)dest + num) = *((char*)src + num);
        }
    }
    return ret;
}

int main()
{
    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr2[10] = { 0 };
    my_memmove(arr2, arr1, 40);//内存块不重叠
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", arr2[i]);//打印出arr2元素
    }
    memmove(arr1+2, arr1, 16);//内存块重叠,将1,2,3,4移动到arr+2处
    printf("\n");
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", arr1[i]);//打印出arr1元素
    }
    return 0;
}

3.3 memcmp

3.3.1 基本使用

memcmp函数的作用是以字节为单位对内存空间中的数据进行比较,其函数原型及说明如下:

  • 其比较从ptr1和ptr2指针开始的num个字节

  • 它的返回值如下:


我们可以这么使用它:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
int main()
{
    int arr1[] = { 1,2,3,4 };
    int arr2[] = { 1,2,4,4 };
    int n;
    n = memcmp(arr1, arr2, sizeof(arr1));//比较16个字节
    if (n > 0)
    {
        printf("arr1 is greater than arr2.\n");
    }
    else if (n < 0)
    {
        printf("arr1 is less than arr2.\n");
    }
    else
    {
        printf("arr1 is the same as arr2.\n");
    }
    return 0;
}

3.4 memset

3.4.1 基本使用

memset函数的作用是以字节为单位设置内存数据,通常用于初始化。其函数原型及说明如下:

  • 我们可以使用它对一段连续的内存空间初始化,通常置为0

  • 使用时需注意是以字节为单位进行设置内存,设置出来每个字节的值是相同的,因此在设置高字节数据具有局限性,例如整形。

  • 由于一个字符只占一个字节,因此memset也可以给字符数组进行赋值。


我们可以这样使用它:

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int arr[10];
    char str[] = "abcd";
    memset(arr, 0, sizeof(arr));//将整形数组初始化为0
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
    }
    memset(str, 'x', 2);//将str前两个字符设置为x
    printf("\n%s", str);
    return 0;
}

4. 写在最后

以上我们只是将一些常见的字符串函数及内存函数进行列举分析。事实上,库里面给我们提供的库函数远远不止这些,有兴趣的小伙伴们可以加以拓展。库里面的每个函数都有它的妙用,给我们带来许多便捷。并且其内部的实现方法有些也非常巧妙,仔细研究的话或许能带来意想不到的事情哦 😋

以上,就是本期的全部内容啦🌸

制作不易,能否点个赞再走呢🙏

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MongoDB【部署 01】mongodb最新版本6.0.5安装部署配置使用及mongodb-shell1.8.0安装使用(云盘分享安装文件)

云盘分享文件&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/11sbj1QgogYHPM4udwoB1rA 提取码&#xff1a;l2wz 1.mongodb简单介绍 MongoDB的 官网 内容还是挺丰富的。 是由 C语言编写的&#xff0c;是一个基于分布式文件存储的开源数据库系统。在高负载的情况下&…

【JavaEE初阶】第八节.网络原理网络层和数据链路层,应用层

文章目录 前言 一、网络层协议 1.1 IP协议 1.2 IP地址&#xff1b; 1.3 路由选择&#xff1b; 二、数据链路层 2.1 以太网协议&#xff1b; 三、应用层&#xff1b; 3.1 应用层协议DNS&#xff1b; 3.2 DNS是如何完成转换的&#xff1b; 3.3 如何解决DNS访问量太高的…

c语言的基础知识之结构体

目录前言结构体结构的自引用typedef函数结构体内存对齐修改默认对齐数位段什么是位段位段的内存分配位段的跨平台问题位段的意义以及应用枚举枚举常量的赋值枚举的优点总结前言 欢迎来到戴佳伟的小课堂&#xff0c;那今天我们讲啥呢&#xff1f; 问得好&#xff0c;我们今天要讲…

数据库面试题——锁

了解数据库的锁吗&#xff1f; 锁是数据库系统区别于文件系统的一个关键特性&#xff0c;锁机制用于管理对共享资源的并发访问。 InnoDB下两种标准行级锁&#xff1a; 共享锁&#xff08;S Lock&#xff09;&#xff0c;允许事务读一行数据。 排他锁&#xff08;X Lock&…

图解如何一步步连接远程服务器——基于VScode

基于VScode连接远程服务器 安装Remote-SSH等插件 想要在vscode上连接远程服务器需要下载Remote-SSH系列插件&#xff1a; 直接在插件中搜索remote&#xff0c;即可找到&#xff0c;选择图片中的3个插件&#xff0c;点击install安装。 配置Remote-SSH 在这个步骤有多种操作…

和ChatGPT对比,文心一言的表现已经是中国之光了

网络上各种测评满天飞&#xff0c;这里就不展开说了&#xff0c;针对“chatgpt”这项技术的难点&#xff0c;是十分巨大的。当你对文心一言以及其他国产AI软件存在不满的时候&#xff0c;你可以简单对着chatgpt或者文心一言搜索&#xff01;ChatGPT技术难点通俗来讲难度&#x…

节流还在用JS吗?CSS也可以实现哦

函数节流是一个我们在项目开发中常用的优化手段&#xff0c;可以有效避免函数过于频繁的执行。一般函数节流用在scroll页面滚动&#xff0c;鼠标移动等。 为什么需要节流呢&#xff0c;因为触发一次事件就会执行一次事件&#xff0c;这样就形成了大量操作dom,会出现卡顿的情况…

LeetCode:35. 搜索插入位置

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340;算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 一、&#x1f331;35. 搜索插入位置 题目描述&#xff1a;给定一个排序数组和一个目标值&…

CentOS8服务篇10:FTP服务器配置与管理

一、安装与启动FTP服务器 1、安装VSFTP服务器所需要的安装包 #yum -y install vsftpd 2、查看配置文件参数 Vim /etc/vsftpd/vsftpd.conf &#xff08;1&#xff09;是否允许匿名登录 anonymous_enableYES 该行用于控制是否允许匿名用户登录。 &#xff08;2&…

年报前瞻:文化产业高质量发展确定性,关注腾讯音乐三大关键能力

港股进入年报季&#xff0c;今年的披露期拥有比往年更多的看点。 一方面&#xff0c;经济复苏态势明显&#xff0c;线上线下消费均有回暖&#xff0c;市场已经对去年的整体表现有更多预期&#xff0c;正关注企业对后续发展的思考&#xff1b;另一方面&#xff0c;两会结束&…

2023美赛C题【分析思路+代码】

以下内容为我个人的想法与实现&#xff0c;不代表任何其他人。 文章目录问题一数据预处理时间序列模型创建预测区间单词的任何属性是否影响报告的百分比&#xff1f;如果是&#xff0c;如何影响&#xff1f;如果不是&#xff0c;为什么不是&#xff1f;问题二问题三难度评估模型…

【Vue3】利用vite创建vue3项目

&#x1f3c6;今日学习目标&#xff1a;利用vite创建vue3项目 &#x1f603;创作者&#xff1a;颜颜yan_ ✨个人格言&#xff1a;生如芥子&#xff0c;心藏须弥 ⏰本期期数&#xff1a;第二期 &#x1f389;专栏系列&#xff1a;Vue3 文章目录前言vite简介利用vite创建vue3项目…

二叉搜索树

1.基础概念介绍 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 1.若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值 2.若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根节点的值 3.它…

设置Typora图床(Github)

PicGo&#xff0c;Github&#xff0c;Typora Nodejs下载&#xff1a; Node.js PicGo下载&#xff1a; GitHub - Molunerfinn/PicGo: A simple & beautiful tool for pictures uploading built by vue-cli-electron-builder 选择downloads或release. 然后进行安装。 Gith…

经典PID控制算法原理以及优化思路

文章目录0、概念1、理解2、实现3、优化4、引用0、概念 PID算法是工业应用中最广泛算法之一&#xff0c;在闭环系统的控制中&#xff0c;可自动对控制系统进行准确且迅速的校正。PID控制&#xff0c;即Proportional – Integral(I) – Derivative(D) Control, 实际上是三种反馈…

Transformer到底为何这么牛

从注意力机制&#xff08;attention&#xff09;开始&#xff0c;近两年提及最多的就是Transformer了&#xff0c;那么Transformer到底是什么机制&#xff0c;凭啥这么牛&#xff1f;各个领域都能用&#xff1f;一文带你揭开Transformer的神秘面纱。 目录 1.深度学习&#xff0…

STM32外设-DMA

1. 简介 DMA(Direct Memory Access)—直接存储器存取&#xff0c;是单片机的一个外设&#xff0c;它的主要功能是用来搬数据&#xff0c;但是不需要占用 CPU&#xff0c;即在传输数据的时候&#xff0c; CPU 可以干其他的事情&#xff0c;好像是多线程一样。数据传输支持从外设…

初时STM32单片机

目录 一、单片机基本认知 二、STM系列单片机命名规则 三、标准库与HAL库区别 四、通用输入输出端口GPIO 五、推挽输出与开漏输出 六、复位和时钟控制&#xff08;RCC&#xff09; 七、时钟控制 八、中断和事件 九、定时器介绍 一、单片机基本认知 单片机和PC电脑相比…

【python进阶】你真的懂元组吗?不仅是“不可变的列表”

&#x1f4da;引言 &#x1f64b;‍♂️作者简介&#xff1a;生鱼同学&#xff0c;大数据科学与技术专业硕士在读&#x1f468;‍&#x1f393;&#xff0c;曾获得华为杯数学建模国家二等奖&#x1f3c6;&#xff0c;MathorCup 数学建模竞赛国家二等奖&#x1f3c5;&#xff0c…

图形视图框架 事件处理(item)

在图形界面框架中的事件都是先由视图进行接收&#xff0c;然后传递给场景&#xff0c;再由场景传递给图形项。通过键盘处理的话&#xff0c;需要设置焦点&#xff0c;在QGraphicsScene中使用setFoucesItem&#xff08;&#xff09;函数可以设置焦点&#xff0c;或者图形项使用s…
最新文章