C语言——字符串函数

这篇博客详细介绍了C语言中的字符串处理函数,包括strlen、strcpy、strcat、strcmp、strncpy、strncat、strncmp、strstr、strtok和strerror的使用、注意事项及模拟实现。强调了字符串以''作为结束标志的重要性,以及函数使用中的潜在问题和解决办法。

前言

C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在

常量字符串 中或者 字符数组 中。

字符串常量 适用于那些对它不做修改的字符串函数.

本文重点介绍处理字符和字符串的库函数的使用和注意事项

  • 求字符串长度

        strlen

  • 长度不受限制的字符串函数

        strcpy strcat strcmp

  • 长度受限制的字符串函数介绍

        strncpy strncat strncmp

  • 字符串查找

        strstr strtok

  • 错误信息报告

        strerror

  • 字符操作 内存操作函数

        memcpy memmove memset memcmp

1. 函数介绍

1.1  strlen

1.1.1 使用

size_t strlen ( const char * str );计算字符串 str 的长度,直到空结束字符('\0'),但不包括空结束字符。

请看代码:

#include<string.h>//使用strlen库函数的头文件
#include<stdio.h>
int main()
{
	char str[] = "abcdef";
	printf("str长度:%d\n", strlen(str));
	return 0;
}

运行结果:

1.1.2 注意事项:

  1. 字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。正如上面的代码一样,字符数组str中包含“abcdef\0”这些字符,所以求出长度:6。如图:

  2. 参数指向的字符串必须要以 '\0' 结束。如这段代码:
    char str[] = { 'a','b','c','d','e','f' };
    printf("str长度:%d\n", strlen(str));

    这段的运行结果:

     为什么是 19 呢?为什么不是 6 了呢?我们调试起来发现:

    字符数组中没有‘\0’的存在,所以函数会顺着这个地址一直找,直到找到'\0'为止,而我们不知道’\0'到底在哪?,19只是一个随机的值,是在这个位置正好有个'\0'存在。这就是 为什么不是 6的原因。

  3. 注意函数的返回值为size_t,是无符号的。比如:
    #include <stdio.h>
    #include<string.h>
    int main()
    {
         const char*str1 = "abcdef";
         const char*str2 = "bbb";
         if(strlen(str2)-strlen(str1)>0)
         {
             printf("str2>str1\n");
         } 
         else
         {
             printf("srt1>str2\n");
         }
         return 0;
    }

    运行结果:

     我们知道str1的长度:6  ;str2的长度:3;那为什么 3-6 的结果 >0 呢?这就要注意:strlen函数返回的类型是无符号整型,不是有符号的!无符号的 3-6 运算结果照样是无符号类型,所以得出 >0的结果。

1.1.3 模拟实现

我们已经知道strlen函数 是如何使用的了,传参,返回值也已经了解。下面我们来自己实现一下这个函数。

我们知道strlen函数是以'\0'为结束标准的,那么我们就有以下几种实现方法。

代码如下:

//模拟实现strlen
#include<string.h>
#include<stdio.h>
#include<assert.h>
//方法一:创建临时变量
size_t my_strlen(const char* str)
{
	assert(str);
	size_t count = 0;
	//找到\0时停止
	while (*str++)
	{
		count++;
	}
	return count;
}
//方法二:递归
size_t my_strlen(const char* str)
{
	assert(str);
	//找到\0时停止,返回0
	if (*str == '\0')
		return 0;
	else
		return 1 + my_strlen(str + 1);
}
//方法三:指针减指针
size_t my_strlen(const char* str)
{
	assert(str);
	char* sign = (char*)str;
	//找到\0时停止
	while (*str)
	{
		str++;
	}
	return str - sign;
}
int main()
{
	char arr[] = "abcdefg";
	printf("%d\n", my_strlen(arr));
	return 0;
}

1.2 strcpy

1.2.1 使用

strcpy函数是将源字符串拷贝到目标字符串当中的函数,返回类型是char*,也就是一个指向最终的目标字符串的指针。下面是 strcpy() 函数的声明:

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

  • destination -- 指向用于存储复制内容的目标数组。
  • source -- 要复制的字符串。

