详解C语言string.h中常见的13个库函数(上)

我计划讲解C语言string.h这个头文件中,最常见的13个库函数。为了让大家更加深入的理解这些函数,部分函数我会模拟实现。篇幅所限,如果文章太长了,可能会较难坚持读完,所以我会分几篇博客来讲述。本篇博客主要讲解的函数有:strlen, strcpy, strcat, strcmp。这四个函数是最基础,最常见的字符串操作函数,在字符串相关的场景中会频繁使用,希望大家在阅读完本篇博客后,对它们的原理、使用方式了如指掌,运用自如。考虑到有朋友可能不太理解C语言字符串的一些基础概念,本篇博客会先铺垫一些基础知识,已经熟悉C语言字符串的朋友们可以跳过。

字符串必备知识

什么是字符串?就是一串字符。C语言如何表示字符串呢?用双引号括起来一串字符即可。如:"hello world"就是一个字符串。

C语言中的字符串的结束标志是\0这个转义字符。比如"hello world"这个字符串,本质上,在内存中存储的是hello world\0,在所有的字符后面,会隐藏一个\0。注意:\0作为一个整体,是一个字符,它的ASCII码值是0。

字符串一般用字符数组来存储,比如:

char arr[] = "hello world";

就把"hello world"这个字符串存储到了字符数组arr里。此时数组arr的长度是多少呢?要把hello world这11个字符算上,后面还有一个\0,总共12个字符。但是,如果计算的是字符串的长度,是不算最后的\0的,长度就是11。

字符串的长度计算的是\0之前出现了几个字符,但是不包含\0本身!那如何求字符串长度呢?这就要引出今天要讲解的第一个库函数了。

详解C语言string.h中常见的13个库函数(上)_第1张图片

strlen

size_t strlen ( const char * str );

strlen是用来求字符串的长度的。只需要给它传字符串首字符的地址,它就会计算出该字符串的长度。注意:字符串常量,即单引号引起来的字符串,作为一个表达式,其值为首字符的地址,可以作为strlen的参数。除此之外,字符串也可以保存到字符数组中,数组名表示首元素地址,也可以作为参数。

字符串直接作为参数:

int len1 = strlen("abc"); // 3

char* str = "defg";
int len2 = strlen(str); // 4

数组名作为参数:

char arr[] = "abc";
int len = strlen(arr); // 3

strlen的返回值是size_t类型的。size_t是一个无符号整型。所以以下程序会输出什么?

if (strlen("abc") - strlen("abcd") < 0)
	printf("<\n");
else
	printf(">=");

看起来应该是“小于”,但结果是“大于等于”,原因是无符号整型的差还是无符号整型,一定是大于等于0的。

下面我们来模拟实现strlen。实现思路很简单,从首字符开始,向后数,直到遇到\0就停下来。

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

当然,我们也可以一直向后找\0,根据“指针-指针得到的是指针之间的元素个数”的原理,用\0的地址减首字符的地址,也可以得到字符串的长度。

size_t my_strlen(const char* str)
{
	assert(str);
	
	const char* eos = str; // end of str
	while (*eos)
	{
		++eos;
	}
	
	return eos - str;
}

当然,如果不创建临时变量,也可以使用递归实现,这种实现并不推荐,因为递归是有缺陷的,递归深度太深可能导致栈溢出。递归实现思路是:字符串的长度=1+从下一个字符开始数的长度。也就是说,strlen(str) = 1+strlen(str+1)。当然,如果str是空字符串,即*str=='\0',长度就为0。

size_t my_strlen(const char* str)
{
	assert(str);
	
	if (*str)
		return 1 + my_strlen(str + 1);
	else
		return 0;
}

strlen总结:

  1. strlen是用来求字符串长度的,会返回\0之前出现了几个字符。
  2. 参数指向的字符串必须以\0结尾,否则结果是随机值。
  3. 函数的返回值类型是size_t的,size_t是一个无符号整型。

strcpy

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

strcpy是用来完成字符串拷贝的。它有2个参数,分别是目的地和起始位置。比如,把字符串arr1拷贝到arr2里,要这么写:

char arr1[20] = {0};
char arr2[] = "abc";
strcpy(arr1, arr2);

注意:拷贝时,会把arr2中的"abc"拷贝到arr1中,包括结尾的\0

strcpy会返回目标空间的起始地址,方便函数的链式访问,比如:

printf("%s\n", strcpy(arr1, arr2));

