C语言中字符or字符串函数

目录

一.strlen函数(求字符串长度函数)

1.函数的介绍

2、模拟实现strlen(计数器、递归、指针方式)

2.1.计数器

2.2指针-指针

2.3递归

二、strcpy和strncpy函数(字符串拷贝函数)

 1.strcpy函数介绍(长度不受限制)

1.1模拟实现strcpy

 2.strncpy函数介绍(长度受限制) 

 2.1模拟实现strncpy

三.strcat和strncat函数(连接字符串)

 1.strcat函数介绍

 1.1模拟实现strcat

 2.strncat函数介绍

四.strcmp和strncmp(比较字符串)

1.strcmp函数介绍

1.1模拟实现strcmp

2.strncmp函数介绍

 五.strstr(字符串查找)

1.strstr函数介绍

 1.1模拟实现strstr(KMP算法)

六.strerror和perror(错误信息)

1.strerror函数介绍

2.perror函数介绍

七.memcpy,memmove和memset (内存函数)

 1.memcpy(内存拷贝函数)

 <1>memcpy拷贝数组中前5个元素

 <2>memcpy拷贝数组中中间5个元素

 <3>memcpy拷贝数组,如果num不是4的倍数该是什么样子呢?

 1.2.memcpy的模拟实现

2.memmove(解决内存重叠的拷贝函数)

  2.1模拟实现memmove

<1>.dest<>

<2>dest>=src

3.memcmp(内存比较函数)

3.1.memcmp函数介绍

3.2.memcmp的应用

 4.memset(内存设置函数)



在使用以下函数时,都需要引用头文件:#include<string.h>

一.strlen函数(求字符串长度函数)

1.函数的介绍

size_t strlen(const char *str); 

注意:

  • 字符串以'\0'作为结束标志,strlen函数返回的是在字符串中'\0'前面出现的字符个数(包括'\0')
  • 参数指向的字符串必须要以'\0'结束,不然就是随机值了
  • 注意函数的返回值为size_t,是无符号的(易错)(typedef unsigned int size_t)

strlen是求字符串长度的,求出的长度是不可能是负数的,所以返回类型设置为size_t 

 由于strlen返回类型是size_t,上面strlen(arr1)-strlen(arr2)属于size_t-size_t=size_t

len1=strlen(arr1)=3,len2=strlen(arr2)=9,len1-len2=3-9=-6,但是无符号整型,所以结果还是6,运行结果始终是大于。

总结:长度短-长度长同样>0,因为strlen是size_t类型,结果都是无符号整型



2、模拟实现strlen(计数器、递归、指针方式)

2.1.计数器

利用计数器实现很简单,循环只要不遇到'\0',就继续循环。

#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strlen(const char*str)
{
	int count = 0;
	while (*str)
	{
		str++;
		count++;
	}
	return count;
}
int main()
{
	char arr[20] = "chenhuangqing";
	int len= my_strlen(arr);
	printf("%d\n",len);
	return 0;
}

2.2指针-指针

前面介绍过,指针-指针得到的是元素之间的个数,利用这个原理,就能实现strlen

int my_strlen(const char* str)
{
	assert(str);
	const char* start =str;
	while (*str)
	{
		str++;
	}
	return str - start;
}

int main()
{
	char arr[20] = "chenhuangqing";
	int len=my_strlen(arr);
	printf("%d\n",len);
	return 0;
}

2.3递归

如果*str不为'\0'就可以return 1 + my_strlen(++str)这样每次都可以+1,直到遇到字符串的结束标志'\0',结束递归。 

int my_strlen(const char* str)
{
    assert(str != NULL);
    if (*str != '\0')
    {
        return 1 + my_strlen(str + 1);
    }
    else
    {
        return 0;
    }
}
int main()
{
	char arr[20] = "chenhuangqing";
	int len=my_strlen(arr);
	printf("%d\n",len);
	return 0;
}


二、strcpy和strncpy函数(字符串拷贝函数)

1.strcpy函数介绍(长度不受限制)

char* strcpy (char * destination, const char * source )

  • strcpy函数字符串source(包括终止字符'\0')复制到字符串destination中。复制或附加字符串时不执行溢出检查,如果源字符串和目标字符串重叠,strcpy行为是未定义的
  • .源字符串必须以 '\0' 结束。
  • 会将源字符串中的 '\0' 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。(需要将源空间arr2复制到目标空间arr1中,但是目标空间没有足够大,则编译器会报错)
  • 目标空间char arr1[3]=" ";
    源空间char arr2[10]="abdasd" 这样编译器会报错
  • 目标空间必须可变(指针和const是不可以修改的,无法满足目标空间必须可变原则)
char  *p="asba";
char arr2[]="ssa ajsj"   编译器会崩,因为*p是不可不修改的,目标空间只有可变才能从源头给目标中去。

1.1模拟实现strcpy


char* my_strcpy(char* des, const char* src)
{
	assert(des&&src);
	char* ret =des;//记录目标空间的起始地址
	while (*des++ = *src++)
	{
		;
	}
	return ret;
}

