【部分C语言函数解析】

本文详细介绍了C语言中的字符串函数(strlen,strcpy,strcmp,strcat,strstr,strncpy,strncat)以及内存函数(memcpy,memmove),包括它们的原型、功能和使用示例,帮助读者理解和掌握这些基本操作。
摘要由CSDN通过智能技术生成

大家可以在下面这个网站上学习C以及C++,里面内容很多,很详细。
https://legacy.cplusplus.com/

使用下面的函数需要包含一个头文件string.h

一、字符串函数

以下是一些比较常用的函数,都是对字符串进行操作的,除了要运用它们,还可以模拟实现一下,加深理解和记忆。

1.strlen

这个函数可以计算一个字符串的长度,但不包含’\0’。

函数原型如图
在这里插入图片描述
函数参数是一个char*类型的指针,返回值是一个整数。
在这里插入图片描述
可以计算整个串的长度,也可以计算子串的长度,只要参数合理即可。
下面是模拟实现的代码。

size_t my_strlen(const char* str)
{
    assert(str);  //保证参数的有效性
    const char* begin = str;
    while (*str)  //字符不为‘0’时,进入循环
    {
        str++;
    }
    return (str-begin);  //两个指针相减,得到的数是两个指针间的元素的个数
}

方法不止一种,也可以选择创建一个计数的变量来计数。
由于字符个数最少是0,所以返回值的类型为size_t,在函数内部不希望修改字符串的内容,所以函数参数类型为const char*

2.strcpy

这个函数可以复制一个字符串。
函数原型如下

该函数有两个参数,source指针指向的是源字符串,destination指针指向的是目标字符串,将源字符串的内容拷贝到目标字符串。如果待拷贝的字符串太长,则会出现错误,这就要求目标字符串的空间必须足够大。返回的是目标字符串的地址。

#include<stdio.h>
#include<string.h>
int main()
{
    char ch[] = "4568215";
    char ch1[5] = "0";
    printf("%s\n",strcpy(ch1, ch));  //错误,因为ch1空间太小
    printf("%s",strcpy(ch1, ch+3));  //正确,因为只需要复制ch后四个字符
    return 0;
}

在复制时,会将源字符串的\0一起复制过去。

下面是模拟实现的代码

char* my_strcpy(char* DesStr, const char* SourceStr)
{
    assert(DesStr && SourceStr);   //保证两个指针的有效性
    int length = my_strlen(SourceStr);  //计算源字符串的长度
    for (int i = 0; i <= length; ++i)   //由于字符0不能进行解引用操作,这里选择   
                                        //循环赋值
    {
        DesStr[i] = SourceStr[i]; 
    }
    return DesStr;
}

由于字符串会包含一个隐藏的\0,所以实际长度是length+1个

在这里插入图片描述
字符串“123456789”可以看成这样的一个数组,赋值时,需要进行length+1次,这样将最后的\0也复制进去了,并且没有改变指针的指向,直接返回目标字符串的地址即可。

3.strcmp

这个函数可以比较两个字符串的大小,如果两个字符串的长度相同且对应字符一一相同,则两个字符串相同。在比较时,比较的不是两个串的长度,而是对应字符的ASCII码值。

函数原型如下
在这里插入图片描述
函数参数是待比较的两个字符串的地址,返回值是一个整数,如果str1指向的串更大,则返回大于0的数字,如果str2指向的串更大,则返回小于0的数字,如果两个串一样大,就返回0。

#include<stdio.h>
#include<string.h>
int main()
{
    char ch[] = "ABCD";
    char ch1[] = "ABCD";
    char ch2[] = "aBCD";
    char ch3[] = "ABC";
    printf("%d\n", strcmp(ch, ch3));   //打印1   因为D的ASCII码值大,\0的ASCII码值是0
    printf("%d\n", strcmp(ch, ch1));   //打印0   因为两个串完全相同
    printf("%d", strcmp(ch, ch2));     //打印-1  因为a的ASCII码值大
    return 0;
}

模拟实现代码如下

int my_strcmp(const char* ch1, const char* ch2)
{
    assert(ch1 && ch2);  //保证两个指针的有效性
    while (*ch1 == *ch2) //字符相同则进入循环
    {
        if (*ch1 == '\0')//两个字符相等,,而且走完了,说明两个串完全相同,返回0
            return 0;    //否则指针向后走一步,比较下一个字符
        ch1++;
        ch2++;
    }
    if (*ch1 > *ch2)     //字符不相等,比较字符的ASCII值
        return 1;
    return -1;
}