请看示例:

#include <stdio.h>
#include <string.h> 
int main ()
{
  char str1[]="Sample string";
  char str2[40];
  char str3[40];
  strcpy (str2,str1);
  strcpy (str3,"copy successful");
  printf ("str1: %s\nstr2: %s\nstr3: %s\n",str1,str2,str3);
  return 0;
}

让我们编译并运行上面的程序,这将产生以下结果:

 1.2.2 注意事项:

  • 源字符串必须以 '\0' 结束。
  • 会将源字符串中的 '\0' 拷贝到目标空间。

     我们来看一段代码:

    int main()
    {
    	char arr1[20] = "xxxxxxxxxxxxx";
    	char arr2[] = "ghijkl";
    	strcpy(arr1, arr2);
    	printf("%s\n", arr1);
    	return 0;
    }

    我们通过编译器调试起来发现:

  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。

 1.2.3 模拟实现

思路:通过两个指针接受两个字符串数组的首元素地址,str1接受目标字符串地址,str2接受要拷贝的源字符串的地址。因为str2不需要改变,为了防止出错,用const限制。利用指针将源字符串拷贝到目标字符串中,同时将'\0'也拷贝过去,同时创建临时变量来记录目标字符串的起始位置,方便返回。

代码如下:

//模拟实现strcpy
#include<string.h>
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* str1, const char* str2)
{
	assert(str1);
	assert(str2);
	char* sign = str1;
    //利用后置++的特性(先使用后++)将'\0'也拷贝过去
    //当*str2为'\0'时  *str1 = *str2的表达式的值为0;所以循环停止。
	while (*str1++ = *str2++)
	{
		;
	}
	return sign;
}
int main()
{
	char arr1[20] = "xxxxxxxxxxxxx";
	char arr2[] = "ghijkl";
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

1.3 strcat

1.3.1使用

strcat库函数 char *strcat(char *dest, const char *src) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。

下面是 strcat() 函数的声明。

char *strcat(char *dest, const char *src)

  • dest -- 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串。
  • src -- 指向要追加的字符串,该字符串不会覆盖目标字符串。

该函数返回一个指向最终的目标字符串 dest 的指针。

示例:

int main()
{
	char arr1[20] = "abc";
	char arr2[] = "def";
	printf("%s\n", strcat(arr1, arr2));
	return 0;
}

运行结果:

 1.3.2 注意事项

  • 源字符串必须以 '\0' 结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。

1.3.3 模拟实现

思路:我们发现, strcat 函数是将源字符串追加在目标字符串的末尾('\0'处),那我们就可以这样实现:

//模拟实现strcat
#include<string.h>
#include<stdio.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{
	char* ret = dest;//记录目标字符串起始位置
	//找到目标字符串'\0'的位置
	while (*dest != '\0')
	{
		dest++;
	}
	//从'\0'的位置开始追加
	while (*dest++ = *src++)
	{
		;
	}
	//返回目标起始位置
	return ret;
}

实现之后,有人就会想,那可不可以用这个函数来实现字符串自己给自己追加呢?

我们操作后会发现,会产生死循环导致程序崩溃。为什么呢?

我们用一张图来解释他:

比如说:我们传"bit"这个字符串进去,就会产生这种情况:

 1.4  strcmp

1.4.1 使用

strcmp是用来比较两个字符串的大小的。那么如何使用呢?请看:

C 库函数 int strcmp(const char *str1, const char *str2) 把 str1 所指向的字符串和 str2 所指向的字符串进行比较。

下面是 strcmp() 函数的声明。

int strcmp(const char *str1, const char *str2)
  • str1 -- 要进行比较的第一个字符串。
  • str2 -- 要进行比较的第二个字符串。

该函数返回值如下:

  • 如果返回值小于 0,则表示 str1 小于 str2。
  • 如果返回值大于 0,则表示 str1 大于 str2。
  • 如果返回值等于 0,则表示 str1 等于 str2。

示例:

