【C语言字符和字符串的库函数的使用注意事项和模拟】

【C语言字符和字符串的库函数的使用注意事项和模拟】_第1张图片


文章目录

  • 前言
  • 一、求字符串长度的strlen函数
    • 1.1 基本语法
    • 1.2 注意事项
    • 1.3 模拟实现strlen函数
      • 1.3.1 计数器方法实现
      • 1.3.2 递归方法实现
      • 1.3.3 指针相减的方法实现
    • 1.4 strlen库函数源码查看
  • ==长度不受限制的字符串函数:(二、三、四)==
  • 二、复制字符串strcpy函数
    • 2.1 基本语法
    • 2.2 注意事项
    • 2.3 模拟实现strcpy函数
    • 2.4 strcpy库函数源码查看
  • 三、追加字符串strcat函数
    • 3.1 基本语法
    • 3.2 注意事项
    • 3.3 模拟实现strcat函数
    • 3.4 strcat库函数源码查看
  • 四、字符串比较strcmp函数
    • 4.1 基本语法
    • 4.2 注意事项
    • 4.3 模拟实现strcmp函数(和特殊②结果不同)
      • 4.3.1 版本1
      • 4.3.2 版本2
    • 4.4 strcmp库函数源码查看
  • ==长度受限制的字符串函数:(五、六、七)==
  • 五、复制字符串strncpy函数
    • 5.1 基本语法
    • 5.3 注意事项
    • 5.4 模拟实现strncpy函数
    • 5.5 strncpy库函数源码查看
  • 六、追加字符串strncat函数
    • 6.1 基本语法
    • 6.2 注意事项
    • 6.3 模拟实现strncat函数(有变动,允许==源==字符串不以'\0'结束)
    • 6.4 strncat库函数源码查看
  • 七、字符串比较strncmp函数
    • 7.1 基本语法
    • 7.2 注意事项
    • 7.3 模拟实现strncmp函数(不考虑特殊情况②)
    • 7.4 strncmp库函数源码查看
  • ==字符串查找函数:(八、九)==
  • 八、字符串查找strstr函数
    • 8.1 基本语法
    • 8.2 注意事项
    • 8.3 模拟实现strstr函数
      • 8.3.1 版本1(两个指针来控制)
      • 8.3.2 版本2(三个指针来控制,前提str1和str2都完整)
      • 8.3.3 版本3(KMP算法)
  • 九、分隔字符串strtok函数
    • 9.1 基本语法
    • 9.2 注意事项
    • 9.3 使用举例
  • ==错误信息报告:(十)==
  • 十、返回错误信息strerror函数
    • 10.1 基本语法
    • 10.2 注意事项
    • 10.3 使用举例
    • 10.4 补充用法
      • 10.4.1 补充用法使用说明
      • 10.4.2 fopen函数基本语法说明
      • 10.4.3 补充用法使用举例
  • ==字符操作:(十一)==
  • 十一、字符分类函数
  • 总结


前言

本文主要介绍:

①处理字符和字符串的库函数的使用和注意事项;
②部分库函数的模拟。


一、求字符串长度的strlen函数

1.1 基本语法

【C语言字符和字符串的库函数的使用注意事项和模拟】_第2张图片

1.2 注意事项

  • 字符串以’\0’作为结束标志,strlen函数返回的是在字符串中’\0’前面出现的字符个数(不包含’\0’);
  • 参数指向的字符串必须要以’\0’结束,否则strlen函数无法找到所需计算位置的结束标志,则会在内存继续向后读取,直到遇到’\0’;
  • 注意:函数的返回值为size_t,是无符号的,例:
#include 
#include 

int main()
{
	if (strlen("abc") - strlen("abcdef") > 0) {
		printf(">");
	}
	else {
		printf("<=");
	}

	return 0;
}

我们预期:“abc”的长度明显更小,所以strlen(“abc”) - strlen(“abcdef”)<0,输出值为<,但是输出值为>,不符合预期。
请添加图片描述

这就是因为函数的返回值为size_t,是无符号的,所以误将-3识别为无符号整数,所以识别为一个非常大的正整数,输出值为>。

解决方案:(将strlen函数的返回值强制转换为int(int默认为有符号整形unsigned int))

#include 
#include 

int main()
{
	if ((int)strlen("abc") - (int)strlen("abcdef") > 0) {
		printf(">");
	}
	else {
		printf("<=");
	}

	return 0;
}

请添加图片描述

1.3 模拟实现strlen函数

1.3.1 计数器方法实现

#include 
#include 
#include 

// 方法1:(计数器方法)

int My_Strlen(const char* str) // const保护str所指内容不被改变
{
	assert(str);
	int sum = 0;
	// 相当while ('\0' != *str)
	while (*str) {
		sum++;
		str++;
	}
	return sum;
}

