C语言——字符函数和字符串函数(下)

引言

在上一篇中,我们介绍了字符函数和一些字符串函数,在这一篇章中,我们会继续学习字符串函数,那我们现在就开始学习吧!!!

字符串函数

strncpy

1.strncpy的用法

strncpy是一个在C语言中常用的字符串处理函数,用于将源字符串的前n个字符复制到目标字符串中。其函数原型为:

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

其中,参数的含义如下:

dest 是目标字符串,即你想要将源字符串的字符复制到的位置

src 是源字符串,即你想要从中复制字符的字符串

n 是你想要从源字符串中复制的字符数

如果源字符串的长度小于 n,strncpy 会复制源字符串的所有字符到目标字符串中,并在目标字符串的剩余位置上用 \0 填充,直到总共复制了 n 个字符

2.strncpy的使用

当src的字符串长度>=n

int main()
{
	char src[12] = "hello world";
	char dest[] = "xxxxxxxxxxxx";
	strncpy(dest, src, 5);
	printf("最终的目标字符串:%s\n", dest);
	return 0;
}

运行结果为:

最终的目标字符串:helloxxxxxxx

当src的字符串长度<n

int main()
{
	char src[25] = "hello world";
	char dest[] = "xxxxxxxxxxxxxx";
	strncpy(dest, src, 20);
	printf("最终的目标字符串:%s\n", dest);
	return 0;
}

运行结果为:

最终的目标字符串:hello world

3.strncpy的模拟实现

思路:通过自定义的 my_strncpy 函数,将源字符串 str2 的 n 个内容复制到目标字符串 str1 中,确保复制操作安全进行(通过断言检查指针非空),并返回目标字符串的起始地址。

代码如下:

char* my_strncpy(char* dest,const char* src,size_t n)
{
	// 断言dest和src不是空指针,以确保它们是有效的内存地址
	assert(dest && src);
	// 保存dest的原始地址,以便稍后返回
	char* ret = dest;
	// 循环复制字符,直到遇到源字符串的结束符'\0',或者复制了n个字符
	while (*src && n)
	{
		*dest = *src;
		dest++;
		src++;
		n--;	// 递减n,表示已经复制了一个字符
	}
	// 如果n不为0,说明还有剩余的空间需要填充null终止符
	if (n != 0)
	{
		while (n)
		{
			*dest = '\0';
			dest++;
			n--;
		}
	}
	return ret;
}

int main()
{
	char arr1[10] = "abcdef";
	char arr2[] = "bbq";
	my_strncpy(arr1, arr2, 3);
	printf("%s\n", arr1);
	return 0;
}

strncat

1.strncat的用法

strncat()函数是C语言中的一个字符串函数,用于将一个字符串的前n个字符连接到另一个字符串的末尾。

函数的原型为:

char *strncat(char *dest, const char *src, size_t n);

其中,参数的含义如下:

dest:目标字符串,即要将源字符串连接到的位置

src:源字符串,即要连接到目标字符串末尾的字符串

n:要连接的字符个数

strncat函数的工作原理是将源字符串src中的字符逐个复制到目标字符串dest的末尾,直到复制了指定的字符个数n或者遇到了源字符串的结束符'\0'。连接完成后,目标字符串dest的末尾会添加一个新的结束符'\0'。如果源字符串的长度小于n,则strncat函数会将整个源字符串连接到目标字符串的末尾。

2.strncat的使用

当src的字符串长度>n

int main()
{
	char str1[20] = "hello";
	char str2[20] = "hello world";
	strncat(str1, str2, 5);
	printf("%s\n", str1);
	return 0;
}

输出结果为:hellohello

当src的字符串长度<=n

int main()
{
	char str1[20] = "hello";
	char str2[20] = "hello world";
	strncat(str1, str2, 15);
	printf("%s\n", str1);
	return 0;
}

输出结果为:hellohello world

3.strncat的模拟实现

思路:先找到目标字符串的末尾位置,然后将源字符串的前n个字符逐个追加到目标字符串的末尾,并更新目标字符串的长度

代码如下:

char* my_strncat(char* dest, const char* src, int n)
{
	char* ret = dest;		//将dest首地址储存在ret中
	assert(dest && src);	//保证dest、src非空
	while (*dest != '\0')	//找到dest结尾的'\0'
	{
		dest++;
	}
	while (n && (*dest++ = *src++))//把src里的字符一个个放入dest后
	{
		n--;                //跳出循环的条件
	}
	if (n == 0)
	{
		*dest = '\0';		//如果n<src
	}
	return ret;				//返回dest字符串起始地址
}

int main()
{
	char str1[20] = "hello ";
	char str2[] = "world";
	my_strncat(str1, str2, 5);
	printf("%s\n", str1);
	return 0;
}

strncmp

1.strncmp的用法

strncmp 是一个字符串比较函数,用于比较两个字符串的前n个字符

