字符函数和字符串函数详解

目录

  • 前言
    • 函数介绍及模拟
      • strlen函数
        • 模拟实现
      • strcpy函数
        • 模拟实现
          • strncpy函数
      • strcat函数
        • 模拟实现
          • strncat函数
      • strcmp函数
        • 模拟实现
          • strncmp函数
      • strstr函数
        • 模拟实现
      • memcpy函数
        • 模拟实现
      • memmove函数
        • 模拟实现
      • strtok函数
      • strerror函数
      • 字符分类函数
      • 字符转换

前言

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

函数介绍及模拟

strlen函数

	size_t strlen ( const char * str );

1.字符串以 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中’\0’ 前面出现的字符个数(不包含 ‘\0’ )。
2.参数指向的字符串必须要以 ‘\0’ 结束。
3.注意函数的返回值为size_t,是无符号的整数

例:

#include 
#include
int main()
{
	const char*str1 = "abcdef";    //常量字符串后面默认带了个'\0'
	const char*str2 = "bbb";
	if(strlen(str2)-strlen(str1)>0)      // 6-3
	{
		printf("str2>str1\n");
	}
	else
	{
		printf("srt1>str2\n");
	}
	return 0;
}
模拟实现
	size_t strlen ( const char * str );
#include
size_t my_strlen(const char* str)
{
	const char* start = str;
	const char* end = str;
	while (*end != '\0')
	{
		end++;
	}
	return end - start;     //指针相减等于两指针间的元素个数
}

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

strcpy函数

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

注:前面的指针指向目标字符串,后面指针指向源字符串

1.源字符串必须以 ‘\0’ 结束。
2.将源指向的字符串复制到目标所指向的数组中,包括 ‘\0’ 字符(并在该点停止)。
3.目标空间必须足够大,以确保能存放源字符串。
4.目标空间必须可变。(常量字符串不能修改,目标空间不能是常量字符串)

#include
#include
int main()
{
	char arr[20] = "abcdef";
	char* m = "fff";
	strcpy(arr, m);
	printf("%s", arr);    
	return 0;
}

输出:fff

模拟实现
	char* strcpy(char * destination, const char * source )

函数返回值为目标字符串起始地址

char* my_strcpy(char* dest, const char* src)
{
	char* ret = dest;
	while (*dest++ = *src++);  //后置加加,先解引用,再赋值,再加加,直到*dest=*src='\0',退出循环
	return ret;
}

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

输出:hello hello

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

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

字符函数和字符串函数详解_第1张图片
我们可以看到,在拷贝完源字符串后又追加了3个0
模拟实现:

char* m_strncpy(char* destination, const char* source, size_t num)
{
	char* p = destination;
	int i = 1;
	while (*destination = *source)
	{
		destination++;
		source++;
		i++;
	}
	destination++;
	num -= i;
	while (num--)
	{
		*destination++ = '\0';
	}
	return p;
}
int main()
{
	char arr[20] = "abcdef";
	char* m = "abc";
	printf("%s", strncpy(arr, m, 6));
	return 0;
}

strcat函数

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

1.将源字符串的内容追加到目标字符串(包括 ‘\0’ ),目标中的 ‘\0’ 字符被源的第一个字符覆盖。
2.源字符串必须以 ‘\0’ 结束。
3.目标空间必须有足够的大,能容纳下源字符串的内容。
4.目标空间必须可修改。

例:

#include
#include
int main()
{
	char arr[20] = "abcdef";
	char* m = "abc";
	printf("%s", strcat(arr, m));     //函数返回值为目标字符串起始地址
	return 0;
}

输出:abcdefabc

模拟实现
	char * strcat ( char * destination, const char * source );
#include
char* m_strcat(char* destination, const char* source)
{
	char* p = destination;
	while (*destination++);     
	destination--;            //destination指向了\0后面的元素,所以--
	while (*destination++ = *source++);
	return p;
}