int main()
{
	char str[20] = {0};
	scanf("%s", str);
	int len = My_Strlen(str);
	printf("%d\n", len);

	return 0;
}

1.3.2 递归方法实现

#include 
#include 
#include 

// 方法2:(递归方法)
// 限定条件为首指针所指内容不为'\0',每次通过字符串首指针的后移一位来接近限定条件

int My_Strlen(const char* str) // const保护str所指内容不被改变
{
	assert(str);
	char* top = str;
	// 相当if ('\0' != *top)
	if (*top) {
		return 1 + My_Strlen(top + 1);
	}
	else {
		return 0;
	}
}

int main()
{
	char str[20] = {0};
	scanf("%s", str);
	int len = My_Strlen(str);
	printf("%d\n", len);

	return 0;
}

1.3.3 指针相减的方法实现

// 方法3:(指针相减的方法)

int My_Strlen(const char* str) // const保护str所指内容不被改变
{
	assert(str);
	char* end = str;
	// 相当while ('\0' != *end)
	while (*end) {
		end++;
	}
	// 最后end指向'\0'
	return end - str;

	// 或者:
	//while (*mask++) {   //方法2,这样的话指针会指向‘\0’的后面,
	//                            //画图分析即可,所以return后额外减去1
	//    ;
	//}
	//return mask - arr - 1;
}

int main()
{
	char str[20] = {0};
	scanf("%s", str);
	int len = My_Strlen(str);
	printf("%d\n", len);

	return 0;
}

1.4 strlen库函数源码查看

/***
*strlen.c - contains strlen() routine
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*       strlen returns the length of a null-terminated string,
*       not including the null byte itself.
*
*******************************************************************************/

#include 

#pragma function(strlen)

/***
*strlen - return the length of a null-terminated string
*
*Purpose:
*       Finds the length in bytes of the given string, not including
*       the final null character.
*
*Entry:
*       const char * str - string whose length is to be computed
*
*Exit:
*       length of the string "str", exclusive of the final null byte
*
*Exceptions:
*
*******************************************************************************/

size_t __cdecl strlen (
        const char * str
        )
{
        const char *eos = str;

        while( *eos++ ) ;

        return( eos - str - 1 );
}

和指针相减方法实现的模拟函数思路基本相同。


长度不受限制的字符串函数:(二、三、四)


二、复制字符串strcpy函数

2.1 基本语法

【C语言字符和字符串的库函数的使用注意事项和模拟】_第3张图片

2.2 注意事项

  • 字符串必须以’\0’结束;
  • 会将字符串中的’\0’拷贝到目标空间;
  • 目标空间必须足够大,以确保能存放源字符串;
  • 目标空间必须可变(不能为const,否则会引发异常:访问冲突);
  • 目标字符串可以已经有内容,源字符串可以放入已有内容的字符空间内。例:
#include 
#include 

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "xxxxxxxxx";
	strcpy(arr2, arr1);
	// 调试到该步骤查看监视arr2
	printf("%s\n", arr2);

	return 0;
}

调试到代码提示的位置,查看监视arr2:
【C语言字符和字符串的库函数的使用注意事项和模拟】_第4张图片

2.3 模拟实现strcpy函数

#include 
#include 
#include 

// 模拟实现strcpy函数

char* My_Strcpy(char* to, const char* from)// const保护from所指内容不被改变
{
	assert(to && from);
	// 代替to指针移动,因为最后要返回to指针
	char* mask = to;
	// 因为to字符串的长度一定是足够容纳from字符串的,
	// 所以只需判断from指针是否后移到'\0'
	// 当*from为'\0'时先赋值给*mask,然后循环条件整体为假退出
	while (*mask++ = *from++) { //相当while ('\0' != (*mask++ = *from++))
		;
	}
	return to;
}

int main()
{
	char from[] = "abcdef";
	char to[] = "xxxxxxxxx";
	My_Strcpy(to, from);
	printf("%s\n", to);

	return 0;
}

2.4 strcpy库函数源码查看

/*
**  Copy the string contained in the second argument to the
** buffer specified by the first argument.
*/
void
strcpy( char *buffer, char const *string )
{
	/*
	** Copy characters until a NUL byte is copied.
	*/
	while( (*buffer++ = *string++) != '\0' )
		;
}

和实现的模拟函数代码思路基本相同。


三、追加字符串strcat函数

3.1 基本语法

【C语言字符和字符串的库函数的使用注意事项和模拟】_第5张图片

3.2 注意事项

  • 字符串必须以’\0’结束;
  • 目标空间必须足够大,能容纳下源字符串的内容;
  • 目标空间必须可修改;
  • 目标字符串必须以’\0’结束(因为源代码根据目标空间的’\0’作为追加的位置,char str1[20] = "abcdef\0aaaaaaa"和char str1[20] = {‘a’,‘b’,‘c’,‘d’,‘e’,‘f’},都以’\0’结尾,因为空间必须保证足够大,未初始化的部分自动初始化为’\0’)
  • 字符串自己给自己追加:
