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


    前言

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

    函数介绍及模拟

    strlen函数

    	size_t strlen ( const char * str );
    
    • 1

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    模拟实现
    	size_t strlen ( const char * str );
    
    • 1
    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    strcpy函数

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

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

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    输出:fff

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

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

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    输出:hello hello

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

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

    在这里插入图片描述
    我们可以看到,在拷贝完源字符串后又追加了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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    strcat函数

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

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    输出:abcdefabc

    模拟实现
    	char * strcat ( char * destination, const char * source );
    
    • 1
    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    strncat函数
    	char * strncat ( char * destination, const char * source, size_t num );
    
    • 1

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

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

    strcmp函数

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

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

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

    	char* m = "abcdef";
    	char* n = "abce";
    	strcmp(m, n);
    
    • 1
    • 2
    • 3

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

    #include
    #include
    int main()
    {
    	char* m = "abcdef";
    	char* n = "abce";
    	printf("%d", strcmp(m, n));
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    输出:-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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    输出:1

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

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

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

    strstr函数

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

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

    int main()
    {
    	char* m = "abcdef";
    	char* n = "cde";
    	printf("%s", strstr(m, n));
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    输出:cdef

    模拟实现
    	char * strstr ( const char *str1, const char * str2);
    
    • 1
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    在这里插入图片描述
    当第一次没找到后,第二次循环 s1 、p指向如图:
    在这里插入图片描述
    若 p 指向 ‘\0’ ,则表示找不到该字符串,退出循环。

    memcpy函数

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

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    我们来看看内存分配情况:
    在这里插入图片描述
    由图可以看出memcpy函数改变了20字节的内容,也就是5个整型,改变了5个整型数,那么它是什么方式改变这20字节的呢?
    在这里插入图片描述
    从左到右一个字节一个字节的依次复制,直到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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    memmove函数

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

    在这里插入图片描述
    假设我要把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

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    模拟实现
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    strtok函数

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

    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[] = "zhangsan@baidu.com";
    	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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    简化一下:

    int main()
    {
    	char arr[] = "zhangsan@baidu.com";
    	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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    strerror函数

    	char * strerror ( int errnum );
    
    • 1

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

    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

    字符分类函数

    在这里插入图片描述
    返回真就是返回大于0的数,可用于条件判断。

    字符转换

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

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

    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    输出:ARE YOU OK?

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

  • 相关阅读:
    【MindSpore】【SoftmaxCrossEntropyWithLogits】如何计算带有class_weight的交叉熵
    JS(javascript)面试题 7点一次过 => 必会之八股文
    扩展-Hooks
    身边的那些信审人员都去哪了?
    flutter 判断data是否是list类型,然后获取里面的值
    【凸优化学习笔记2】仿射集、凸集
    模型的保存加载、模型微调、GPU使用及Pytorch常见报错
    k8s--基础--22.1--storageclass--介绍
    猿创征文|【云原生】学习云原生经验分享
    **JavaScript解密日记5**
  • 原文地址:https://blog.csdn.net/qq_71360748/article/details/126889554