专题:C语言常用函数

目录

前言

1.strlen函数 

(1)strlen()的基本使用方法

(2)strlen()的返回值类型

(3)自定义my_strlen()

2.strcpy函数

(1)strcpy()的基本使用方法

(2)自定义my_strcpy()

3.strcat函数

(1)strcat()的基本使用方法

(2)自定义my_strcat()

4.strcmp函数

(1)strcmp()的基本使用方法

(2)自定义my_strcmp()

5.str- 与 strn-

(1)长度不受限制的字符串函数

(2)长度受限制的字符串函数

6.strstr函数

(1)strstr()的基本使用方法

(2)自定义my_strstr()

(3)strchr 与 strrchr 函数

7.strtok函数

(1)strtok()的基本使用方法

8.strerror函数

(1)strerror()的基本使用方法

9.其它字符串操作函数

输入输出函数

大小写转换函数

字符串检索

10.常见字符操作函数

输入输出函数

大小写转换函数

字符分类函数

11.memset函数

(1)memset()的基本使用方法

12.memcpy函数

(1)memcpy()的基本使用方法

(2)自定义my_memcpy()

13.memmove函数

(1)memmove()的基本使用方法

(2)自定义my_memcpy()

14.memcmp函数

(1)memcmp()的基本使用方法

总结



前言

        作者本人在学习了一些C语言的函数后,解决问题的时候很少去使用它们(除了strlen),在观摩到一些大佬巧妙的解题思路与方法之后总会感慨:代码都看得懂,为什么我没有这样去做呢?然后自己再按照大佬的思路打算重新做一遍,结果发现自己却写不出来了。我觉得这样的初学者并不是少数,所以我打算重新学习一遍,并把自己的学习过程分享在这里。

注意:本文主要涉及一些比较常见的字符(串)操作函数(string)及内存相关函数(memory),我们只需要掌握最常用的几个函数即可,其它函数在需要的时候再去查找

学习函数的网站推荐:cplusplus.com


1.strlen函数 

(1)strlen()的基本使用方法

#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[] = "abcdefg";
	char arr2[] = "abc\0defg";
	char arr3[20] = "abcdef";
	char arr4[] = { 'a','b','c','d','e','f','g' };
	char* arr5 = "abcde";
	printf("%d\n", strlen(arr1));
	printf("%d\n", strlen(arr2));
	printf("%d\n", strlen(arr3));
	printf("%d\n", strlen(arr4));
	printf("%d\n", strlen(arr5));
	return 0;
}

功能:strlen()函数返回的是字符串中'\0'之前出现的字符的个数,不包括'\0'。参数指向的字符串必须以'\0'结束才能返回正常的结果。


(2)strlen()的返回值类型

int main()
{
	char arr1[] = "abcd";
	char arr2[] = "abcdefg";
	printf("part1:>");
	if (strlen(arr1) - strlen(arr2) < 0)
	{
		printf("arr1的长度小于arr2\n");
	}
	else
	{
		printf("arr1的长度大于等于arr2\n");
	}

	printf("part2:>");
	if ((int)strlen(arr1) - (int)strlen(arr2) < 0)
	{
		printf("arr1的长度小于arr2\n");
	}
	else
	{
		printf("arr1的长度大于等于arr2\n");
	}
	return 0;
}

注意:两个用strlen()函数计算出来的字符串的长度相减恒大于等于0,可见strlen()函数的返回值并非我们想象的int类型,而是无符号类型size_t

 

(3)自定义my_strlen()

自定义my_strlen()函数类型为 int 或 size_t 都可以,但它们各有优缺点
例如:如果为 int 类型,就比较方便理解 strlen(arr1) - strlen(arr2) 这样
的形式,但是如果出现意料之外的错误,就有可能导致计算的字符串长度为负数

//创建临时变量-计数器count
int my_strlen1(const char *arr)
{
	assert(arr != NULL);
	int count = 0;
	while (*arr != '\0')
	{
		count++;
		arr++;
	}
	return count;
}

//不创建临时变量-递归
int my_strlen2(const char* arr)
{
	assert(arr != NULL);
	if (*arr != '\0')
		return 1 + my_strlen2(arr + 1);
	else
		return 0;
}

int main()
{
	char arr[] = "abcdefghijk";
	int ret = my_strlen1(arr);
	printf("%d\n", ret);
	printf("%d\n", my_strlen2(arr));
	return 0;
}


