常用的字符函数与字符串函数

主要介绍处理字符和字符串的库函数的使用和注意事项

  • 求字符串长度
    • strlen
  • 长度不受限制的字符串函数
    • strcpy strcat strcmp
  • 长度受限制的字符串函数介绍
    • strncpy strncat strncmp
  • 字符串查找
    • strstr strtok
  • 错误信息报告
    • strerror
  • 字符操作 内存操作函数
    • memcpy memmove memset memcmp
  • …还有许多

前言

C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在
常量字符串中或者字符数组中。
字符串常量则适用于那些对它不做修改的字符串函数。

函数介绍

1.1 strlen(计算字符串长度)
size_t strlen ( const char * str );
  • 字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包 含 ‘\0’ )
  • 参数指向的字符串必须要以 ‘\0’ 结束。
  • 注意函数的返回值为size_t,是无符号的( 易错 )
  • 函数的使用,以及模拟实现
#include <stdio.h>
#include <string.h>
#include <assert.h>

/*strlen计算字符串长度*/
int my_strlen(const char* str)
{
    int count = 0;
    assert(str != NULL);    //p
    while (*str != '\0')
    {
        count++;
        str++;      //这里不用解引用,我们只是让这里的地址+1指向下一个地址的内容。       
    }
    return count;
}

int main()
{
    /*系统函数的使用*/
    // int len = strlen("abcdef");
    // printf("%d\n" ,len);

    /*自己模拟实现strlen函数*/
    int len = my_strlen("abcdef");
    printf("%d\n" ,len);

    /*strlen函数的返回类型是size_t == unsigned int*/

    //3 - 6 = -3;   但由于strlen函数的返回值是无符号整型。-3会被当成3,输出“hehe”。
    if (strlen("abc") - strlen("abcdef") > 0)
    {
        printf("hehe");
    }
    else
    {
        printf("haha");
    }
    return 0;
}
  • 一道有趣的题目

主要考察对strlen函数返回类型的理解

#include <stdio.h>
#include <string.h>

int main()
{
    /*strlen函数的返回类型是size_t == unsigned int*/
    //3 - 6 = -3;   但由于strlen函数的返回值是无符号整型。-3会被当成3,输出“hehe”。
    if (strlen("abc") - strlen("abcdef") > 0)
    {
        printf("hehe");
    }
    else
    {
        printf("haha");
    }
    return 0;
}

1.2strcpy(拷贝字符串)
定义函数:
    char *strcpy(char *dest, const char *src);
    char * strncpy(char *dest, const char *src, size_t n);
参数分析:
    dest --> 目标地址(数据拷贝到这里)
    src --> 源地址,需要拷贝的数据所在的地址
    n  --> 指定拷贝 str 的前N 字节
返回值:
    返回dest 的地址
  • 源字符串必须以 ‘\0’ 结束。 会将源字符串中的 ‘\0’ 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。
  • 函数的使用以及模拟实现
#include <stdio.h>
#include <string.h>
#include <assert.h>

char* my_strcpy(char* dest ,const char* src)
{
    assert(dest != NULL);
    assert(src != NULL);
    char* ret = dest;   //把源头的地址先存起来。

    //拷贝src指向的字符到dest指向的空间

    /*写法一*/
    while (*src != '\0')
    {
        *dest = *src;
        dest++;
        src++;
    }
    *dest = *src;   //拷贝'\0'

    /*写法二*/
    // while (*src != '\0')
    // {
    //     *dest++ = *src++;   //先进行解引用,再进行++    
    // }
    // *dest = *src;   //拷贝'\0'

    /*写法三*/
    // while (*dest++ = *src++)
    // {
    //     ;
    // }
    // return ret; //返回目的空间的起始地址
}

int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "bit";
    
    /*函数的使用*/
    // strcpy(arr1 ,arr2);  //把arr2的内容拷贝到arr1里面去。

    /*模拟实现*/
    my_strcpy(arr1 ,arr2);  //把arr2的内容拷贝到arr1里面去。
    printf("%s\n" ,arr1);
    return 0;
}