#include 
#include 

int main()
{
	char from[20] = "abcdef";
	printf("%s\n", strcat(from, from));

	return 0;
}

引发异常
【C语言字符和字符串的库函数的使用注意事项和模拟】_第6张图片

3.3 模拟实现strcat函数

#include 
#include 
#include 

// 模拟实现strcat函数

char* My_Strcat(char* to, const char* from)// const保护from所指内容不被改变
{
	assert(to && from);
	// 代替to指针移动,因为最后返回to指针
	char* mask = to;
	while (*mask) {
		mask++;
	}
	// mask指针此时指向to字符串中的'\0'
	// 因为to字符串空间一定足以容纳,所以我们仅需判断from后移过程是否指向'\0'
	// 当*from为'\0'时,先赋值给*mask,然后循环条件整体为假,退出循环
	while (*mask++ = *from++) { //相当while ('\0' != (*mask++ = *from++))
		;
	}
	return to;
}

int main()
{
	char from[] = "abcdef";
	char to[20] = "xxxxxxxxx";
	char* ret = My_Strcat(to, from);
	printf("%s\n", ret);

	return 0;
}

3.4 strcat库函数源码查看

/***
*char *strcat(dst, src) - concatenate (append) one string to another
*
*Purpose:
*       Concatenates src onto the end of dest.  Assumes enough
*       space in dest.
*
*Entry:
*       char *dst - string to which "src" is to be appended
*       const char *src - string to be appended to the end of "dst"
*
*Exit:
*       The address of "dst"
*
*Exceptions:
*
*******************************************************************************/

char * __cdecl strcat (
        char * dst,
        const char * src
        )
{
        char * cp = dst;

        while( *cp )
                cp++;                   /* find end of dst */

        while((*cp++ = *src++) != '\0') ;       /* Copy src to end of dst */

        return( dst );                  /* return dst */

}

源码思路和模拟函数代码基本相同。


四、字符串比较strcmp函数

4.1 基本语法

【C语言字符和字符串的库函数的使用注意事项和模拟】_第7张图片

4.2 注意事项

  • 字符串比较的是对应位置上的字符大小,该位置相同情况下则比较下个字符,直到有大小之分,或者最后比较结束均相同,则返回0;
  • 比较字符比较的是该字符对应的ASCII码
  • 在VS下底层通过返回-1,0,1来实现返回负数,零,整数,而标准规定返回正负零,而不是-1,0,1,所以在模拟时需注意;
  • 特殊情况①说明:如果如下两个字符串相比较(前面的字符都相同,字符串长短不同):
	char str1[] = "abcdef";
	char str2[] = "abcde";

则当str1指针指向’f’时,实际str2指针指向的是字符串最后的’\0’,’\0’的ASCII码值为0,所以‘f’的ASCII一定大于0,所以最终比较结果是str1大于str2。

  • 特殊情况②说明:如果如下两个字符串相比较(前面的字符都相同,字符串长短不同):
#include 
#include 

int main()
{
	char str1[] = { 'a','b','c','d','e'};
	char str2[] = {'a','b','c','d'};
	printf("%d\n", strcmp(str1, str2));
	
	return 0;
}

请添加图片描述

4.3 模拟实现strcmp函数(和特殊②结果不同)

在VS下底层通过返回-1,0,1来实现返回负数,零,整数,而标准规定返回正负零,而不是-1,0,1,所以在模拟时需注意

4.3.1 版本1

#include 
#include 
#include 

// 模拟实现strcmp函数
int My_Strcmp(const char* str1, const char* str2) // const保护二者字符串内容都不能被改变
{
	assert(str1 && str2);
	while (*str1 == *str2) { 
		if ('\0' == *str1) {
			return 0;
		}
		str1++;
		str2++;
	}

	return *str1 - *str2;
}

int main()
{
	char str1[] = "abcde";
	char str2[] = "abcdef";
	int ret = My_Strcmp(str1, str2);
	if (ret > 0) {
		printf("str1字符串较大\n");
	}
	else if (ret < 0) {
		printf("str2字符串较大\n");
	}
	else {
		printf("二者相同\n");
	}

	return 0;
}

4.3.2 版本2

#include 
#include 
#include 

// 模拟实现strcmp函数

// 通过返回-1,0,1来实现返回负,零,正值
int My_Strcmp(const char* str1, const char* str2) // const保护二者字符串内容都不能被改变
{
  assert(str1 && str2);
	while (*str1 || *str2) { // 相当while (('\0' != *str1) || ('\0' != *str2))
		if ((*str1 == *str2) && '\0' != *str1) {
			str1++;
			str2++;
		}
		else if (*str1 > *str2) {
			return 1;
		}
		else {
			return -1;
		}
	}
	// 如果循环结束还没有退出,则说明二者完全相同
	return 0;
}

