[c语言]认识string字符函数+内存函数 | 模拟实现|strlen strcpy strcat strcmp strstr strtok memcpy memmove memset memcmp

字符函数+内存函数的介绍

重点

  • 求字符串长度

    • strlen
  • 长度不受限制的字符串函数

    • strcpy,strcat,strcmp
  • 长度受限制的字符串函数介绍

    • strncpy,strncat,strncmp
  • 字符串查找

    • strstr,strtok
  • 错误信息报告

    • strerror
  • 内存操作函数

    • memcpy,memmove,memset,memcmp

函数介绍

strlen返回字符串str的长度

size_t strlen ( const char * str );
  • 字符串的长度由终止空字符'\0'决定:字符串的长度等于字符串开头和'\0'之间的字符数==(不包括'\0'本身)==。
  • 不可与sizeof函数混淆,如:char mystr[100]="test string";sizeof(mystr)计算为100,strlen(mystr)计算为11。
  • 注意:str指向的字符串必须以'\0'结束,返回类型size_t无符号整型
#include <stdio.h>
#include <string.h>
int main()
{
	char str1[] = "abcdef";
	char str2[] = "abc";
	if (strlen(str2) - strlen(str1) > 0)
		printf("str1<str2\n");
	else
		printf("str1>str2\n");
	return 0;
}

结果为str1<str2,与预期不同,因为无符号整型相减结果非负。

strcpy复制字符串

char * strcpy ( char * destination, const char * source );
  • 将source指向的字符串复制到destination指向的数组中,包括'\0'(并在该点停止)。
  • 为避免溢出,destination指向的数组的大小应足够长,以包含与source相同的字符串(包括'\0'),并且在内存中不应与source重叠
  • 返回destination

strcat连接字符串

char * strcat ( char * destination, const char * source );
  • 将source字符串的副本追加到destination字符串。destination中=='\0'被source的第一个字符覆盖==,'\0'包含在由destination中的两个字符串连接而成的新字符串的末尾
  • destination和source不得重叠
  • 返回destination
  • 注意:destination字符串必须以'\0'结尾。

strcmp比较两个字符串

int strcmp ( const char * str1, const char * str2 );
  • 此函数一开始比较每个字符串的第一个字符。如果它们彼此相等,则继续比较下一对,直到字符不同或到达'\0'
  • 返回值:
返回值含义
<0第一个不匹配的字符在ptr1中的值小于ptr2中的值
0两个字符串的内容相等
>0第一个不匹配的字符在ptr1中的值大于ptr2中的值

(vs中返回值分别为-1,0,1)

strncpystrncatstrncmp

这些带n的函数的参数部分分别在以上对应函数的参数后面多一个size_t num

  • strncpy:
    • 拷贝num个字符从源字符串到目标空间。
      如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
  • strncat:
    • 将源字符串中最开始的num个字符加上'\0'追加到目标字符串。
      如果源字符串的长度小于num,则仅复制'\0'之前的内容。
  • strncmp:
    • 比较字符串str1和字符串str2的至多num个字符。
      此函数一开始比较每个字符串的第一个字符。如果它们彼此相等,则继续比较下一对,直到字符不同,或到达'\0'或者num个字符全部比较完。

strstr:找出子串

const char * strstr ( const char * str1, const char * str2 );
      char * strstr (       char * str1, const char * str2 );
  • 返回str1中第一个出现str2的字符串的指针,如果str2不是str1的子串,则返回空指针。
  • 比对过程不包括'\0',但会在此停止。

例子:

int main()
{
    char str[] = "This is a simple string";
    char* pch = strstr(str, "simple");
    if (pch != NULL)
        printf("%s", pch);
    else
        printf("不是子串");
    return 0;
}

结果:simple string

strtok:拆分字符串

char * strtok ( char * str, const char * delimiters );
  • sep参数是个字符串,定义了用作分隔符的字符集合
  • str参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
  • strtok函数找到str中的下一个标记,并将其用'\0'结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
  • str参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
  • str参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
  • 如果字符串中不存在更多的标记,则返回 NULL 指针。

例子:

#include <stdio.h>
#include <string.h>
int main()
{
	const char* p = "/.";
	char arr[] = "csdn.net/CegghnnoR";
	char buf[50] = { 0 };
	strcpy(buf, arr);
	
	char* str = strtok(buf, p);
	printf("%s\n", str);
	str = strtok(NULL, p);
	printf("%s\n", str);
	str = strtok(NULL, p);
	printf("%s\n", str);
	//开始返回NULL
	str = strtok(NULL, p);
	printf("%s\n", str);
	return 0;
}

结果:csdn
net
CegghnnoR
(null)

strerror:获取错误消息

char * strerror ( int errnum );
  • c语言中规定了一些错误信息

  • errnum为错误码,返回一个指向这些错误信息字符串的指针。

    相当于将错误码翻译成错误信息。

例子:

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (NULL == pf)
	{
		//出错误的原因是什么
		printf("%s\n", strerror(errno));
		return 0;
	}
	//读文件
	//...
	
	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

结果:No such file or directory

  • errno是C语言提供的一个全局变量,可以直接使用,放在errno.h文件中的
  • 当库函数使用的时候,发生错误会把errno变量设置为本次执行库函数产生的错误码

字符 分类&转换 函数

头文件ctype.h

函数参数符合下列条件则返回真
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任何可打印字符,包括图形字符和空白字符
int tolower ( int c );//转小写
int toupper ( int c );//转大写

memcpy:复制内存块