int main()
{
	char arr[20] = "abcdef";
	char* m = "abc";
	printf("%s", m_strcat(arr, m));     
	return 0;
}
strncat函数
	char * strncat ( char * destination, const char * source, size_t num );

1.将源的前 num 个字符追加到目标,外加一个终止空字符。
2.如果源中 C 字符串的长度小于 num,则仅复制到终止空字符之前的内容。

类似于前面的 strncpy 这里就不展开讲了。

strcmp函数

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

1.第一个字符串大于第二个字符串,则返回大于0的数字
2.第一个字符串等于第二个字符串,则返回0
3.第一个字符串小于第二个字符串,则返回小于0的数字

如何判断两个字符串呢?
假设有如下两个字符串:

	char* m = "abcdef";
	char* n = "abce";
	strcmp(m, n);

首先是两个字符串的第一个字符比较,很明显相等,再比较下一个字符,直到字符 ‘d’ 与 ‘e’ 比较,字符 ‘d’ 是小于 ‘e’ 的,所以m指向的字符串小于n指向的字符串。

#include
#include
int main()
{
	char* m = "abcdef";
	char* n = "abce";
	printf("%d", strcmp(m, n));
	return 0;
}

输出:-1

模拟实现
#include
int m_strcmp(const char* str1, const char* str2)
{
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
	}
	if (*str1 > *str2)
		return 1;
	if (*str1 < *str2)
		return -1;
}
int main()
{
	char* m = "abcdef";
	char* n = "abca";
	printf("%d", m_strcmp(m, n));
	return 0;
}

输出:1

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

比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。

有兴趣可以自己实现一下。

strstr函数

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

1.返回指向 str1 中第一次出现的 str2 的指针,如果 str2 不是 str1 的一部分,则返回空指针。
2.匹配过程不包括终止空字符,但它在那里停止。

int main()
{
	char* m = "abcdef";
	char* n = "cde";
	printf("%s", strstr(m, n));
	return 0;
}

输出:cdef

模拟实现
	char * strstr ( const char *str1, const char * str2);
char* m_strstr(const char* str1, const char* str2)
{
	const char* s1 = str1;       //见下图,s1 标记目标字符串每次开始查找的首元素
	const char* s2 = str2;       // s2 标记源字符串每次开始查找的首元素
	const char* p = str1;       // p 标记每次从 s1 开始遍历的元素
	if (*str2 == '\0')
	{
		return str1;
	}
	while (*p)
	{
		s1 = p;
		s2 = str2;        //每次循环开始将 s1 移到后一个元素处,s2 移到首元素处
		while (*s1 != '\0' && *s2 != '\0' && (*s1 == *s2))
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')      //当源字符串遍历完了后即找到了
		{
			return (char*)p;   
		}
		p++;
	}
	return NULL;   //找不到子串
}
int main()
{
	char* m = "abcdef";
	char* n = "cde";
	printf("%s", m_strstr(m, n));
	return 0;
}

字符函数和字符串函数详解_第2张图片
当第一次没找到后,第二次循环 s1 、p指向如图:
字符函数和字符串函数详解_第3张图片
若 p 指向 ‘\0’ ,则表示找不到该字符串,退出循环。

memcpy函数

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

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

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

我们来看看内存分配情况:
字符函数和字符串函数详解_第4张图片
由图可以看出memcpy函数改变了20字节的内容,也就是5个整型,改变了5个整型数,那么它是什么方式改变这20字节的呢?
字符函数和字符串函数详解_第5张图片
从左到右一个字节一个字节的依次复制,直到20个字节。

模拟实现
void* m_memcpy(void* destination, const void* source, size_t num)
{
	void* p = destination;
	while (num--)      //循环num次
	{
		*(char*)destination = *(char*)source;   //char型访问一个字节,方便逐个字节打印
		destination = (char*)destination + 1;
		source = (char*)source + 1;
	}
	return p;
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	m_memcpy(arr2, arr, 20);
	return 0;
}