注意:其中拷贝是把’\0’也拷贝过去!!!

1.3strcat(字符串追加)
定义函数:
    char *strcat(char *dest, const char *src);
    char * strncat(char *dest, const char *src, size_t n);
参数:
    dest --> 目标地址
    src --> 源数据的地址
    n  --> 控制需要拷贝的字节数
返回值:
    返回参数 dest 的字符串起始地址
  • 源字符串必须以 ‘\0’ 结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • 函数的使用以及模拟实现
#include <stdio.h>
#include <string.h>
#include <assert.h>

char* my_strcat(char* dest ,const char* src)
{
    char* ret = dest;
    assert(dest != NULL);   //写法一
    assert(src);            //写法二

    while (*dest != '\0')
    {
        dest++;
    }

    while (*dest++ = *src++)
    {
        ;   
    }
}

int main()
{
    char arr[30] = "hello";     //在'\0'处进行追加,完了自动补上\0
    char arr1[] = "bit";

    /*函数的使用*/
    // strcat(arr ,arr1);   

    /*模拟实现*/
    my_strcat(arr ,arr1);
    printf("%s\n" ,arr);

    return 0;
}

1.4strcmp(字符串比较函数)
定义函数:
    int strcmp(const char *s1, const char *s2);
    int strncmp(const char *s1, const char *s2, size_t n);
参数分析:
    s1 --> 需要比较的字符串1
    s2 --> 需要比较的字符串2
    n  --> 需要比较的前N个字节
返回值:
    第一个字符串大于第二个字符串,则返回大于0的差值数字
    第一个字符串等于第二个字符串,则返回0
    第一个字符串小于第二个字符串,则返回小于0的差值数字
  • 函数的使用以及模拟实现
#include <stdio.h>
#include <string.h>
#include <assert.h>

int main()
{
    char* p = "abc";
    char* p1 = "qbc";
    int  ret = strcmp(p ,p1);
    // if (ret > 0)
    // {
    //     printf("p > p1\n");
    // }
    // else if(ret == 0)
    // {
    //     printf("p == p1\n");
    // }
    // else if(ret < 0)
    // {
    //     printf("p < p1\n");
    // }
    printf("%d\n" ,ret);
    return 0;
}


/*模拟实现strcmp函数*/
int my_strcmp(const char* dest ,const char* src)
{    
    assert(dest && src);    //判断两个指针不能等于空指针
    while (*dest == *src)   //判断是否相等
    {
        if (*dest == '\0')  //再判断是否是\0
        {
            return 0;   
        }

        dest++;
        src++;
    }

    //写法一:直接返回1,或者-1
    // if(*dest > *src)
    //     return 1;   //大于
    // else
    //     return -1;  //小于

    //写法二:大小不相同,则直接返回相差字符的个数
    return (*dest - *src);
}

int main()
{
    char* p = "abc";
    char* p1 = "qbc";
    int ret = my_strcmp(p ,p1);
    printf("自己实现%d\n" ,ret);
    return 0;
}

1.5strstr(字符串查找函数)

在一字符串中查找指定的字符串。

定义函数:
    char *strstr(const char *haystack, const char * needle);
参数分析:
    haystack -->  需要查找的源字符串
    needle -->  需要查找的内容
返回值:
	成功 返回needle第一次出现的地址
    失败 返回NULL 

注意:查找成功,则返回的是要查找的字符串在被查找的字符串中第一次出现的位置!!!

  • 字符串查找函数的使用
#include <stdio.h>
#include <string.h>
#include <assert.h>

int main()
{
    char* p = "abcdefabcdef";
    char* p1 = "deff";
    char* ret = strstr(p, p1);      //查找的是p1在p中第一次出现的位置

    if (ret == NULL)
    {
        printf("查找的子串不存在\n");
    }else
    {
        printf("%s\n" ,ret);
    }
    return 0;
}
  • 模拟实现strstr函数