2.strcpy函数

(1)strcpy()的基本使用方法

参数1:目标字符串(必须是可以修改的);参数2:源字符串(定义为const类型防止被修改)。

下面哪些代码可以正常运行?

	char arr1_src[] = "abcdef";
	char arr1_dest[] = { 0 };
	strcpy(arr1_dest, arr1_src);
	printf("%s\n", arr1_dest);
//arr1_dest 所占空间太小
	char arr2_src[] = { 'a','b','c','d','e','f' };
	char arr2_dest[20] = { 0 };
	strcpy(arr2_dest, arr2_src);
	printf("%s\n", arr2_dest);
//arr2_src 中没有 '\0' 
	char arr3_src[] = "abc\0def";
	char arr3_dest[20] = { 0 };
	strcpy(arr3_dest, arr3_src);
	printf("%s\n", arr3_dest);
//仅成功拷贝 \0 之前的 abc 三个字符
	char arr4_src[] = "abcdef";
	char* arr4_dest = "qwerty";
	strcpy(arr4_dest, arr4_src);
	printf("%s\n", arr4_dest);
//目标空间为 const char 类型,不可被修改
	char arr5_src[] = "abcdef";
	char arr5_dest[20] = { 0 };
	strcpy(arr5_dest, arr5_src);
	printf("%s\n", arr5_dest);
//拷贝成功

功能:把源字符串拷贝一份存放在目标字符串中,源字符串必须以'\0'结束;会将源字符串中的'\0'拷贝到目标空间;目标空间必须足够大,以确保能够存放源字符串;目标空间必须可变

(2)自定义my_strcpy()

char* my_strcpy(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

int main()
{
	char arr1[] = "Practise makes perfect!";
	char arr2[30] = { 0 };
	my_strcpy(arr2, arr1);
	printf("%s", arr2);
	return 0;
}


3.strcat函数

(1)strcat()的基本使用方法

参数1:主字符串;参数2:附加的字符串。

int main()
{
	char arr1_dest[20] = "hello ";
	char arr1_src[] = "world!";
	strcat(arr1_dest, arr1_src);
	printf("%s\n", arr1_dest);

	char arr2_dest[20] = "hello \0xxxxxx";
	char arr2_src[] = "world!";
	strcat(arr2_dest, arr2_src);
	printf("%s\n", arr2_dest);

	char arr3_dest[20] = { 'h','e','l','l','o',' '};
	char arr3_src[] = "world!";
	strcat(arr3_dest, arr3_src);
	printf("%s\n", arr3_dest);

	char arr4_dest[20] = "hello ";
	char arr4_src[] = { 'w','o','r','l','d', '!'};
	strcat(arr4_dest, arr4_src);
	printf("%s\n", arr4_dest);
	return 0;
}

功能:把源字符串的内容(包括'\0')连接到目标字符串的'\0'后面(覆盖目标字符串的'\0'),确保目标空间足够大并且可以修改

(2)自定义my_strcat()

char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest != '\0')
	{
		dest++;
	}
	while (*dest++ = *src++)
	{
        ;
	}
	return ret;
}

int main()
{
	char arr1[30] = "hello ";
	char arr2[30] = "world!";
	my_strcat(arr1, arr2);
	printf("%s", arr1);
	return 0;
}

思考:
my_strcat(arr1, arr1);
可以达到如下效果吗?
arr1 = "hello hello ";


4.strcmp函数

(1)strcmp()的基本使用方法

char arr1[] = "compare";
char arr2[] = "compbre";
//可以这样比较字符串的大小吗?
if("compare" == "compbre")
    printf("相等\n");
//这里实际上比较的是它们的地址
//因此这种写法是不能满足我们的需要的



int main()
{
	char arr1[] = "abcde";
	char arr2[] = "abcde";
	char arr3[] = "abcdabcd";
	char arr4[] = "z";

	if (strcmp(arr1, arr2) > 0)
		printf("arr1 > arr2\n");
	else if (strcmp(arr1, arr2) == 0)
		printf("arr1 = arr2\n");
	else
		printf("arr1 < arr2\n");

	if (strcmp(arr1, arr3) > 0)
		printf("arr1 > arr3\n");
	else if (strcmp(arr1, arr3) == 0)
		printf("arr1 = arr3\n");
	else
		printf("arr1 < arr3\n");

	if (strcmp(arr1, arr4) > 0)
		printf("arr1 > arr4\n");
	else if (strcmp(arr1, arr4) == 0)
		printf("arr1 = arr4\n");
	else
		printf("arr1 < arr4\n");
	return 0;
}

