C语言、字符串函数具体实现和功能介绍 汇总

此文介绍常用、面试爱考的几个函数: strlen()、strcpy()、strncpy()、strcat()、strncat()、strcmp()、strncmp()、strstr()、strchr()、strtok()、strerror()使用、memcpy()、memmove()、memset()、memcmp()

 字符串函数在实现的时候一定得进行 :

Double check 即:1、函数调用者在函数调用时需保证参数的有效性。

    合理化校验          2、函数使用者在函数内部保证参数的有效性。

    即:判断参数是否为空,或者指向 '\0'.

     方案一:

if(str == NULL)
{
    return 0;   //使用条件语句判空则函数结束
}

     方案二: 

assert(str != NULL);    //断言函数

    解释 :表达式为真,断言通过,函数顺利执行;

                表示式为假,断言失败, 程序直接崩溃(函数封装时使用,但平时写大型程序时不介意使用); 

   使用时需包含头文件    面试时断言函数很重要。

 

 strlen ()

size_t strlen (const char* str);

   注: 函数参数必须为指向字符串;

            strlen(),返回指向字符串中 '\0' 前的字符得个数,      返回值为size_t型,是无符号的。

函数模拟实现代码:

#include 
#include 
#include   //使用断言函数必须包含头文件

size_t my_strlen(const char* dest)  //返回值为无符号型
{
	size_t count = 0;
	assert(dest != NULL); //断言
	while(*dest++)       //循环直到dest指向 '\0'结束
	{              
		count++;   
	}              
	return count;
}

int main ()
{
	char str[] = "abcdefg";  //操作对象必须为字符串
	int k;
	k = my_strlen(str);
	printf("字符串str的长度为:%d\n", k);
	system("pause");
	return 0;
}

上面实现的my_strlen()函数中while循环等价于:

while ( *dest )   //等价于 while ( * dest != '\0' ) 

      {     dest++;  count++; }

 

strcpy ()

char* strcpy (char* dest, const char* src) ;

  注 :  实现把src里的内容全部赋给dest, 包括  '\0',覆盖dest里原有的内容。

            实参 dest 的空间必须保证足够大,保证拷贝不会出现内存越界行为

            返回值为: 拷贝后指向 dest 的指针。

函数模拟实现代码:

#include 
#include 
#include   //使用断言函数必须包含头文件

char* my_strcpy(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest != NULL);
	assert(src != NULL);
	while(*src) //src指向 '\0'时循环退出
	{
		*dest++ = *src++;
	}   //函数退出条件必须为尾指针指向'\0'
	*dest = '\0'; // 此时 *dest 正好指向字符串的末尾,并给此位置赋值为'\0'
	return ret;  //返回dest 开始时的首地址

}

int main ()
{
	char str1[1024] = "abcdefg";  //目标字符串的空间必须足够大,推荐写成1024
	char* str2 = "hijk";   //保证str2开辟的内存小于str1,避免出现内存访问越界       
	char* ret;
	ret = my_strcpy(str1, str2);
	printf("my_strcpy的结果为:%s\n", ret);
	system("pause");
	return 0;
}

 

strncpy ( )

char* strncpy(char* dest, const char* src, size_t len);

    注:  拷贝 len 长的字符串到 dest字符串中。

             实参 dest 的空间必须保证足够大,保证拷贝不会出现内存越界行为

             当 len < strlen(src) 时,拷贝后字符时应向dest 后面追加 '\0' 直到 len 个

             其他与strcpy相同。

   函数模拟实现代码:

          注:必须得考虑 len 大于 strlen(src)的情况

#include 
#include 
#include   //使用断言函数必须包含头文件
#include 

char* my_strncpy(char* dest, const char* src, size_t len)
{
	char* ret = dest;
	size_t offset = 0;
	assert(dest != NULL);
	assert(src != NULL);
	if(len > strlen(src)) //此条件语句完美解决 len 过长的问题
	{
		offset = len - strlen(src);
		len = strlen(src);          //文章中具体解释语句
	}
	while(len--)
	{
		*dest++ = *src++; //拷贝字符
	}
	while(offset--)
	{
		*dest = '\0'; //将len大于strlen(src)的长度全都赋值为'\0'
	}
	return ret;  //返回dest 开始时的首地址

}