int main()
{
	char str1[] = "abcde";
	char str2[] = "abcdef";
	int ret = My_Strcmp(str1, str2);
	if (ret > 0) {
		printf("str1字符串较大\n");
	}
	else if (ret < 0) {
		printf("str2字符串较大\n");
	}
	else {
		printf("二者相同\n");
	}

	return 0;
}

4.4 strcmp库函数源码查看

/***
*strcmp.c - routine to compare two strings (for equal, less, or greater)
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*       Compares two string, determining their ordinal order.
*
*******************************************************************************/

#include 

#pragma function(strcmp)

/***
*strcmp - compare two strings, returning less than, equal to, or greater than
*
*Purpose:
*       STRCMP compares two strings and returns an integer
*       to indicate whether the first is less than the second, the two are
*       equal, or whether the first is greater than the second.
*
*       Comparison is done byte by byte on an UNSIGNED basis, which is to
*       say that Null (0) is less than any other character (1-255).
*
*Entry:
*       const char * src - string for left-hand side of comparison
*       const char * dst - string for right-hand side of comparison
*
*Exit:
*       returns -1 if src <  dst
*       returns  0 if src == dst
*       returns +1 if src >  dst
*
*Exceptions:
*
*******************************************************************************/

int __cdecl strcmp (
        const char * src,
        const char * dst
        )
{
        int ret = 0 ;

        while((ret = *(unsigned char *)src - *(unsigned char *)dst) == 0 && *dst)
                {
                ++src, ++dst;
                }

        return ((-ret) < 0) - (ret < 0); // (if positive) - (if negative) generates branchless code
}

※※※源码的思路大概为:

当两个指针所指位置的字符相同且不为’\0’时,两个指针同时后移,当不满足该循环条件(①两指针所指位置的字符不同;②两指针所指位置的字符均为’\0’;③某个指针所指字符为’\0’,此时两指针所指字符必不相同),先将不满足循环条件时的两指针内容相减的结果赋值给ret变量,然后退出循环,返回ret,此时ret就记录了最后退出循环时的两字符比较结果。


长度受限制的字符串函数:(五、六、七)


五、复制字符串strncpy函数

5.1 基本语法

【C语言字符和字符串的库函数的使用注意事项和模拟】_第8张图片

5.3 注意事项

  • 拷贝count个字符从字符串到目标空间;
  • 如果字符串的长度小于count,则拷贝完源字符串之后,在目标后追加0,直到count个
  • 如果字符串的长度大于count,则拷贝源字符串中的count个字符。
  • count不能大于目标字符串的长度(如果目标字符串是通过初始化赋值字符串来定义大小的,类似char str[] = “abc”,则count最大值为字符串长度加1(加的是最后的’\0’);如果类似char str[20] = “abc”,初始化时已规定大小,则count最大值为20,否则会溢出,总之,count最大值为sizeof(str) / sizeof(str[0]));
  • 目标空间必须可变(不能为const,否则会引发异常:访问冲突,例const char* str1 = “abcdef”);
  • 目标字符串可以已经有内容,源字符串可以放入已有内容的字符空间内;
  • 字符串不需要必须以’\0’结束。

5.4 模拟实现strncpy函数

#include 
#include 
#include 

// 模拟实现strncpy函数

char* My_Strncpy(char* str1, const char* str2, size_t count, int max1, int max2)
{
	assert(str1 && str2);
	char* mask = str1;

	// 判断count和目标空间大小关系,条件合法再执行代码
	if (count <= max1) {

		// 判断count和源字符串长度关系,两种方式来复制字符串
		if (count > max2) {
			int i = 0;
			for (i = 0; i < count; i++) {
				if (i < max2) {
					*mask++ = *str2++;
				}
				else {
					*mask++ = '\0';
				}
			}
		}
		else {
			int j = 0;
			for (j = 0; j < count; j++) {
				*mask++ = *str2++;
			}
		}
	}
	return str1;
}

int main()
{
	char str1[] = "abcdef";
	char str2[] = {'x', 'x', 'x'};
	int count = 4;
	int max1 = sizeof(str1) / sizeof(str1[0]);
	int max2 = sizeof(str2) / sizeof(str2[0]);
	char* ret = My_Strncpy(str1, str2, count, max1, max2);
	printf("%s\n", ret);
	
	return 0;
}

5.5 strncpy库函数源码查看

/***
*strncpy.c - copy at most n characters of string
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*       defines strncpy() - copy at most n characters of string
*
*******************************************************************************/

#include 