功能:比较字符串的大小,首先比较两个字符串的首元素的ASCII码的大小,如果相等就向后比较各自的下一个元素,如果两个字符串的所有元素都相等,那么才能判断这两个字符串相等

(2)自定义my_strcmp()

int my_strcmp(const char* arr1, const char* arr2)
{
	assert(arr1 && arr2);
	while (*arr1 == *arr2)
	{
		if (*arr1 == '\0')
			return 0;
		arr1++;
		arr2++;
	}
	return *arr1 - *arr2;
}

int main()
{
	char arr1[] = "compare";
	char arr2[] = "compbre";
	int ret = my_strcmp(arr1, arr2);

	if (my_strcmp(arr1, arr2) > 0)
		printf("arr1 > arr2\n");
	else if(my_strcmp(arr1, arr2) == 0)
		printf("arr1 = arr2\n");
	else
		printf("arr1 < arr2\n");
	return 0;
}


5.str- 与 strn-

(1)长度不受限制的字符串函数

我们前面介绍的strlen、strcpy、strcmp、strcat函数中作为参数的字符串的长度都不受限制,可能会留下安全隐患,例如:

(2)长度受限制的字符串函数

长度受限制的字符串函数只是相对安全一些,如果一个人要写bug,是谁也拦不住的!!!

strnlen、strncpy、strncmp、strncat

例如:


6.strstr函数

(1)strstr()的基本使用方法

参数1:母字符串;参数2:子字符串。

功能:判断str2是否为str1的子字符串,返回的是str1中str2第一次出现的位置的地址

(2)自定义my_strstr()

char* my_strstr(const char* arr1, const char* arr2)
{
	assert(arr1 && arr2);
	char* str1 = NULL;
	char* str2 = NULL;
	char* p = (char*)arr1;
	while (*p)
	{
		str1 = p;
		str2 = (char*)arr2;
		while (*str1 && *str2 && *str1++ == *str2++)
		{
			;
		}
		if (*str2 == '\0')
			return p;
		p++;
	}
	return NULL;
}

int main()
{
	char arr1[] = "qwereabcd";
	char arr2[] = "were";
	char* ret = my_strstr(arr1, arr2);
	if (ret == NULL)
	{
		printf("找不到\n");
	}
	else
	{
		printf("找到了:%s中存在字符串%s\n", ret, arr2);
	}
	return 0;
}

(3)strchr 与 strrchr 函数

int main()
{
	char arr1[] = "abcdefgbcde";
	char arr2[] = "bcde";
	char* ret1 = strchr(arr1, 'b');
	if (ret1 == NULL)
	{
		printf("找不到\n");
	}
	else
	{
		printf("%s\n", ret1);
	}
	char* ret2 = strrchr(arr1, 'b');
	if (ret2 == NULL)
	{
		printf("找不到\n");
	}
	else
	{
		printf("%s\n", ret2);
	}
	return 0;
}

总结:strchr返回的是第一次遇到目标字符的地址,strrchr返回的是最后一次遇到目标字符的地址


7.strtok函数

(1)strtok()的基本使用方法

参数1:待分割的字符串;参数2:常量字符串(分割字符的集合)。

0

功能:返回一个“标记”:指向待分割的字符串其中一段的指针(把原字符串中的分割字符修改为'\0',从而分割原字符串)。

strtok的第一个参数不为NULL时,函数返回第一个标记并保存它在字符串中的位置;

strtok的第一个参数为NULL时,函数将从被保存的位置开始查找下一个标记;

如果字符串中不存在更多标记时,返回NULL

我认为目前能够学会使用strtok函数就足够了

有兴趣的同学可以自行尝试,欢迎在评论区分享自己的my_strtok!


8.strerror函数

(1)strerror()的基本使用方法

参数:错误码

功能:返回错误码对应的错误信息的首字符的地址(把一个错误码翻译为错误信息)

解释:C语言的库函数在调用失败的时候会把一个错误码存放在一个叫errno的变量中,当我们想知道发生了什么错误的时候,可以利用strerror将错误码errno翻译为错误信息

接下来我们举个例子:


9.其它字符串操作函数

输入输出函数

gets 读取一串字符(包括空格)