void * memcpy ( void * destination, const void * source, size_t num );
  • 从source指向的位置开始向后直接复制num个字节的值到destination指向的内存块。
  • source指针和destination指针指向的对象的基本类型与此函数无关;结果是数据的二进制副本。
  • 该函数不检查source中是否有'\0'——它总是精确复制num个字节。
  • 为避免溢出,destination和source参数指向的数组大小应至少为num字节,且不应重叠(重叠可使用memmove)。(vs中memcpy也可复制重叠内存块)
  • 返回destination
  • strcpy专用于拷贝字符串,而memcpy可以则拷贝各种类型的数据。

例子:

#include <stdio.h>
#include <string.h>
typedef struct stu
{
	char name[10];
	int age;
}stu;

//将arr1中的数据复制到arr2
int main()
{
	stu arr1[2] = { {"zhangsan",20},{"lisi",18} };
	stu arr2[10] = { 0 };
	memcpy(arr2, arr1, sizeof(arr1));
	for (int i = 0; i < 2; i++)
	{
		printf("%s %d\n", arr1[i].name, arr1[i].age);
	}
}

memmove:移动内存块

  • 具有memcpy的功能,并且源内存块和目标内存块可以重叠

例子:

#include <stdio.h>
#include <string.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
    //将arr开始后面四个元素拷贝到arr+2开始的内存块
	memmove(arr + 2, arr, 4 * sizeof(arr[0]));
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

结果:1 2 1 2 3 4

memcmp:比较两块内存

int memcmp ( const void * ptr1, const void * ptr2, size_t num );
  • 分别对ptr1和ptr2指向的两块num字节内存进行逐字节比较。

  • 注意:与strcmp不同,函数在找到空字符后不会停止比较。

  • 返回值:

    返回值含义
    <0两个内存块中第一个不匹配的字节在ptr1中的值小于ptr2中的值
    0两个内存块的内容相同
    >0两个内存块中第一个不匹配的字节在ptr1中的值大于ptr2中的值

例子:

#include <stdio.h>
#include <string.h>
int main()
{
	char s1[] = "abcd";
	char s2[] = "abad";
	int n = memcmp(s1, s2, 4);
	if (n < 0)
		printf("s1<s2");
	else if (n == 0)
		printf("s1=s2");
	else
		printf("s1>s2");
	return 0;
}

结果:s1>s2

memset:填充内存块

void * memset ( void * ptr, int value, size_t num );
  • 将ptr指向的内存块的num个字节设置为指定值value(解释为无符号字符)。
int main()
{
	char s[10] = { 0 };
    //将前五个字符设置成'x'
	memset(s, 'x', 5);
	printf("%s", s);
	return 0;
}

结果:xxxxx

  • 也可以用于整型数组的初始化

模拟实现

strlen的模拟实现

方法1:计数器

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

方法2:递归

int my_strlen(const char* str)
{
	if (*str == '\0')
		return 0;
	else
		return 1 + my_strlen(str + 1);
}

方法3:指针

int my_strlen(char* s)
{
   char* p = s;
   while (*p != '\0')
   	p++;
   return p - s;
}

strcpy的模拟实现

char* my_strcpy(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest && src);
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

strcat的模拟实现

char* my_strcat(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest && src);
	//找到目标空间中的\0
	while (*dest)
	{
		dest++;
	}
	//追加内容到目标空间
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

strcmp的模拟实现

int my_strcmp(const char* str1, const char*str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		
		str1++;
		str2++;
	}
	return *str1 - *str2;
}

strstr的模拟实现

  • 定义三个指针变量cur,s1,s2
    • s1和s2相等则同时++
    • 不等则cur++,s1指向cur指向的元素,s2指向起始位置
    • 重复以上步骤,直到s2指向'\0'(找到),或cur指向'\0'(未找到)

strstr

char* my_strstr(const char* str, const char* substr)
{
	const char* s1 = str;
	const char* s2 = substr;
	const char* cur = str;
	//子串为\0,直接返回原字符串
	assert(str && substr);
	if (*substr == '\0')
	{
		return (char*)str;
	}
	while (*cur)
	{
		s1 = cur;
		s2 = substr;
		while (*s1 &&  *s2 && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return (char*)cur;

		cur++;
	}
	return NULL;
}

memcpy的模拟实现

void* my_memcpy(void* dest, const void*src, size_t num)
{
	void* ret = dest;
	assert(dest && src);

	while (num--) 
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}
  • 使用void*类型可以接收任意类型参数
  • 指针向后移动需要先强制转换为char*

memmove的模拟实现

要实现重叠空间拷贝,需分两种情况讨论:

memmove

  • 如将红色部分拷贝到蓝色部分,应该从前向后拷贝,3->1,4->2,5->3,6->4,结果为3 4 5 6 5 6,如从后向前拷贝,则会变成5 6 5 6 5 6,因为3和4在被拷贝之前就被5和6覆盖掉了。
  • 如将蓝色部分拷贝到红色部分,应该从后向前拷贝,结果为1 2 1 2 3 4,如从前向后拷贝,则会变成1 2 1 2 1 2 。

故当源指针大于目标指针(源内存块在目标内存块之后),则从前向后拷贝,

源指针小于目标指针(源内存块在目标内存块之前),则从后向前拷贝。

void* my_memmove(void* dest, const void* src, size_t num)
{
	void* ret = dest;
	assert(dest && src);
	
	if (dest < src)
	{
		//前->后
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		//后->前
		while (num--)
		{
			*((char*)dest+num) = *((char*)src + num);
		}
	}
	return ret;
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

世真

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值