4.strcat

这个函数可以追加一个字符串到另一个字符串末尾。
函数原型如下
在这里插入图片描述
同样,函数参数是两个指针,返回值为目标字符串的地址。
由于需要添加字符到目标字符串末尾,则目标字符串需要有足够的空间。

#include<stdio.h>
#include<string.h>
int main()
{
    char ch[] = "123456";
    char ch1[20] = "ABCDEF";
    printf("%s\n", strcat(ch1, ch));//打印ABCDEF123456
    printf("%s\n", strcat(ch1 + 4, ch + 3));//打印EF123456456
    return 0;
}

追加字符串时,从目标字符串的\0位置处开始追加。

模拟实现代码如下

char* my_strcat(char* des, const char* source)
{
    assert(des && source);  //保证两个指针的有效性
    char* ret = des;        //创建一个指针保存目标字符串地址
    while (*des)            //寻找目标字符串的\0
    {
        des++;
    }
    while (*source)        //进行追加
    {
        *des = *source;    
        des++;
        source++;
    }
    return ret;
}

5.strstr

函数原型如下
在这里插入图片描述
函数参数是两个指针,功能为在str1指向的字符串找str2指向的串,如果不存在这样的串,返回NULL,如果存在,则返回第一次出现这个串的地址。

对于函数功能下图可以帮助理解下。
在这里插入图片描述

#include<stdio.h>
#include<string.h>
int main()
{
    char ch[15] = "123456789789";
    printf("%s\n", strstr(ch, "123"));//打印123456789
    printf("%s\n", strstr(ch, "135"));//找不到字串,打印null
    printf("%s\n", strstr(ch, "89")); //打印89789
    return 0;
}

模拟实现strstr函数,代码如下

char* my_strstr(const char* ch1, const char* ch2)
{
    char* ch = (char*)ch1; 
    char* s1, * s2;  
    if (!*ch2)              //如果ch2是空串,直接返回ch1
    {
        return (char*)ch1;
    }
    int len1 = strlen(ch1);   //ch1指向的字符串的长度
    int len2 = strlen(ch2);   //ch2指向的字符串的长度
    while (len1 >= len2)    //串ch1余下的长度大于等于ch2的长度,说明余下的部分可
    {                       //能存在该字串
        s1 = ch; 
        s2 = (char*)ch2;
        while (!(*s1 - *s2)) //两个字符相等,则进行迭代
        {
            s1++;
            s2++;
        }
        if (!*s2)      //待查找的字串已经走完了,直接返回ch
        {
            return ch;
        }
        ch++;    //代码走到这里时,说明从ch位置开始,找不到字串,则ch往后走一步
                 //继续找
        len1--;  //ch1的长度减1
    }
    return NULL;
}

如果余下的ch1的长度比待查找的字串的长度小,则不可能出现该字串,直接返回NULL

6.strncpy

函数原型如下
在这里插入图片描述
这个函数可以从source位置处拷贝num个字符到des位置处,如果num太大,则将source拷贝完后,需要额外拷贝\0,直到num个字符。

返回值是des位置处的地址。另外需要注意,两个指针指向的空间不能有重叠的部分,拷贝含有重叠部分时,会用到memmove函数。

#include<stdio.h>
#include<string.h>
int main()
{
    char ch[20] = "1234";
    char ch1[10] = "ABC";
    printf("%s\n", strncpy(ch, ch1, 2));    //打印AB34
    printf("%s\n", strncpy(ch, ch1+2, 10)); //打印C
    printf("%s\n", strncpy(ch+3, ch1, 2));  //打印AB
    return 0;
}

这个函数和strcpy函数相似,只是拷贝的字符数收到了限制

模拟实现代码如下

char* my_strncpy(char* des, const char* source, size_t num)
{
    assert(des && source);    //保证指针有效性
    char* cur = des;          
    while (num--)             //拷贝num次
    {
        if (*source != '\0')  //拷贝source指向的字符串
        {
            *cur = *source;
        }
        else                  //source指向的字符串拷贝完了,需要额外补充\0
        {
            *cur = '\0';
        }
        cur++;
        source++;
    }
    return des;         //返回起始地址
}

函数参数和返回值都和库函数保持一致。

7.strncat

这个函数可以追加num个字符到目标字符串末尾

函数原型如下