int main()
{
	char str1[]= "abcdef";
	char str2[]= "abCdef";
	int ret = strcmp(str1, str2);
	if (ret < 0)
	{
		printf("str1 小于 str2");
	}
	else if (ret > 0)
	{
		printf("str1 大于 str2");
	}
	else
	{
		printf("str1 等于 str2");
	}
	return(0);
}

 运行结果:

 1.4.2 注意事项

须注意一点:字符串比较是比较每个字符的ASCII码值,

 ANSI 标准规定,返回值为正数,负数,0 。而确切数值是依赖不同的C实现的。当两个字符串不相等时,C 标准没有规定返回值会是 1 或 -1,只规定了正数和负数。有些会把两个字符的 ASCII 码之差作为比较结果由函数值返回。但无论如何不能以此条依据作为程序中的流程逻辑。

意思就是,你不能通过判断返回值是 1或-1 来以此判断字符串的大小关系。

1.4.3 模拟实现

我们了解到,字符串比较是比较每个字符的ASCII码值。所以我们可以这样实现,

思路:同时遍历两个字符串,找到不同的字符,返回两者的ASCII码值的差值。如果,找不到不同的字符,说明字符串相等,那么判断是否遍历完整个字符串,返回0值。

//模拟实现strcmp
#include<string.h>
#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
    //断言(判断空指针)
	assert(str1);
	assert(str2);
	//找到不同点
	while (*str1 == *str2)
	{
		//或找到'\0',相等return 0
		if (*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
	}
	//大于返回>0的值,小于返回<0的值
	return *str1 - *str2;
}

1.5 strncpy

1.5.1 使用

C 库函数 char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。

下面是 strncpy() 函数的声明。

char *strncpy(char *dest, const char *src, size_t n)
  • dest -- 指向用于存储复制内容的目标数组。
  • src -- 要复制的字符串。
  • n -- 要从源中复制的字符数。

该函数返回最终复制的字符串。

示例:

#include <stdio.h>
#include <string.h>
int main()
{
	char src[40] = { 0 };
	char dest[12] = { 0 };
	strcpy(src, "This is runoob.com");
	strncpy(dest, src, 10);
	printf("最终的目标字符串: %s\n", dest);
	return(0);
}

运行结果:

与strcpy比较,strncpy在功能不变的基础上,增加了可控的个数,是我们想复制几个就复制几个。

 下面的函数亦是如此。

1.6 strncat

1.6.1 使用

C 库函数 char *strncat(char *dest, const char *src, size_t n) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。

下面是 strncat() 函数的声明。

char *strncat(char *dest, const char *src, size_t n)
  • dest -- 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串,包括额外的空字符。
  • src -- 要追加的字符串。
  • n -- 要追加的最大字符数。

该函数返回一个指向最终的目标字符串 dest 的指针。

示例:

#include <stdio.h>
#include <string.h>
int main()
{
    char dest[20] = "abcdef";
    char src[] = "ghijklmn";
    strncat(dest, src, 5);
    printf("  最终的目标字符串: %s", dest);
    return(0);
}

运行结果:

1.7 strncmp

1.7.1 使用

C 库函数 int strncmp(const char *str1, const char *str2, size_t n) 把 str1 和 str2 进行比较,最多比较前 n 个字节。

下面是 strncmp() 函数的声明。

int strncmp(const char *str1, const char *str2, size_t n)
  • str1 -- 要进行比较的第一个字符串。
  • str2 -- 要进行比较的第二个字符串。
  • n -- 要比较的最大字符数。

该函数返回值如下:

  • 如果返回值 < 0,则表示 str1 小于 str2。
  • 如果返回值 > 0,则表示 str1 大于 str2。
  • 如果返回值 = 0,则表示 str1 等于 str2。

示例:

#include<string.h>
#include<stdio.h>
int main()
{
	char str1[] = "abcDEF";
	char str2[] = "abcdef";
	int ret = strncmp(str1, str2, 3);
	if (ret < 0)
	{
		printf("  str1 小于 str2\n");
	}
	else if (ret > 0)
	{
		printf("  str2 小于 str1\n");
	}
	else
	{
		printf("  str1 等于 str2\n");
	}
	return 0;
}