char* my_strstr(const char* p1 ,const char* p2)
{
    //先确定两个字符串不能为空指针
    assert(p1 != NULL);
    assert(p2 != NULL);
    char* s1 = (char*)p1;
    char* s2 = (char*)p2;
    char* cur = (char*)p1; //把被查找的子串地址赋值给cur

    //2、如果要查找的子串是'\0',那么就返回地址
    if (*s2 == '\0')
    {
        return (char*)p1;
    }

    while (*cur)    //1、如果cur不是等于'\0',则可以继续查找
    {
        s1 = cur ;
        s2 = (char*)p2;

        //3、如果s1与s2所指向的值都不等于\0并且s1 == s2,那么指针偏移到下一位继续找
        while ((*s1 != '\0') && (*s2 != '\0') && (*s1 == *s2))
        {
            s1++;
            s2++;
        }

        if (*s2 == '\0')
        {
            return cur;     //找到子串,则返回之前记录的位置
        }
        cur++;
    }
    return NULL;    //找不到子串
}


int main()
{
    char* p1 = "abbbcdef";  //将要被查找的子串
    char* p2 = "bcd";          //要查好的子串
    char* ret = my_strstr(p1, p2);      //查找的是p1在p中第一次出现的位置

    if (ret == NULL)
    {
        printf("查找的子串不存在\n");
    }else
    {
        printf("%s\n" ,ret);
    }
    return 0;
}

1.6strtok(字符串分割)
定义函数:
    char * strtok ( char * str, const char * str );
参数分析:
    str --> 需要分割的字符串
    str --> 分割的标记
返回值:
    成功 返回下一个分割后的字符串指针
    如果已无从分割则返回 NULL.

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

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

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

  • strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标 记。 如果字符串中不存在更多的标记,则返回 NULL 指针。

  • 分割的操作其实原理就是在字符串中找到该字符然后把他替换成\0结束符

  • 函数的使用

#include <stdio.h>
#include <string.h>
#include <assert.h>

int main()
{
    char arr[] = "sjf@bitedu.com";
    char arr2[] = "192.168.14.225";
    char* p = "@.";     //用作分隔符的标记集合
    char arr1[50] = {0};
    
    /*strok函数会改变被操作的字符串,
    所以在使用字符串操作函数切割的字符串一般都是临时拷贝的内容,并且可以被修改.*/
    strcpy(arr1 ,arr);

    /*写法一:*/
    // char* ret = strtok(arr ,p);
    // printf("%s\n" ,ret);

    // ret = strtok(NULL ,p);  //第二次调用时,传空指针进去即可
    // printf("%s\n" ,ret);

    // ret = strtok(NULL ,p);  //第二次调用时,传空指针进去即可
    // printf("%s\n" ,ret);

    /*写法二:巧妙利用for语句来实现strtok函数的使用*/
    char* ret = NULL;
    for (ret = strtok(arr2 ,p); ret != NULL; ret = strtok(NULL ,p))
    {
        printf("%s\n" ,ret);
    }
    
    return 0;
}

1.7strerero(错误信息报告函数)

返回错误原因的描述字符串
`char* strerror ( int errnum );

errnum --> 错误号码
返回值:
	错误原因的描述(字符串)`

strerror 是通过错误号码来获得错误信息的字符串指针返回,我们可以把该字符串在保存到想要的地方。

  • 函数的使用
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <errno.h>	//包含此头文件

int main()
{
    FILE* Pf = fopen("test.txt" ,"r");  //以只读的方式打开test.txt文件
    if (Pf == NULL)
    {
        printf("%s\n" ,strerror(errno));
    }
    else 
    {
        printf("open file success\n");
    }
    
    return 0;
}

1.8memcpy(内存拷贝函数)
memccpy ( 拷贝内存内容 )
memcpy ( 拷贝内存内容 )
定义函数:
    void * memcpy (void * dest, const void *src, size_t n);
    void * memccpy(void *dest, const void * src, int c, size_t n);
 参数分析:
    dest --> 目标地址
    src --> 源地址
    n --> 需要拷贝的字计数