#if defined _M_ARM
    #pragma function(strncpy)
#endif

/***
*char *strncpy(dest, source, count) - copy at most n characters
*
*Purpose:
*       Copies count characters from the source string to the
*       destination.  If count is less than the length of source,
*       NO NULL CHARACTER is put onto the end of the copied string.
*       If count is greater than the length of sources, dest is padded
*       with null characters to length count.
*
*
*Entry:
*       char *dest - pointer to destination
*       char *source - source string for copy
*       unsigned count - max number of characters to copy
*
*Exit:
*       returns dest
*
*Exceptions:
*
*******************************************************************************/

char * __cdecl strncpy (
        char * dest,
        const char * source,
        size_t count
        )
{
        char *start = dest;

        while (count && (*dest++ = *source++) != '\0')    /* copy string */
                count--;

        if (count)                              /* pad out with zeroes */
                while (--count)
                        *dest++ = '\0';

        return(start);
}

源码先通过count && (*dest++ = *source++) != ‘\0’来将源字符串、目标字符串长度都小于count,且都不为’\0’的字符段完成复制,然后剩余的count如果大于0,则再目标字符串后加剩余count数量个’\0’。
但是,自己测试当count大于源字符串长度和大于目标字符串长度时有点问题。待解决


六、追加字符串strncat函数

6.1 基本语法

【C语言字符和字符串的库函数的使用注意事项和模拟】_第9张图片

6.2 注意事项

  • 目标空间必须足够大,能容纳下源字符串的内容;
  • 目标空间必须可修改;
  • 目标字符串必须以’\0’结束(因为源代码根据目标空间的’\0’作为追加的位置,char str1[20] = "abcdef\0aaaaaaa"和char str1[20] = {‘a’,‘b’,‘c’,‘d’,‘e’,‘f’},都以’\0’结尾,因为空间必须保证足够大,未初始化的部分自动初始化为’\0’)
  • 字符串必须以’\0’结束;
  • 特殊情况①:当count小于字符串长度时,复制源字符串的count个字符到目标空间,并在最后追加一个’\0’,见下;
#include 
#include 

int main()
{
	// 用字符串中间的\0模拟一个局部字符串的结尾,从而调试观察如何追加
	char str1[20] = "abcdef\0aaaaaaa";
	char str2[] = "xxxx";
	int count = 2;
	char* ret = strncat(str1, str2, count);
	
	printf("%s\n", ret);
	
	return 0;
}

调试到printf步骤:
【C语言字符和字符串的库函数的使用注意事项和模拟】_第10张图片

  • 特殊情况②:当count大于字符串长度时,复制源字符串的所有到目标空间,并在最后追加一个’\0’目标后续其他字符不变动,见下;
#include 
#include 

int main()
{
	// 用字符串中间的\0模拟一个局部字符串的结尾,从而调试观察如何追加
	char str1[20] = "abcdef\0aaaaaaa";
	char str2[] = "xxxx";
	int count = 7;
	char* ret = strncat(str1, str2, count);
	
	printf("%s\n", ret);
	
	return 0;
}

【C语言字符和字符串的库函数的使用注意事项和模拟】_第11张图片

6.3 模拟实现strncat函数(有变动,允许字符串不以’\0’结束)

#include 
#include 
#include 

// 模拟实现strncat函数

char* My_Strncat(char* str1, const char* str2, size_t count, int len2)
{
	assert(str1 && str2);
	// 代替str来移动
	char* mask = str1;
	while (*mask) {
		mask++;
	}
	// mask指向str1中的'\0'
	// 找到len2和count的较小值,将源字符串的min个字符来追加到目标空间
	int min = len2 < count ? len2 : count;
	while (min) {
		*mask++ = *str2++;
		min--;
	}
	// 如果此时*(mask - 1)不为'\0',需要追加'\0',
	// 因为当源字符串如果自带'\0',例char str[20] = "abc",追加之后的*(mask - 1)已经为'\0',
	// 而当源字符串如果没有'\0',例char str[20] = {'a','b','c'},追加之后的*(mask - 1)不为'\0',则需要*mask追加'\0'
	if (*(mask - 1)) {
		*mask = '\0';
	}
	return str1;
}

int main()
{
	// 用字符串中间的\0模拟一个局部字符串的结尾,从而调试观察如何追加
	char str1[25] = "abcdef\0aaaaaaaa";
	char str2[] = "xxxx";
	int count = 3;
	int len2 = sizeof(str2) / sizeof(str2[0]);
	char* ret = My_Strncat(str1, str2, count, len2);
	
	printf("%s\n", ret);
	
	return 0;
}

6.4 strncat库函数源码查看

/***
*strncat.c - append n chars of string to new string
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*       defines strncat() - appends n characters of string onto
*       end of other string
*
*******************************************************************************/