在这里插入图片描述
同strcat一样,这里就不举例了,直接模拟实现一下
代码如下

char* my_strncat(char* des, const char* source, size_t num)
{
    assert(des && source);    //保证指针有效性
    char* cur = des;
    while (*cur)          //寻找\0
    {
        cur++;
    }
    while (num)         //开始迭代追加
    {
        if (*source)    //source指向的串没有追加完
        {
        *cur = *source;
        cur++;
        source++;
        }
        else          //追加完之后,需要追加一个\0
        {
            *cur = '\0';
            break;   //跳出循环
        }
        num--;
    }
    return des;
}

如果source指向的字符长度小于num,则追加完后,需要添加一个\0

二、内存函数

1.memcpy

函数原型如下
在这里插入图片描述

这个函数可以从source位置处拷贝num个字节到des位置处,函数参数和返回值类型都为void*,说明这个函数可以拷贝任意类型的数据。

比如整型数组,结构体类型…

如下代码

#include<stdio.h>
#include<string.h>
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr1[10] = { 0 };
    char ch[10] = "ABCDEF";
    char ch1[10] = "0";
    memcpy(arr1, arr, sizeof(int) * 10);
    memcpy(ch1, ch, 10);
    for (int i = 0; i < 10; i++)
        printf("%d ", arr[i]);
    printf("\n%s", ch1);
    return 0;
}

打印结果分别是1,2,3,4…10和ABCDEF
函数的返回值为des指向的地址,在拷贝时,仍需要注意内存重叠的问题。

模拟实现代码如下

void* my_memcpy(void* des, void* source, size_t num)
{
    void* ret = des;
    assert(des && source);      //保证指针有效性
    for (size_t i = 0; i < num; i++)  //num次
    {
        *(char*)des = *(char*)source; //将指针强制转换为char*类型,在进行复制
        ((char*)des)++;               //一次拷贝一个字节,指针一次走一个字节的长度
        ((char*)source)++;
    }
    return ret;
}

由于不知道需要拷贝的数据的类型,但是直到需要拷贝的字节数,所以可以选择一个字节一个字节的拷贝。

2.memmove

函数原型如下
在这里插入图片描述
这个函数的功能也是拷贝,但是允许源空间和目标空间重叠

比如下面这段代码

int main()
{
    int arr[10] = { 1,5,4,2,3,5,6,9,8,744 };
    char arr1[20] = "ABCDEFG1234567";
    memmove(arr+2, arr, 5*sizeof(int));
    memmove(arr1 + 2, arr1, 7);
    for(int i=0;i<10;i++)
    printf("%d ", arr[i]);
    //打印结果1,5,1,5,4,2,3,9,8,744
    printf("\n%s", arr1);
    //打印结果为ABABCDEFG34567
    return 0;
}

第一个memmove将arr中的1,5,4,2,3拷贝给4,2,3,5,6,存在内存重叠。
第二个memmove将arr1中的ABCDEFG拷贝给CDEFG12,也存在内存重叠。
但是结果都没有错误。

这个函数的返回值是目标空间的起始地址。

由于存在内存重叠,这里需要判断从后往前拷贝或者从前往后拷贝。
在这里插入图片描述
由于数组在内存中存储是连续的,随着下标的增加,地址由低到高,所以根据图中结果,如果存在内存重叠且源数据的地址小于目标数据的地址,应该从后往前拷贝。

下面是模拟实现的代码。

void* my_memmove(void* des,const void* source,size_t num)
{
    void* ret = des;
    assert(des && source);    //保证指针有效性
    if (des < source)         //源数据地址大,选择正向拷贝
    {
        while (num--)
        {
           *(char*)des = *(char*)source;
           ((char*)des)++;
           ((char*)source)++;
        }
    }
    else //源数据地址小与或者等于目标地址,逆向拷贝
    {
        while (num--)     
        {
            *((char*)des + num) = *((char*)source + num);
             //从最后一个字节开始拷贝,随着num--,实现从后往前拷贝。
        }
    }
    return ret;
}

由于不知道拷贝的数据的类型,但是知道总字节数,根据指针的加法操作,找到末尾数据进行拷贝。

关于部分字符串函数和内存函数的介绍,到这里就结束了。大家可以在网站上进行更深入的学习。
https://legacy.cplusplus.com/

如果有不足或错误的地方,欢迎指出,如果有什么不懂的地方,也欢迎提问,拜拜!

  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值