函数原型为:

int strncmp(const char *str1, const char *str2, size_t n);

其中,参数的含义如下:

str1:要比较的第一个字符串

str2:要比较的第二个字符串

n:要比较的最大字符数

返回值:

如果返回值 < 0,则表示 str1 小于 str2。

如果返回值 > 0,则表示 str1 大于 str2。

如果返回值 = 0,则表示 str1 等于 str2。

2.strncmp的使用

int main()
{
	char arr1[20] = "abcdef";
	char arr2[20] = "abcddd";
	char arr3[20] = "abcdff";
	int ret1 = strncmp(arr1, arr2, 3);
	int ret2 = strncmp(arr1, arr2, 8);
	int ret3 = strncmp(arr1, arr2, 6);
	int ret4 = strncmp(arr1, arr3, 6);
	printf("%d %d %d %d\n", ret1, ret2, ret3, ret4);
	return 0;
}

输出结果为:0 1 1 -1

3.strncmp的模拟实现

思路:逐个比较两个字符串的前n个字符的ASCII值,直到遇到不同字符、遇到空字符或比较完n个字符为止,根据比较结果返回相应的整数。

代码如下:

int my_strncmp(const char* str1, const char* str2, size_t n)
{
    assert(str1 && str2);
    // 如果n为0,说明不需要比较任何字符,直接返回0,表示两个字符串的前0个字符是相同的。  
    if (n == 0)
    {
        return 0;
    }
 
    // 循环条件中,*str1 == *str2 确保两个字符相同,  
    // *str1 确保str1没有到达字符串结束符,  
    // --n 确保已经比较了n个字符或n已经减到0。  
    while (*str1 == *str2 && *str1 && --n)
    {
        // 如果两个字符相同且没有到达字符串结束符,继续比较下一个字符。  
        str1++;
        str2++;
    }
    //返回的是两个字符的ASCII码差值。  
    return *str1 - *str2;
}

int main()
{ 
    char str1[] = "abcddd";
    char str2[] = "abcdef";  
    int ret1 = my_strncmp(str1, str2, 4);
    int ret2 = my_strncmp(str1, str2, 6);
    printf("%d %d\n", ret1, ret2);
    return 0;
}

strstr

1.strstr的用法

strstr用于查找一个字符串 str2 是否存在于另一个字符串 str1 中,并返回 str2 在 str1 中首次出现的位置。以下是关于strstr函数的详细介绍。

函数原型为:

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

其中,参数的含义如下:

str1:这是主串,即你希望在其中查找子串的字符串。

str2:这是子串,即你希望在主串中查找的字符串。

返回值:

如果str2是str1的子串,strstr返回一个指向str1中str2首次出现的位置的指针。

如果str2不是str1的子串,strstr返回NULL。

2.strstr的使用

int main() 
{
    const char* str1 = "hello world";
    const char* str2 = "world";
    char* result = strstr(str1, str2);

    if (result != NULL) 
    {
        printf("找到了,子字符串为:%s\n", result);
    }
    else 
    {
        printf("找不到\n");
    }
    return 0;
}

输出结果为:找到了,子字符串为:world

3.strstr的模拟实现

思路:通过遍历主串,依次比较每个位置开始的子串是否与目标子串匹配,直到找到匹配或遍历完整个主串

代码如下:

char* my_strstr(const char* str1, const char* str2)
{
	const char* s1 = NULL;	//用于遍历str1
	const char* s2 = NULL;	//用于遍历str2
	const char* cp = str1;	//指向str1的起始地址
	//检查传入的第二个字符串str2是否为空字符串(即只包含一个结束字符\0)
	//如果str2是空字符串,函数会立即返回str1的起始地址。
	if (*str2 == '\0')
		return (char*)str1;
	//遍历str1的字符
	while (*cp)
	{
		s1 = cp;		//初始化s1为当前cp的位置
		s2 = str2;		//初始化s2为当前str2的位置
		//s1和s2均为到达末尾,且字符相等,则继续比较
		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
		{
			s1++;		//s1向后移动一位
			s2++;		//s2向后移动一位
		}
		//如果s2到达str2末尾,说明str2在str1中从cp位置被找到
		if (*s2 == '\0')
		{
			return (char*)cp;	//返回找到的位置的指针
		}
		cp++;			//没找到则继续遍历str1
	}
	return NULL;		//遍历完没找到则返回NULL
}

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "eg";
	char* ret = my_strstr(arr1, arr2);
	if (ret == NULL)
		printf("找不到\n");
	else
		printf("%s\n", ret);
	return 0;
}

strtok

1.strtok的用法

strtok用于分割字符串,该函数的主要目的是将字符串按照指定的分隔符切分成多个子串,并返回指向这些子串的指针。

函数原型为:

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

其中,参数的含义如下:

str:在第一次调用时,str是传入需要被切割字符串的首地址;在后续调用时,str应设置为NULL,以便strtok函数能够在之前的位置基础上继续查找下一个标记。

sep:这是一个字符串,它定义了用作分隔符的字符集合。字符串中的每个字符都会被当作一个分割符。

工作机制:

1.strtok在str字符串中查找由sep字符串定义的分隔符。当找到分隔符时,strtok会将其替换为\0字符,从而结束当前子串。

2.strtok返回指向当前找到的子串的指针。这个子串是从str开始,到当前找到的分隔符(现在已替换为\0)结束的部分。

3.在第一次调用之后,strtok会保存其在str中的位置,以便在后续调用时能够继续查找下一个子串。因此,在后续的调用中,需要将str参数设置为NULL。

4.如果字符串中不存在更多的分隔符,strtok将返回NULL指针。

需要注意的是,strtok函数会破坏被分解字符串的完整性。调用strtok后,原字符串str的内容会被修改,因为它会将分隔符替换为\0。因此,通常建议对原始字符串进行拷贝,并在拷贝的字符串上使用strtok,以避免修改原始数据。

2.strtok的使用

int main()
{
	char arr1[] = "114514.2314.666";
	char* sep = ".";
	char* str = NULL;
	for (str = strtok(arr1, sep); str != NULL; str = strtok(NULL, sep))
	{
		printf("%s\n", str);
	}
	return 0;
}

输出结果为:

114514

2314

666

我们来分析一下:

114514.2314.666

使用strtok函数后,它会在在找到分隔符的位置后,将那个位置的字符设置为\0来结束当前的标记

然后从上一次标记的地方开始找下一个标记,直至结束

简单来说,就是"strtok会把目标字符串中间的符号作为分隔符,将目标字符串分为几个子串"

strerror

1.strerror的用法

在不同的系统和C语⾔标准库的实现中都规定了⼀些错误码,⼀般是放在 errno.h 这个头⽂件中说明的,C语⾔程序启动的时候就会使⽤⼀个全⾯的变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表⽰没有错误,当我们在使⽤标准库中的函数的时候发⽣了某种错误,就会讲对应的错误码,存放在errno中,⽽⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。

函数原型为:

char* strerror(int errnum);

2.strerror的使用

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

输出结果为:

No error(没有错误。这通常表示操作成功,没有发生任何错误)

Operation not permitted(操作不允许。这表明尝试进行的操作由于权限不足或其他限制而未能执行)

No such file or directory(没有这样的文件或目录。这通常意味着程序试图访问一个不存在的文件或目录)

No such process(没有这样的进程。这通常发生在尝试运行或管理一个不存在的进程时)

Interrupted function call(被中断的函数调用。这可能是由于某些外部因素(如用户中断)导致正在执行的函数被突然终止)

Input/output error(输入/输出错误。这通常发生在读取或写入文件或其他设备时,可能是由于设备故障、文件系统损坏或其他问题导致的)

No such device or address(没有这样的设备或地址。这通常表示程序试图访问一个不存在的设备或网络地址)

Arg list too long(参数列表过长。这通常发生在命令行参数超过系统限制时)

Exec format error(执行格式错误。这通常意味着尝试执行的程序或文件格式不正确或不被支持)

Bad file descriptor(错误的文件描述符。这通常表明程序尝试使用了一个无效或未正确初始化的文件描述符)

结束语

字符函数和字符串函数的内容就先讲到这里,下一篇文章我们将会学习内存操作函数

看到这里的友友们,感谢你们的支持,

求个点赞收藏加关注!!!

十分感谢!!!

相关推荐
小_太_阳3 分钟前
Scala_【1】概述
开发语言·后端·scala·intellij-idea
向宇it3 分钟前
【从零开始入门unity游戏开发之——unity篇02】unity6基础入门——软件下载安装、Unity Hub配置、安装unity编辑器、许可证管理
开发语言·unity·c#·编辑器·游戏引擎
古希腊掌管学习的神40 分钟前
[LeetCode-Python版]相向双指针——611. 有效三角形的个数
开发语言·python·leetcode
赵钰老师40 分钟前
【R语言遥感技术】“R+遥感”的水环境综合评价方法
开发语言·数据分析·r语言
就爱学编程1 小时前
重生之我在异世界学编程之C语言小项目:通讯录
c语言·开发语言·数据结构·算法
Oneforlove_twoforjob1 小时前
【Java基础面试题025】什么是Java的Integer缓存池?
java·开发语言·缓存
emoji1111111 小时前
前端对页面数据进行缓存
开发语言·前端·javascript
每天都要学信号1 小时前
Python(第一天)
开发语言·python
TENET信条1 小时前
day53 第十一章:图论part04
开发语言·c#·图论
北国无红豆1 小时前
【CAN总线】STM32的CAN外设
c语言·stm32·嵌入式硬件