int main ()
{
	char str1[1024] = "abcdefg";  //目标字符串的空间必须足够大,推荐写成1024
	char* str2 = "hijk";   //保证str2开辟的内存小于str1,避免出现内存访问越界    
	int size = 100;
	char* ret;
	ret = my_strncpy(str1, str2, size);
	printf("my_strncpy的结果为:%s\n", ret);
	system("pause");
	return 0;
}

        上面函数中的 if 条件语句:

    if(len > strlen(src)) //此条件语句完美解决 len 过长的问题
    {
        offset = len - strlen(src);        把len大于strlen(src)的具体值拿出来 ,拷贝完后把这个长度的每个地址都赋值为  '\0 '。
        len = strlen(src);           再让len 等于strlen(src),代表要拷贝的字符。
    }

 

strcat ( )

char* my_strcat (char* dest, const char* src);

    注:  首先还是一样的,目标字符串必须足够大并且可以修改。

             实参 dest 的空间必须保证足够大,保证拷贝不会出现内存越界行为

              将 src 指向的字符串内容拼接到 dest 的后面,最后再补上 '\0' 。

函数模拟实现代码:

#include 
#include 
#include   //使用断言函数必须包含头文件


char* my_strcat(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest != NULL);
	assert(src != NULL);
	
	while(*dest)
	{
		dest++; // 让dest指向目标字符串中'\0'的位置
	}
	while(*src)
	{
		*dest++ = *src++; //从目标字符串'\0'的位置开始拷贝 src 中的字符串
	}
	*dest = '\0';
	return ret;  //返回dest 开始时的首地址

}

int main ()
{
	char str1[1024] = "abcdefg";  //目标字符串的空间必须足够大,推荐写成1024
	char* str2 = "hijk";   //保证str2开辟的内存小于str1,避免出现内存访问越界    
	char* ret;
	ret = my_strcat(str1, str2);
	printf("my_strcat的结果为:%s\n", ret);
	system("pause");
	return 0;
}

 

strncat ( )

char* my_strncat(char* dest, const char* src, size_t len);

     注:  基本用法和strcat相同,但只是拼接 len 个字符到 dest 里面 。

              实参 dest 的空间必须保证足够大,保证拷贝不会出现内存越界行为

              特别注意 len > strlen(src)  的情况(应把大于 strlen(src) 的长度赋值为  '\0')。

函数模拟实现代码:       

         注:必须得考虑 len 大于 strlen(src)的情况

#include 
#include 
#include   //使用断言函数必须包含头文件
#include 

char* my_strncat(char* dest, const char* src, size_t len)
{
	char* ret = dest;
	size_t offset = 0;
	assert(dest != NULL);
	assert(src != NULL);
	if(len > strlen(src)) //此条件语句完美解决 len 过长的问题
	{
		offset = len - strlen(src);
		len = strlen(src);
	}
	while(*dest)
	{
		dest++; // 让dest指向目标字符串中'\0'的位置
	}
	while(len--)
	{
		*dest++ = *src++; //从目标字符串'\0'的位置开始拷贝 src 中的字符串
	}
	while(offset--)
	{
		*dest = '\0'; //将len大于strlen(src)的长度全都赋值为'\0'
	}
	return ret;  //返回dest 开始时的首地址

}

int main ()
{
	char str1[1024] = "abcdefg";  //目标字符串的空间必须足够大,推荐写成1024
	char* str2 = "hijk";   //保证str2开辟的内存小于str1,避免出现内存访问越界    
	int size = 100;
	char* ret;
	ret = my_strncat(str1, str2, size);
	printf("my_strncat的结果为:%s\n", ret);
	system("pause");
	return 0;
}

 

strcmp ( )

int strcmp(const char* dest, const char* src);

     注:   返回值为 int 类型, 即 strcmp (str1, str2)     str1 = str2  , 返回 0

                                                                                         str1 < str2  , 返回一负数

                                                                                         str1 > str2  , 返回一正数 

               参数 dest   src 为常量字符串,不能被改变

函数模拟实现代码:

#include 
#include 
#include   //使用断言函数必须包含头文件

int my_strcmp(const char* dest, const char* src)
{
	int i = 0;
	int j = 0;
	assert(dest != NULL);
	assert(src != NULL);
	while(*dest++)
	{
		i++;
	}
	while(*src++)
	{
		j++;
	}
	return i - j;
}