puts  输出一串字符

大小写转换函数

strlwr 转小写

strupr 转大写

字符串检索

strpbrk ...

strspn ...

... ...


10.常见字符操作函数

输入输出函数

getchar 读取一个字符

putchar 输出一个字符

大小写转换函数

tolower 转小写

toupper 转大写

字符分类函数

符合条件就返回真

isalnum 判断是否为字母或数字
isdigit 判断是否为数字
isxdigit 判断是否为十六进制数字
isalpha 判断是否为字母
isupper 判断是否为大写字母
islower 判断是否为小写字母
iscntrl 判断是否为控制字符
isspace 判断是否为空格、换页、换行、回车、制表符
ispunct 判断是否为标点符号
isgraph 判断是否为图形字符
isprint 判断是否为可打印字符

... ...


11.memset函数

(1)memset()的基本使用方法

参数1:指向需要被修改的位置(指针/数组名);参数2:修改后的数据;参数3:被修改的空间的大小(单位是字节)

val为int类型

val为char类型

总结:

(1) 若ptr指向char型地址,value可为任意字符值;
(2) 若ptr指向非char型,如int型地址,要想赋值正确,value的值只能是 - 1或0,因为 - 1和0转化成二进制后每一位都是一样的,因此我们常用memset初始化数组为全0
 


12.memcpy函数

(1)memcpy()的基本使用方法

参数1:指向目标空间的(void类型)指针;参数2:源空间的(void类型)指针;参数3:被拷贝的空间的大小(单位是字节)

注意:当我们需要拷贝字符串的时候,是否可以使用memcpy呢?答案是当然可以,
但是我们为什么不直接使用更加方便的strcpy呢?

void test1()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	memcpy(arr2, arr1, 32);
	for (int i = 0; arr2[i] != 0; i++)
	{
		printf("%d ", arr2[i]);
	}
	printf("\n");
}

void test2()
{
	float arr1[] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 };
	float arr2[48] = { 0.0 };
	memcpy(arr2, arr1, 32);
	for (int i = 0; arr2[i] != 0; i++)
	{
		printf("%.2f ", arr2[i]);
	}
	printf("\n");
}

int main()
{
	test1();
	test2();
	return 0;
}

(2)自定义my_memcpy()

void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	while (num--)
	{
		//我们不知道使用者需要拷贝的类型,所以采用逐字节拷贝的方法
        *(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

void test3()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	my_memcpy(arr2, arr1, 32);
	for (int i = 0; arr2[i] != 0; i++)
	{
		printf("%d ", arr2[i]);
	}
	printf("\n");
}

int main()
{
	test3();
	return 0;
}


13.memmove函数

(1)memmove()的基本使用方法

参数1:指向目标空间的(void类型)指针;参数2:源空间的(void类型)指针;参数3:被移动的空间的大小(单位是字节)

 

下面代码可以实现我们期望的结果吗?

为什么呢?

注意:并非所有的编译器都无法在内存重叠的时候正常拷贝,但是memcpy确实存在覆盖内存的风险! 

(2)自定义my_memcpy()

void* my_memmove(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	if (dest < src)
	{
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	return ret;
}


14.memcmp函数

(1)memcmp()的基本使用方法

参数1:需要被比较的空间的(void类型)指针1;参数2:需要被比较的空间的(void类型)指针2;参数3:被比较的空间的大小(单位是字节)

void test1()
{
	int arr1[] = { 1,2,3,4,5,7 };
	int arr2[] = { 1,2,3,4,6,7 };
	int ret = memcmp(arr1, arr2, 16);
	printf("%d\n", ret);//0
}

void test2()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,6 };
	int ret = memcmp(arr1, arr2, 20);
	printf("%d\n", ret);//-1
}

void test3()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,6 };
	int ret = memcmp(arr1, arr2, 17);
	printf("%d\n", ret);//-1
}

int main()
{
	test1();
	test2();
	test3();
	return 0;
}


总结

        以上就是今天要讲的内容,其实学习编程不仅需要刷题,还得多思考总结,很多时候我们没有大佬的灵光一闪(我认为一般情况下这是需要很大的努力之后才可能做到的),把一些解题的技巧与思路记录下来,能够在遇到问题的时候有更多方法。希望本文能够对大家有所帮助,欢迎广大读者批评指正^W^

猜你喜欢

转载自blog.csdn.net/weixin_72501602/article/details/129194248