#include 

/***
*char *strncat(front, back, count) - append count chars of back onto front
*
*Purpose:
*       Appends at most count characters of the string back onto the
*       end of front, and ALWAYS terminates with a null character.
*       If count is greater than the length of back, the length of back
*       is used instead.  (Unlike strncpy, this routine does not pad out
*       to count characters).
*
*Entry:
*       char *front - string to append onto
*       char *back - string to append
*       unsigned count - count of max characters to append
*
*Exit:
*       returns a pointer to string appended onto (front).
*
*Uses:
*
*Exceptions:
*
*******************************************************************************/

char * __cdecl strncat (
        char * front,
        const char * back,
        size_t count
        )
{
        char *start = front;

        while (*front++)
                ;
        front--;

        while (count--)
                if ((*front++ = *back++) == 0)
                        return(start);

        *front = '\0';
        return(start);
}

源码分析:
先找到目标空间的’\0’,然后循环count次,中途如果遇到源字符串中的’\0’则说明count大于源字符长度,追加完毕,返回;
如果循环count次中途没有退出,则说明count小于源字符长度,追加count个字符,完毕后,返回。


七、字符串比较strncmp函数

7.1 基本语法

【C语言字符和字符串的库函数的使用注意事项和模拟】_第12张图片

7.2 注意事项

  • 比较到出现一个字符不一样,或者一个字符串结束,或者count个字符全部比较完;
  • 字符串比较的是对应位置上的字符大小,该位置相同情况下则比较下个字符,直到有大小之分,或者count个字符比较结束均相同,则返回0;
  • 比较字符比较的是该字符对应的ASCII码
  • 在VS下底层通过返回-1,0,1来实现返回负数,零,整数,而标准规定返回正负零,而不是-1,0,1,所以在模拟时需注意;
  • 特殊情况①说明:如果如下两个字符串相比较(前面的字符都相同,字符串长短不同):
#include 
#include 

int main()
{
	char str1[] = "abcde";
	char str2[] = "abcd";
	printf("%d\n", strncmp(str1, str2, 5));
	
	return 0;
}

请添加图片描述

  • 特殊情况②说明:如果如下两个字符串相比较(前面的字符都相同,字符串长短不同):
#include 
#include 

int main()
{
	char str1[] = { 'a','b','c','d','e'};
	char str2[] = {'a','b','c','d'};
	printf("%d\n", strncmp(str1, str2, 5));
	
	return 0;
}

请添加图片描述

7.3 模拟实现strncmp函数(不考虑特殊情况②)

#include 
#include 
#include 

// 模拟实现strncmp函数

int My_Strncmp(const char* str1, const char* str2, size_t count)
{
	// 当该位置两字符相同,且count-1大于0时,进入循环指针向后移动继续比较下一个字符
	while ((count-1) && (*str1++ == *str2++)) {
		// 因为*str1和2相同,所以二者如果都为'\0'则说明字符比较完毕,返回0
		if ('\0' == *str1) {
			return 0;
		}
		count--;
	}
	// 之前未返回,说明要么count-1不大于0,或者该位置两字符不同,返回(*str1 - *str2)即可(不考虑特殊情况②)
	return (*str1 - *str2);
}
 
int main()
{
	char str1[] = "abcde";
	char str2[] = "abcd";
	int count = 5;
	int ret = My_Strncmp(str1, str2, count);
	if (ret > 0) {
		printf("str1字符串较大\n");
	}
	else if (ret < 0) {
		printf("str2字符串较大\n");
	}
	else {
		printf("二者相同\n");
	}
	
	return 0;
}

7.4 strncmp库函数源码查看

/***
*strncmp.c - compare first n characters of two strings
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*       defines strncmp() - compare first n characters of two strings
*       for ordinal order.
*
*******************************************************************************/

#include 

#ifdef _M_ARM
    #pragma function(strncmp)
#endif

/***
*int strncmp(first, last, count) - compare first count chars of strings
*
*Purpose:
*       Compares two strings for ordinal order.  The comparison stops
*       after: (1) a difference between the strings is found, (2) the end
*       of the strings is reached, or (3) count characters have been
*       compared.
*
*Entry:
*       char *first, *last - strings to compare
*       unsigned count - maximum number of characters to compare
*
*Exit:
*       returns <0 if first < last
*       returns  0 if first == last
*       returns >0 if first > last
*
*Exceptions:
*
*******************************************************************************/