int main ()
{
	char str1[1024] = "abcdefg";  //目标字符串的空间必须足够大,推荐写成1024
	char* str2 = "hijk";    
	int ret = 0;
	ret = my_strcmp(str1, str2);
	if(ret > 0)
	{
		printf("str1 大于 str2!\n");
	}
	else if(ret < 0)
	{
		printf("str1 小于 str2!\n");
	}
	else
	{
		printf("str1 等于 str2!\n");
	}
	printf("my_strcmp的结果为:%d\n", ret);
	system("pause");
	return 0;
}

 

strncmp ( )

int strncmp ( const char * dest, const char * src, size_t num );

           注 :   返回值和 strcmp 相同

                      此函数表示 :  dest  和  src 的前 num项比较大小

                      个人表示对这个函数很无语,感觉就是个鸡肋

函数模拟实现代码:

        注: num 大小的处理

                 len > strlen(src)时len - strlen(src)的长度空间为空,需自动略去

                 当 len < strlen(src),只用比较strlen(src) - len 长度的字符

#include 
#include 
#include   //使用断言函数必须包含头文件
#include 

int my_strncmp(const char* dest, const char* src, size_t len)
{
	int i = 0;
	int j = 0;
	assert(dest != NULL);
	assert(src != NULL);
	//首先判断并处理 len > strlen(src) 的情况
	if(len > strlen(src))//len > strlen(src)时len - strlen(src)的长度空间为空,需自动略去
	{
		len = strlen(src);
	}
	//当 len < strlen(src),只用比较strlen(src) - len 长度的字符
	if(len < strlen(src))
	{
		len = strlen(src) - len;
	}
	while(*dest++)
	{
		i++;
	}
	while(len--)
	{
		j++;
	}
	return i - j;
}

int main ()
{
	char str1[1024] = "abcdefg";  //目标字符串的空间必须足够大,推荐写成1024
	char* str2 = "hijk";   
	int size = 20; //此处的size大小应严格考虑再赋值
	int ret = 0;
	ret = my_strncmp(str1, str2, size);
	if(ret > 0)
	{
		printf("str1 大于 str2!\n");
	}
	else if(ret < 0)
	{
		printf("str1 小于 str2!\n");
	}
	else
	{
		printf("str1 等于 str2!\n");
	}
	printf("my_strncmp的结果为:%d\n", ret);
	system("pause");
	return 0;
}

 

strstr ( )

char* strstr(const char* dest, const char* src);

    为实现判断字符串 dest 中是否包含字符串 src

   注:     返回值为指针类型    包含则: 返回dest中和src首元素相同元素的指针;

                                                不包含:返回空指针(NULL)。

函数模拟实现图解:

       注:   需设定好三个指向分别指向 dest 和 src 如下图 分别黑、红、蓝三个指针

                  C语言、字符串函数具体实现和功能介绍 汇总_第1张图片

                 三个指针分别指向dest和src的开始的位置

                  C语言、字符串函数具体实现和功能介绍 汇总_第2张图片

                  红指针和蓝指针一起移动并做比较,当红指针和蓝指针指向的内容不同时,黑指针加一如下图

                  C语言、字符串函数具体实现和功能介绍 汇总_第3张图片

                 此时黑指针和和红指针都指向b, 蓝指针重新指向src的起始位置

                 重复图二 和 图三 的步骤直到达到下图的条件

                   C语言、字符串函数具体实现和功能介绍 汇总_第4张图片

                   蓝指针指向了 '\0',条件达成。

函数模拟实现代码:

#include 
#include 
#include   //使用断言函数必须包含头文件
#include 

char* my_strstr(const char* dest, const char* src)
//建议结合解释图来看
{
	char* black_ptr = (char*)dest;
	assert(dest != NULL);
	assert(src != NULL);
	assert(dest != '\0'); //判断字符串包含时如果字符串中只有 '\0'
	assert(src != '\0'); //则必然包含,所以也许断言
	while(*black_ptr)
	{
		char* red_ptr = black_ptr;
		char* blue_ptr = (char*)src;
		while(*red_ptr && *blue_ptr && *red_ptr == *blue_ptr)//表示图中的条件
		{
			red_ptr++;
			blue_ptr++;
		}
		if(*blue_ptr == '\0')//当蓝指针指向'\0'时表示找到了
		{
			return black_ptr;
		}
		black_ptr++;
	}
	return NULL;
}