memmove函数

不知道大家有没有想过,如果是自己复制自己,那memcpy为什么不能用呢?什么函数可以实现呢?
memcpy函数会改变目标数据,如果目标数据与源数据有重叠,可能导致
如下情况:

字符函数和字符串函数详解_第6张图片
假设我要把1 2 3 4 复制到3 4 5 6 的位置上,那么从前往后就是如图,我们可以看到原来的3 4 变为了1 2 ,然后1 2 再复制到5 6 处,原来的3 4 5 6 就变为了1 2 1 2,这与我们要的结果不同,所以不能用该函数了,那么我们应该如何才能正确打印呢?

我们发现如果从后往前复制就可以很好的避免这种情况发生,即从两数据重叠处开始复制。而memmove函数就很好的解决了这点。

我们来看看memmove函数:

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

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

#include
#include
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	memmove(arr1 + 2, arr1, 20);
	return 0;
}

字符函数和字符串函数详解_第7张图片

模拟实现
void* m_memmove(void* destination, const void* source, size_t num)
{
	void* p = destination;
	if (destination < source)
	{
		while (num--)
		{       //从前往后复制
			*(char*)destination = *(char*)source;
			destination = (char*)destination + 1;
			source = (char*)source + 1;
		}
	}
	else
	{
		while (num--)
		{       //从后往前复制
			*((char*)destination+num) = *((char*)source+num);
		}
	}
	return p;
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	m_memmove(arr1 + 2, arr1, 20);
	return 0;
}

strtok函数

	char * strtok ( char * str, const char * sep );

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

我们来具体看看:

#include
#include
int main()
{
	char arr[] = "[email protected]";
	char buf[200] = { 0 };
	strcpy(buf, arr);     //将arr的内容复制到buf中

	const char* p = "@.";
	char* str = strtok(buf, p);  //找到第第一个标记字符'@',并将其改为'\0',返回指向这个字符串的指针
	printf("%s\n", str);   //打印: zhangsan

	str = strtok(NULL, p);   //从上次标记的'\0'开始查找下一个标记符号,重复以上操作
	printf("%s\n", str);    //打印: baidu

	str = strtok(NULL, p);
	printf("%s\n", str);     //打印: com

	return 0;
}

简化一下:

int main()
{
	char arr[] = "[email protected]";
	char buf[200] = { 0 };
	strcpy(buf, arr);
	const char* p = "@.";
	char* str = NULL;

	for (str=strtok(buf, p); str!=NULL; str=strtok(NULL, p))
	{
		printf("%s\n", str);
	}

	return 0;
}

strerror函数

	char * strerror ( int errnum );

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

#include
#include
#include
int main()
{                  //errno - C语言提供的全局的错误变量
	printf("%s\n", strerror(0));
	//printf("%s\n", strerror(1));
	//printf("%s\n", strerror(2));
	//printf("%s\n", strerror(3));
	//printf("%s\n", strerror(4));
	return 0;
}

输出:在这里插入图片描述
其他的错误变量对应的错误码可以自己输出试试。

字符分类函数

字符函数和字符串函数详解_第8张图片
返回真就是返回大于0的数,可用于条件判断。

字符转换

	int tolower ( int c );   //大写转小写
	int toupper ( int c );   //小写转大写

我们来看下面一段小写转大写的代码:

#include
#include 
int main()
{
	char arr[] = "Are you ok?";
	char* p = arr;
	while (*p)
	{
		if (islower(*p))   //判断是否是小写
		{
			*p = toupper(*p);    //小写转大写
		}
		p++;
	}
	printf("%s\n", arr);
	return 0;
}

输出:ARE YOU OK?

本希望本期内容对大家能有所帮助,下期见了~

你可能感兴趣的:(记录学习,c语言,开发语言)