int main()
{
	char arr1[20] = "";
	char arr2[20] = "chenhuangqing";
	char*ret=my_strcpy(arr1, arr2);
	printf("arr1:%s\n", ret);
	return 0;
}

注意:strcpy的返回值是目标字符串的起始地址 

2.strncpy函数介绍(长度受限制) 

char *strncpy (char *destination,const char*source,size_t num);

  • 拷贝num个字符从源字符串到目标空间。
  • 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

 上面代码只拷了abc没有拷贝\0;

 2.1模拟实现strncpy

复制字符串的同时也得控制要小于等于size_t num,如果大于num个就直接复制为'\0'即可

len代表复制多少字符到目标空间中去,复制字符串的前提有2个(len=0和源字符串遇到'\o'结束)

如果先遇到'\0',但是len!=0,那么就将剩下的len个都设置成'\0'

char* my_strncpy(char* arr1, const char* arr2, int len)
{
    char* ret = *arr1;//记录目标数组的首地址
    assert(arr1 && arr2);
    while (len&&(*arr1++=*arr2++))//复制字符串,并考虑n小于或等于被拷贝字符串数目的情况
    {
        len--;//如果len=0了则表示已经赋值好了所需要的字符串,如果后面的等于零,表示已经复制完了<=len的字符串
    }
    if (len)//如果n大于被拷贝字符串数目,须将除被拷贝字符的其他内容用空字符进行补充
    {
        while (len)
        {
            *arr1 = '\0';
            len--;
        }
    }
    return ret;
}
int main()
{
    char arr1[20] = "xxxxxxxxxxxxxxxx";
    char arr2[] = "abcdef";
    char*ret=my_strncpy(arr1,arr2, 10);
    printf("%s\n", arr1);
    return 0;
}


三.strcat和strncat函数(连接字符串)

1.strcat函数介绍

char * strcat ( char * destination, const char * source );

  • 源字符串必须以 '\0' 结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • 字符串自己给自己追加,如何? 自己给自己追加就会使'\0'覆盖掉,所以不成立

 1.1模拟实现strcat

先到达源字符串的终点位置,然后追加,将追加的字符串连接到源字符串中去

char* my_strcat(char* des, const char* src)
{
	assert(des && src);
	char* start = des;
	//找到目标空间'\0'
	while (*des)
	{
		des++;
	}
	//追加,复制字符串
	while (*des++= *src++)
	{
		;
	}
	return start;
}
int main()
{
	char arr[20] = "hello hello ";
	char*ret=my_strcat(arr, "world");
	char* p = "world";
	printf("%s\n", arr);
	return 0;
}


2.strncat函数介绍

char * strncat ( char * destination, const char * source, size_t num );

 将追加字符串中的前4位,将目标字符串前num个字符连接到source字符串后面



四.strcmp和strncmp(比较字符串)

1.strcmp函数介绍

int strcmp ( const char * str1, const char * str2 );

  • 标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字 1

第一个字符串等于第二个字符串,则返回等于0的数字0

第一个字符串小于第二个字符串,则返回小于0的数字 -1

vs环境下 小于0是-1,等于0是0,大于0是1

1.1模拟实现strcmp

int my_strcmp(const char* arr1, const char* arr2)
{
	assert(arr1 && arr2);
	while (*arr1 == *arr2)//如果俩者相等就进循环
	{
		if (*arr1 == '\0')
		//如果其中一个是'\0'就返回0
		{
			return 0;
		}
		arr1++;
		arr2++;
	}
	return *arr1 - *arr2;//遇到不相等就可以指针相减
}
int main()
{
	char arr1[10] = "abc";
	char arr2[10] = "abc";
	int ret = my_strcmp(arr1, arr2);
	printf("%d\n", ret);
	return 0;
}

2.strncmp函数介绍

int strncmp ( const char * str1, const char * str2, size_t num );

俩个字符串常量相比较,比较到第num个字符为止就结束,哪怕num之前相等,num后不相等,都不比较了。 

 



 五.strstr(字符串查找)

1.strstr函数介绍

KMP算法

查找一个大串中是否存在一个小串字符串

char * strstr ( const char *str1, const char * str2);

  • 函数搜索字符串str2在字符串str1中的第一次出现
  • 该函数返回字符串的其余部分
  • 如果未找到所搜索的字符串,则返回false

找到了就将后面的字符都打印出来,直到遇到'\0' 

 1.1模拟实现strstr(KMP算法)

char *my_strstr(char* str1, char* str2)
{
    assert(str1 && str2);
    if (*str2 == '\0')
    {
        return (char*)str1;
    }
    const char* s1 = str1;
    const char* s2 = str2;
    const char* cp = str1;//记录str1的首地址
    while (*cp)
    {
        s1 = cp;//遇到不相等的值,cp回到s1的位置,,然后cp负责走,s1负责留在原位置  
        s2 = str2;//遇到不相等的值,str2又回到重新位置
        while (*s1!='\0'&&*s2!='\0'&& * s1 == *s2)
            //如果其中一个遇到'\0'就直接跳出循环,如果俩个相等就加加判断下一个是不是也是相等的,同时cp++
        {
            s1++;
            s2++;
        }
        if (*s2 == '\0')//如果s2遇到'\0'了就说明大串已经遍历完成了,返回的是bcdef这小串
        {
            return (char*)cp;
        }
        cp++;
    }
    return NULL;
}
int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "bc";
    char* ret = strstr(arr1, arr2);
    if (ret == NULL)
    {
        printf("找不到\n");
    }
    else
    {
        printf("%s\n",ret);
    }
    return 0;
}