int main ()
{
	char str1[1024] = "abcdefg";
	char* str2 = "cde";
	char* ret;
	ret = my_strstr(str1, str2);
	printf("my_strstr的结果为:%s\n", ret);
	system("pause");
	return 0;
}

KMP算法用来专门解决字符串包含的问题:   目前我还处于了解阶段,等以后掌握一定的按自己的理解写出来

         给大家推荐大神写的 KMP 算法理解实现,特别棒

           https://blog.csdn.net/starstar1992/article/details/54913261

 

strchr()

char *strchr(const char *dest, char ch) ;

    查找字符串s中首次出现c字符的位置

  说明: 返回首次出现c的位置的指针,返回的地址是被查找的字符串指针开始的第一个与c相同字符的指针,若s中不存在c则返回                            NULL。

  返回值: 成功返回要查找的字符第一次出现的位置,否则返回NULL。

函数模拟实现代码:

#include 
#include 
#include   //使用断言函数必须包含头文件
#include 

char* My_strchr(const char* dest, char ch)
{
	char* start = (char*)dest;
	assert(dest != NULL);
	assert(dest != '\0');
	assert(ch != '\0');
	while(*start && *start != ch)
	{
		start++;
	}
	if(*start == ch)
		{
			return start; 
		}
	return NULL;
}

int main()
{
	char* str = "abcdefgh";
	char ch = 'd';
        printf("strchr的结果为:%s\n", My_strchr(str, ch));

	system("pause");
	return 0;
}

 

 

 

 

 

strrok ( )

char* strtok (char* dest, const char* src);

           为实现 把 dest  以 src内标记的方式切分开来

 

  函数使用示例:

         ************************************************************

  函数实现:  这个函数应该是字符串函数中实现最复杂的一个,过程比较杂,一般面试中是不会提出这个问题的

                    等我掌握之后再补上具体实现。

 

strerror ( )

char* strerror (int errnum);

     注: 使用是必须包含头文件 < errno.h > 是操作系统中的错误码

       具体功能: 如果库函数或操作系统函数执行出错时,就会给这个 errno 设置一个特定值,根据 errno 的值了解到出错的具体原因。

使用演示代码:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include 
#include   //使用断言函数必须包含头文件
#include 
#include   //使用strerror必须包含的头文件

int main()
{
	FILE* pFile;
	pFile = fopen ("unexist.ent", "r");
	if(pFile == NULL)
	{
		printf("error opening file unexist.ent: %s\n", strerror(errno));
	}//结果为:  No such file or directory
	printf("%d\n", errno); //第二种类型错误
	system("pause");
	return 0;
}

 

memcpy ( )

void* memcpy(void* dest, const void* src, size_t num);

      函数实现,把src 开始的位置复制到 dest中 '\0' 开始的位置。

      函数遇到 '\0' ,并不会停下来,复制的长度和结束都由num来决定。

      函数返回 指针dest的起始地址。

    注: 如果 dest 和 src 有任何的重叠, 复制结果都是未定义的(无法解决缓冲区重合的问题)。

 函数模拟实现代码:

#include 
#include 
#include 
#include 

char* My_memcpy(char* dest, const char* src, size_t len)
{
	char* ret = dest;
	assert(dest != NULL);
	assert(src != NULL);
	while(len--) //len刚好等于src的长度时,循环这样写
	{
		*dest++ = *src++;
	}
	*dest = '\0';
	return ret;
}

char* My_memcpy1(char* dest, const char* src, size_t len)
{
	char* ret = dest;
	int offset = 0;
	assert(dest != NULL);
	assert(src != NULL);
	if(len > strlen(src))  //当len大于strlen(src)时
	{
		offset = len - strlen(src);
		len = strlen(src);
	}
	while(len--)
	{
		*dest++ = *src++;
	}
	if(offset > 0)
	{
		while(offset--)  //len大于strlen(src)的长度全部赋值为 '\0'
		{
			*dest++ = '\0';
		}
	}
	*dest = '\0';
	return ret;
}

int main()
{
	char str1[1024] = "supreme";
	char* str2 = "doit!";

	int len = 0;

	printf("%s\n", My_memcpy(str1, str2, strlen(str2)));

	printf("请输入要复制str2 的长度(小于strlen(str1)):");
	scanf("%d", &len);
	printf("%s\n", My_memcpy1(str1, str2, len));

	system("pause");
	return 0;
}

  注: 代码需要  考虑传递参数 len的长度

 