运行结果:

这是因为我们只比较了前三个字符。 

1.8 strstr

1.8.1 使用

strstr函数是用来在一个字符串中查找另一个字符串的函数。C 库函数 char *strstr(const char *haystack, const char *needle) 在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 '\0'。

下面是 strstr() 函数的声明。

char *strstr(const char *haystack, const char *needle)
  • haystack -- 要被检索的 C 字符串。
  • needle -- 在 haystack 字符串内要搜索的小字符串。

该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。

示例:

int main()
{
	char haystack[] = "RUNOOB";
	char needle[] = "NOOB";
	char* ret;
	ret = strstr(haystack, needle);
	printf("子字符串是: %s\n", ret);
	return(0);
}

运行结果:

 1.8.2 模拟实现

思路:

代码如下:

//模拟实现strstr
#include<string.h>
#include<stdio.h>
#include<assert.h>
char* my_strstr(const char* str1, const char* str2)
{
	assert(str1);
	assert(str2);
	//创建三个临时指针变量
	//因为我们不想对str1,str2进行改动
	char* p1 = NULL;
	char* p2 = NULL;
	char* cp = (char*)str1;//利用cp代替目标字符串
//遍历整个目标字符串
	while (*cp)
	{
		p1 = cp;
		p2 = (char*)str2;//将要查找的子串的起始位置传入p2
		//找到相同的第一个字符,两个指针同时后移
		while ( *p2 && *p1 == *p2)//当子串遍历完或者有不相等的字符时停止循环
		{
			p1++;
			p2++;
		}
		//判断是否子串遍历完
		if (*p2 == '\0')
		{
			//找到,返回首次找到子串的位置
			return cp;
		}
		//从起始位置的下一个字符开始,再次查找。
		cp++;
	}
	//找不到返回空指针
	return NULL;
}

1.9 strtok

1.9.1 使用

strtok是一个分割字符串的函数;

C 库函数 char *strtok(char *str, const char *delim) 分解字符串 str 为一组字符串,delim 为指向分隔符的集合的指针。

下面是 strtok() 函数的声明。

char *strtok(char *str, const char *delim)
  • str -- 要被分解成一组小字符串的字符串。
  • delim -- 包含分隔符的 C 字符串。

该函数返回被分解的第一个子字符串,如果没有可检索的字符串,则返回一个空指针。

示例:

int main()
{
	char arr[] = "sun&fu*zheng";
	char c[20] = { 0 };
	strcpy(c, arr);//拷贝一份要分割的字符串
	char* p = "&*";//分隔符集合
	char* str = NULL;
	for (str = strtok(c, p); str != NULL; str = strtok(NULL, p))
	{
		printf("%s\n", str);
	}
	return 0;
}

使用str 来接受分割出来的子字符串,注意:该函数会将分隔符位置更改为'\0',同时记录下一个子字符串的开头。所以第一次传进要切割的字符串后,没有完全切割时,只需传入NULL 空指针,使它继续切割保存的字符串即可。

for (str = strtok(c, p); str != NULL; str = strtok(NULL, p))
	{
		printf("%s\n", str);
	}

运行结果:

 1.10 strerror

1.10.1 使用

strerror函数可以读取当前的错误信息。

C 库函数 char *strerror(int errnum) 从内部数组中搜索错误号 errnum,并返回一个指向错误消息字符串的指针。strerror 生成的错误字符串取决于开发平台和编译器。

下面是 strerror() 函数的声明。

char *strerror(int errnum)
  • errnum -- 错误号,通常是 errno

该函数返回一个指向错误字符串的指针,该错误字符串描述了错误 errnum。

示例:

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
    FILE* fp;
    fp = fopen("file.txt", "r");//用只读的方式打开一个文件
    if (fp == NULL)
    {
        printf("  Error: %s\n", strerror(errno));//读取错误信息
    }
    return(0);
}

运行结果:

 错误信息:没有文件或者打开失败

这是因为我们尝试打开一个不存在的文件。

小结

通过这些字符串函数,我们就能更好的操作字符串。

感谢各位的观看!再见,谢谢!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值