int __cdecl strncmp
(
    const char *first,
    const char *last,
    size_t      count
)
{
    size_t x = 0;

    if (!count)
    {
        return 0;
    }

    /*
     * This explicit guard needed to deal correctly with boundary
     * cases: strings shorter than 4 bytes and strings longer than
     * UINT_MAX-4 bytes .
     */
    if( count >= 4 )
    {
        /* unroll by four */
        for (; x < count-4; x+=4)
        {
            first+=4;
            last +=4;

            if (*(first-4) == 0 || *(first-4) != *(last-4))
            {
                return(*(unsigned char *)(first-4) - *(unsigned char *)(last-4));
            }

            if (*(first-3) == 0 || *(first-3) != *(last-3))
            {
                return(*(unsigned char *)(first-3) - *(unsigned char *)(last-3));
            }

            if (*(first-2) == 0 || *(first-2) != *(last-2))
            {
                return(*(unsigned char *)(first-2) - *(unsigned char *)(last-2));
            }

            if (*(first-1) == 0 || *(first-1) != *(last-1))
            {
                return(*(unsigned char *)(first-1) - *(unsigned char *)(last-1));
            }
        }
    }

    /* residual loop */
    for (; x < count; x++)
    {
        if (*first == 0 || *first != *last)
        {
            return(*(unsigned char *)first - *(unsigned char *)last);
        }
        first+=1;
        last+=1;
    }

    return 0;
}

不懂- -待补充


字符串查找函数:(八、九)


八、字符串查找strstr函数

8.1 基本语法

【C语言字符和字符串的库函数的使用注意事项和模拟】_第13张图片

8.2 注意事项

  • 无法查找空字符串,如果查找的str2是空字符串,则返回的就是str1指针;
  • 如果找到,函数返回的指针是字符串str2首次出现在字符串str1中的位置,如果没有找到,则返回NULL。
  • 查找的是连续、完整的子串(说明见下),例见下;

代码①(所查找的字符串连续且完整):

#include 
#include 

int main()
{
	char str1[] = "abcde";
	char str2[] = "bcde";
	// char str2[] = {'b','c','d','e'};
	char* ret = strstr(str1, str2);
	if (NULL == ret) {
		printf("没找到\n");
	}
	else {
		printf("%s\n", ret);
	}
	
	return 0;
}

请添加图片描述
代码②(所查找的字符串连续但不完整):

#include 
#include 

int main()
{
	char str1[] = "abcde";
	//char str2[] = "bcde";
	char str2[] = {'b','c','d','e'};
	char* ret = strstr(str1, str2);
	if (NULL == ret) {
		printf("没找到\n");
	}
	else {
		printf("%s\n", ret);
	}
	
	return 0;
}

代码③(所查找的字符串连续且完整):

#include 
#include 

int main()
{
	char str1[] = "abcde";
	//char str2[] = "e";
	char str2[] = {'b','c','d','e','\0'};
	char* ret = strstr(str1, str2);
	if (NULL == ret) {
		printf("没找到\n");
	}
	else {
		printf("%s\n", ret);
	}
	
	return 0;
}

综上:完整代表该字符串包含结尾处的’\0’。

  • str1字符串无特殊要求,如果查找成功即返回首次出现在字符串str1中的位置,结尾有无’\0’仅是打印结果不同。

8.3 模拟实现strstr函数

8.3.1 版本1(两个指针来控制)

#include 
#include 
#include 

// 模拟实现strstr函数

char* My_Strstr(const char* str1, const char* str2, int len2)
{
	assert(str1 && str2);
	if('\0' == *str2){
		return (char*)str1; // 因为str1为const char*类型,避免编译器警告
	}
	int i = 0;
	// 通过len2来控制循环上限
	for (i = 0; i < len2; i++) {
		// 如果对比二者的此位置字符内容不同且str1未执行完(*str1 != '\0'),
		// 则将str1向后移动一位继续比较,将循环变量重置为0,从新位置的首字符开始比较
		if ((*(str1 + i) != *(str2 + i)) && (*str1 != '\0')) {
			str1++;
			i = 0;
		}
	}
	// 如果执行完i
	// 如果执行完循环后,*str1 == '\0',则说明查找失败
	if (*str1 == '\0') {
		return NULL;
	}
	else {
		return (char*)str1; // 因为str1为const char*类型,避免编译器警告
	}

}

int main()
{
	char str1[] = "abcde";
	char str2[] = "cd";
	int len2 = strlen(str2);
	char* ret = My_Strstr(str1, str2, len2);
	if (NULL == ret) {
		printf("没找到\n");
	}
	else {
		printf("%s\n", ret);
	}
	
	return 0;
}
}

8.3.2 版本2(三个指针来控制,前提str1和str2都完整)

#include 
#include 
#include 

// 模拟实现strstr函数