memmove ( )

void* memmove(void* dest, const void* src, size_t num);

   此函数和memcpy 的区别 可解决缓冲区重叠的问题

    具体方法: 当内存重合时应从后往前 拷贝。

 函数模拟实现代码:

#include 
#include 
#include 
#include 

//memmove 与 memcpy主要的区别就是 可以解决缓冲区重合(内存重合)的问题
char* My_memmove(char* dest, const char* src, size_t len)
{
	char* ret = dest;
	int offset = 0;
	assert(dest != NULL);
	assert(src != NULL);
	if(len > strlen(src))  //当len大于strlen(src)时
	{
		offset = len - strlen(src);
		len = strlen(src);
	}

	//判断内存是否重合,是则从后往前复制
	if(dest >= src && dest <= src + len - 1)
	{
		char* tem = NULL;
		dest = dest + len - 1;
		src = src + len - 1;
		tem = dest;
		while(len--)
		{
			*dest-- = *src--;
		}
		*tem = '\0';
	}
	else
	{
		while(len--)
		{
			*dest++ = *src++;
		}
		*dest = '\0';
	}

	if(offset > 0)
	{
		while(offset--)  //len大于strlen(src)的长度全部赋值为 '\0'
		{
			*dest++ = '\0';
		}
	}
	return ret;
}

int main()
{
	char str1[1024] = "supreme";
	char* str2 = "doit!";

	int len = 0;

	printf("请输入要复制str2 的长度(小于strlen(str1)):");
	scanf("%d", &len);
	printf("%s\n", My_memmove(str1, str2, len));

	system("pause");
	return 0;
}

注: 1. 考虑len长度过长

         2.考虑两个字符串缓冲区重合的问题。

 

memset ( )

void* memset(void* dest, void ch, size_t len);

     以dest为起始位置的n个字节的内存区域用整数set来进行填充,len为要填充的字节数,返回值为目标dest内存的起始地址

     特别说明:len表示的是字节数,函数是以字节的形式每次赋值给目标地址

     注:  考虑len的大小应小于dest的长度,否则出现内存越界行为。

  函数模拟实现代码:

#include 
#include 
#include 
#include 

char *my_memset(char *dest, int set, size_t len)
{
	char *ret = dest;
	if (dest == NULL || len < 0)
	{
		return NULL;
	}
	while (len--)
	{
		*dest++ = set;
	}
	return ret;
}
 
int main()
{
	char str[] = "hello worlddsfsafsfsfas";
	int set = 4;
	printf("%s\n", my_memset(str, set, strlen(str)));
	system("pause");
	return 0;
}

 

memcmp()

int memcmp(const char* dest, const char* src, size_t len);

        比较内存区域 dest 和 src 的前 len 个字节。

    返回值

       当dest < src时,返回值 < 0

       当dest = src时,返回值 = 0

       当dest > src时,返回值 > 0

注:

         该函数是按字节比较的。

例如:

          s1,s2为字符串时候memcmp(s1,s2,1)就是比较s1和s2的第一个字节的ascII码值;

           memcmp(s1,s2,n)就是比较s1和s2的前n个字节的ascII码值;

  函数模拟实现代码:

#include 
#include 
#include 
#include 

int My_memcmp(const char* dest, const char* src, size_t len)
{
	assert(dest != NULL);
	assert(src != NULL);
	while(len--)
	{
		while(*dest++ == *src++)
		{
			if(*dest == '\0')
			{
				return 0;
			}
		}
	}
	if(*dest > *src)
	{
		return 1;
	}
	if(*dest < *src)
	{
		return -1;
	}
}

int main()
{
	char str1[1024] = "ABCDE";
	char* str2 = "abcd";
	int ret = 0;

	int len = 0;

	printf("请输入要比较的两个字符串的长度(小于strlen(str1)):");
	scanf("%d", &len);

	ret = My_memcmp(str1, str2, len);
	if(ret > 0)
	{
		printf("str1的前len项大于str2 的前len项!\n");
	}
	if(ret < 0)
	{
		printf("str1的前len项小于str2 的前len项!\n");
	}
	else
	{
		printf("两字符串前len项相同!\n");
	}

	system("pause");
	return 0;
}

 

   

你可能感兴趣的:(学习)