六.strerror和perror(错误信息)

1.strerror函数介绍

char * strerror ( int errnum );

#include<errno.h>

返回错误码,所对应的错误信息

在c语言中的库函数运行的时候,如果发生错误,就将错误码存在一个变量中,这个变量是:error

我们要将错误码翻译成错误信息

 strerror的作用在于就是可以明确指明此物是否可以找到,会给你明确的答复,文件text.exe这个是表示没有存在此文件的。No such file or directory----没有这个文件

2.perror函数介绍

perror是直接打印错误信息,在打印错误信息前会先打印自定义信息

perror=printf+strerror的组合

strerror只是将errno错误信息传给strerror,并没有自定义信息

perror会去打印自定义函数,而且会报出错误信息

七.memcpy,memmove和memset (内存函数)

 1.memcpy(内存拷贝函数)

void * memcpy ( void * destination, const void * source, size_t num );num代表字节

strcpy只是对字符串的拷贝,并不能拷贝其他类型的字符串,memcpy可以实现

  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
  • 这个函数在遇到 '\0' 的时候并不会停下来。
  • 如果source和destination有任何的重叠,复制的结果都是未定义的。

 <1>memcpy拷贝数组中前5个元素

 <2>memcpy拷贝数组中中间5个元素

<3>memcpy拷贝数组,如果num不是4的倍数该是什么样子呢?

这应该打印出什么呢?

 17个字节,int占4个字节,4*4=16,为什么打印5个元素,因为vs是小端存储

01 00 00 00  02  00  00  00  03  00 00  00  04  00  00  00这里是16个字节

第17个字节是 05,所以17个字节是05,所以打印出就是5个元素。

1.2.memcpy的模拟实现

注意:

1.首先对于字节是char类型的,需要对其强制类型转换为char类型

2.其次对地址往后移一位是要注意的

++(char*)dest          dest=(char*)dest+1

后置++是不行的; 不能dest++因为即使你强制类型转化为char*,但是地址++,还是int*类型

实现过程:



那如果出现内存重叠该如何拷贝呢?

我们要知道拷贝是(拷贝一个,读取一个,并不是一次性拿完再放上去)

 所以接下来我们就看看memmove拷贝函数来解决这个问题

2.memmove(解决内存重叠的拷贝函数)

void * memmove ( void * destination, const void * source, size_t num );

 

  • 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
  • 如果源空间和目标空间出现重叠,就得使用memmove函数处理

 2.1模拟实现memmove

<1>.dest<src

先3给1,4给2,是从前往后走

 <2>dest>=src

先拿7给8,6给7,从后往前走

 总结:

当目标数组起始位置小于源数组起始位置,那么从前往后走、

当目标数组起始位置大于等于源数组起始位置,那么从后往前走

 

 代码如下:


void* my_memmove(char* des, const char* src, size_t  num)
{
    assert(des && src);
    void* ret = des;
    if (des < src)
    {
        //从前往后走
        while (num--)
        {
            *(char*)des = *(char*)src;
            ++(char*)des;
            ++(char*)src;
        }
        return ret;
    }
    else
    {
        //从后往前走
        while (num--)//num=20
        {
            *((char*)des + num) = *((char*)src + num);//num--后是19
        }
        return ret;
    }
}

    int main()
    {

        int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
        int len = sizeof(arr1) / sizeof(arr1[0]);
        my_memmove(arr1, arr1 + 2, 20);
        for (int i = 0; i < len; i++)
        {
            printf("%d ", arr1[i]);
        }
        return 0;
    }

总结:

当dest<src时,就用memcpy

当dest>=src时,就用memmove

第一个参数是dest,第二个参数是src,如果第一个小于第二个,就用memcpy,反之用memmove

3.memcmp(内存比较函数)

3.1.memcmp函数介绍

int memcmp ( const void * ptr1, const void * ptr2,size_t num);

  • 比较从ptr1和ptr2指针开始的num个字节
    • 第一个元素大于第二个元素,则返回大于0的数字 1
    • 第一个元素等于第二个元素,则返回0 0
    • 第一个元素小于第二个元素,则返回小于0的数字 -1

 vs环境下 小于0是-1,等于0是0,大于0是1;

3.2.memcmp的应用

 

 4.memset(内存设置函数)

 大多数情况下都是给置为0,将数组中所有元素都置为0

 将改为0,则表示改为'\0',当打印的时候,第一个遇到了'\0'就直接结束了,打印的都是空的。



答案在明天,每天的明天。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值