// (前提:str1和str2必须完整)
char* My_Strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	if ('\0' == str2) {
		return (char*)str1;
	}
	// s1和s2代替str1和str2来移动
	const char* s1 = str1;
	const char* s2 = str2;
	// cur记录本轮比较时s1的首位置,因为s1查找时会移动,如果成功查找需返回该首位置
	const char* cur = str1;
	// 相当while ('\0' != *cur)
	while (*cur) {
		// 每轮循环重置s1,s2位置
		s1 = cur;
		s2 = str2;
		while ((*s1 == *s2) && '\0' != *s2) {
			s1++;
			s2++;
		}
		// 如果上述循环执行完毕后将s2所有字符都进行比较,和s1对应相同,则此时s2应指向字符串最后的'\0'
		if ('\0' == *s2) {
			return cur;
		}
		// 每轮循环执行完毕后如果没有返回,则cur后移一位
		cur++;
	}
	// 在上述循环内未返回,则说明查找失败,返回NULL
	return NULL;
}

int main()
{
	char str1[] = "abcde";
	char str2[] = "cd";
	char* ret = My_Strstr(str1, str2);
	if (NULL == ret) {
		printf("没找到\n");
	}
	else {
		printf("%s\n", ret);
	}
	
	return 0;
}

8.3.3 版本3(KMP算法)

待补充


九、分隔字符串strtok函数

9.1 基本语法

【C语言字符和字符串的库函数的使用注意事项和模拟】_第14张图片

9.2 注意事项

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

9.3 使用举例

代码①:

#include 
#include 

int main()
{
	char str1[] = "abcde@!sdasd@!feef";
	char str2[] = "@!";
	char tmp[30] = { 0 };
	// 因为strtok会改变被操作的字符串,所以将str1临时拷贝到tmp中
	strcpy(tmp, str1);

	char* p = strtok(tmp, str2);
	printf("%s\n", p);
	p = strtok(NULL, str2);
	printf("%s\n", p);
	p = strtok(NULL, str2);
	printf("%s\n", p);
	
	return 0;
}

请添加图片描述

代码②:(代码①的基础上优化)

#include 
#include 

int main()
{
	char str1[] = "abcde@!sdasd@!feef";
	char str2[] = "@!";
	char tmp[30] = { 0 };
	// 因为strtok会改变被操作的字符串,所以将str1临时拷贝到tmp中
	strcpy(tmp, str1);

	char* p = NULL;
	for (p = strtok(tmp, str2); p != NULL; p = strtok(NULL, str2)) {
		printf("%s\n", p);
	}
	
	return 0;
}

请添加图片描述


错误信息报告:(十)


十、返回错误信息strerror函数

10.1 基本语法

【C语言字符和字符串的库函数的使用注意事项和模拟】_第15张图片

10.2 注意事项

  • C语言中规定了一些信息,strerror函数返回错误码所对应的错误信息

10.3 使用举例

#include 
#include 

int main()
{
	int i = 0;
	for (i = 0; i < 10; i++) {
		printf("%s\n", strerror(i));
	}
	
	return 0;
}

【C语言字符和字符串的库函数的使用注意事项和模拟】_第16张图片

10.4 补充用法

10.4.1 补充用法使用说明

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

10.4.2 fopen函数基本语法说明

【C语言字符和字符串的库函数的使用注意事项和模拟】_第17张图片

10.4.3 补充用法使用举例

使用举例:

说明

#include 
#include 
#include 

int main()
{
	// 打开文件,如果test.txt已创建则没有错误
	FILE* pf = fopen("test.txt", "r");
	// 如果文件出错会返回NULL
	if (NULL == pf) {
		// 打印出错原因
		printf("%s\n", strerror(errno));
		// 出错直接结束main函数
		return 0;
	}
	// 读文件。。。

	// 关闭文件
	fclose(pf);
	pf = NULL;
	
	return 0;
}

请添加图片描述


字符操作:(十一)


十一、字符分类函数

函数 如果他的参数符合下列条件就返回真
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 ); 小写转换大写

对于isdigit函数:

int main()
{
	char c = '3';

	// 判断字符数字是否为十进制0-9
	// 方法1
	if (c > '0' && c < '9') {
		printf("bingo\n");
	}

	// 方法2(更好)
	if (isdigit(c)) {
		printf("bingo\n");
	}
	
	return 0;
}

对于大小写转换函数(tolower和toupper):

int main()
{
	char c = 'a';

	if (islower(c)) {
		c = toupper(c);
	}
	else {
		c = tolower(c);
	}
	printf("%c\n", c);
	
	return 0;
}

总结

这里对文章进行总结:
以上就是今天总结的内容,本文包括了C语言字符和字符串的库函数的使用注意事项和模拟,分享给大家。
真欢迎各位给予我更好的建议,欢迎访问!!!小编创作不易,觉得有用可以一键三连哦,感谢大家。peace
希望大家一起坚持学习,共同进步。梦想一旦被付诸行动,就会变得神圣。

欢迎各位大佬批评建议,分享更好的方法!!!

你可能感兴趣的:(C语言进阶知识,c语言,字符串,经验分享)