以上代码,在把arr2中的字符串拷贝到arr1中后,顺便把arr1打印出来,看看有没有拷贝成功。

下面讲讲模拟实现。其实重点是拷贝的过程,也就是*dst++ = *src++,把src指向的字符拷贝到dst指向的空间中,并且2个指针向后走,直到src遇到\0,此时结束循环,返回起始地址。注意dst在拷贝的过程中一直在向后走,所以需要在最开始先保存下来,方便最后返回目标空间的地址。

char* my_strcpy(char* dst, const char* src)
{
	assert(dst && src);

	char* ret = dst;
	while (*dst++ = *src++)
	{
		;
	}
	return ret;
}

strcpy总结:

  1. strcpy是用来拷贝字符串的。
  2. 源字符串必须以\0结束,否则会一直拷贝字符,直到遇到内存中的\0
  3. 拷贝时,会把源字符串结尾的\0也拷贝到目标空间中。
  4. 目标空间必须足够大,能够容纳拷贝的源字符串,否则会导致内存的越界访问。
  5. 目标空间必须可变(可修改)。

strcat

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

strcat是完成字符串追加的,它可以在目标字符串后面追加源字符串。可以理解为,先从目标空间中找到\0,即目标字符串的结尾,然后从\0所在的位置开始,向后追加源字符串。比如:在abc后面追加def后就得到了abcdef,追加的时候会把源字符串的\0也追加过去。函数会返回目标字符串的起始地址。

其实可以简单理解为:先找到目标字符串结尾的\0,然后从把源字符串以strcpy的方式拷贝到目标字符串后面,大家看到模拟实现后就明白了。

char* my_strcat(char* dst, const char* src)
{
	assert(dst && src);
	char* ret = dst;

	while (*dst)
	{
		++dst;
	}

	while (*dst++ = *src++)
	{
		;
	}
	return ret;
}

其实就是在strcpy的模拟实现的基础上,加上了下面的代码,即找dst中的\0

while (*dst)
{
	++dst;
}

根据以上的实现,能不能自己给自己追加呢?比如:

char arr[10] = "abc";
strcat(arr, arr);

我们想再arr后面追加arr,预期结果是,“abc"后面追加"abc"得到"abcabc”,但是根据以上模拟实现的代码,在追加的同时,会把src中的\0给覆盖掉,所以源字符串中就内有\0了,在拷贝的时候,本来是遇到\0就停止了,但是一直找不到\0,就导致无限循环。

那如何实现自己给自己追加呢?这就要用到strncpy函数了,这个函数我会在下一篇博客中介绍。

strcat总结:

  1. strcat是用来追加字符串的,会把源字符串追加到目标字符串后面。
  2. 源字符串和目标字符串都必须以\0结束。
  3. 目标空间必须足够大,以容纳追加的字符串。
  4. 目标空间必须可变(可修改)。
  5. 不能自己给自己追加。

strcmp

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

strcmp是用来比较2个字符串的。比较方式是:从第一个字符开始,一个一个往后比,直到遇到第一对不同的字符或者都遇到\0。如果遇到第一对不同的字符,则比较其ASCII码值,哪个大,对应的字符串就更大;如果都遇到\0,则2个字符串相等。函数会根据不同的大小关系返回不同的值,当str1>str2时,返回一个正整数;如果str1

根据以上的描述,可以模拟实现,如下:

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;
}

strcmp总结:

  1. strcmp是用来比较2个字符串的大小的。
  2. 假设从左到右2个参数分别是str1和str2。如果str1>str2,返回正整数;如果str1
  3. 比较时,是根据从2个字符串的第一个字符开始比较,向后比,直到遇到不同的字符或者都遇到\0。如果遇到不同的字符,则ASCII码值大的字符对应的字符串更大;如果都遇到\0,则2个字符串相等。
  4. 2个字符串结尾都应该有\0,没有的话,会一直向后比较字符,如果都相等,会一直比下去。

总结

  1. strlen是用来求字符串长度的,统计的是\0之前出现的字符个数。
  2. strcpy是用来拷贝字符串的。有2个参数,目标空间在前面,源字符串在后面;strcat是用来追加字符串的,也是2个参数,目标字符串在前面,源字符串在后面,比strcpy多了1个步骤,即先在目标空间中找到\0,再拷贝。
  3. strcmp是用来比较字符串的。前面字符串和后面字符串比较,如果前面大会返回正整数,前面小会返回负整数,前后相等返回0。

你可能感兴趣的:(C语言,c语言,算法,字符串,函数,开发语言)