返回值:
    返回指向 dest 的指针.(memcmp)
    找到字符c 则返回c下一个字节的指针,否则与memcpy 一致(memccpy)
  • 函数memcpy从src的位置开始向后复制num个字节的数据到dest的内存位置。

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

  • 如果src和dest有任何的重叠,复制的结果都是未定义的。

  • memccpy : 在拷贝的过程中遇到 c 则会停止拷贝,并返回目标地址中c的下一个位置

  • 函数的使用

struct 
{
    char name[20];
    int age;
}s;

/*size_t 用做sizeof 操作符的返回值类型,同时也是很多函数的参数类型,包括malloc 和strlen。*/
void* my_memcpy(void* dest, void* src ,size_t num)    
{
    assert(dest != NULL);
    assert(src != NULL);
    void* ret = dest;
    
    while (num--)
    {
        *(char*)dest = *(char*)src;
        //++(char*)dest;    //这样会报错“表达式必须是可以修改的左值”
        dest = (char*)dest + 1;     
        src = (char*)src + 1 ;
    }
    return ret;
}

int main()
{
    char name[20] ="hello world";

    /*memcpy-内存拷贝函数的使用*/
    // memcpy(s.name ,name ,strlen(name)+1);   

    /*模拟实现一个内存拷贝函数*/
    my_memcpy(s.name ,name ,strlen(name)+1);   
    printf("%s\n" ,s.name);
    return 0;
}

1.9memmove(内存拷贝)

void* memmove ( void* destination, const void* source, size_tnum );

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

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

  • 但是源空间和目标空间没有重叠则和memcpy函数功能相同

  • 函数的使用以及模拟实现

#include <stdio.h>
#include <string.h>
#include <assert.h>

void* my_memmove(void* dest, void* src ,size_t num)    //size_t 用做sizeof 操作符的返回值类型
{
    assert(dest != NULL);
    assert(src != NULL);
    void* ret = dest;
    
    //从前向后拷贝
    // if ((dest < src) || (dest > (char*)src + num))      //写法二
    // {
    
    // }
    
    if (dest < src)     //写法一
    {
        while (num--)
        {
            *(char*)dest = *(char*)src;
            dest = (char*)dest + 1;     //++(char*)dest;    //这样会报错“表达式必须是可以修改的左值”
            src = (char*)src + 1 ;
        }
    }else //从后向前拷贝
    {
        while (num--)
        {
            *((char*)dest + num) = *((char*)src + num);
        }
    }
    return ret;
}

int main()
{
    int arr[] = {1,2,3,4,5,6,7,8,9,10};
    int i = 0;

    /*memmove库函数的使用*/
    //memmove(arr + 5, arr ,12);  

    /*模拟实现memmove函数*/
    my_memmove(arr, arr + 2, 20);      
    for (i = 0; i < 10; i++)
    {
        printf("%d" ,arr[i]);
    }
    printf("\n");

    return 0;
}

2.0memcmp(内存比较函数)

按字节比较大小

定义函数:
    int memcmp (const void *s1, const void *s2, size_t n);
参数说明:
    s1 --> 需要比较的内存区1  
    s2 --> 需要比较的内存区2 
    n  --> 需要比较的字节数
返回值:
    返回差值 , 0 表示相同非零表示有不同
  • 函数的使用
#include <stdio.h>
#include <string.h>
#include <assert.h>

int main()
{
    //01 00 00 00 02 00 00 00 03 00 00 00
    //01 00 00 00 02 00 00 00 05 00 00 00
    int arr[] = {1, 2, 3, 4, 5, 6};
    int arr1[] = {1, 2, 5, 8, 10, 12};
    
    int ret = memcmp(arr ,arr1 ,9);		//按字节比较大小
    printf("%d\n" ,ret);
    return 0;
}

总结:
以上总结的函数相对来说还算常用吧。还是需要能熟练掌握使用方法,以及它们的使用注意事项。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值