字符串函数和内存函数

字符串基础

我们知道:
字符串就是零个或多个字符的序列,该序列以NULL'\0’)为结束标志。

常用的字符串函数有以下几类:

字符串长度

字符串的长度就是它所包含的字符个数(不包括'\0')。

求字符串长度的函数—strlen

size_t strlen ( const char * str );
//返回值size_t本质上是undigned int 类型

strlen函数返回的是在字符串中'\0'之前出现的字符个数

简单运用一下

#include<string.h>
#include<stdio.h>
int main()
{
    
    
	char arr[] = "abcdefg";
    int ret = strlen(arr);
    printf("%d\n");
	return 0;
}//打印结果是7

在这里插入图片描述

如果把字符串中的某一个字符修改成为'\0',那么得到的结果就是修改后的第一个'\0'前面的字符个数

使用strlen函数需要注意:

  • 参数传过去的字符串必须是包含'\0'这个结束标志的,否则会得到随机值

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

复制字符串—strcpy

下面是这个函数的函数原型

char * strcpy ( char * dst, const char * src );
//这个函数将src字符串复制到dst字符串中,dst字符串中原来的字符将会被覆盖掉。

简单运用:

#include<string.h>
#include<stdio.h>
int main()
{
    
    
    char str1[] = "abcdef";
    char str2[10] = {
    
    0};
    strcpy(str2,str1);//将第二个参数的内容拷贝到第一个参数中
    printf("%s",str2);
    return 0;
}
//打印的结果是abcdef

使用trcpy时我们应该知道的是:

  • 该函数拷贝结束的标志是'\0',并且'\0'也会被拷贝过去。所以和strlen函数一样,如果我们要拷贝的字符串末尾没有'\0',那么程序不会正确执行。

  • 目标空间应该足够大,如果要拷贝的字符串长度大于接受的数组长度,就会出现数组越界:

    因为strcpy函数不会判断字符数组的长度,在没有遇见'\0'字符前,会不断的进行拷贝,甚至会侵占目标数组后面的空间,从而造成了数组越界。

  • 目标空间必须可变(不能用const修饰)。

  • 该函数的返回值是目标数组首元素的地址,有利于嵌套调用。

连接字符串—strcat

该函数可以把一个字符串连接到另一个字符串的后面,函数原型:

char *strcat(char *dst,char const *src);
//第一个参数是目标字符串,第二个参数是源字符串

简单运用

#include<string.h>
#include<stdio.h>
int main()
{
    
    
    char str1[20] = "hello";
    char str2[] = "world";
    strcat(str1,str2);
    printf("%s\n",str1);
    return 0;
}
//会打印 helloworld

需要注意:

  • 和使用strcpy一样,我们应该保证目标字符数组剩余的空间足以保存整个源字符串。
  • 该函数会成功连接后会在字符串后面补上NULL
  • 该函数的返回值是目标数组首元素的地址,有利于嵌套使用。

字符串比较—strcmp

下面有两个字符串

char str1[] = "abcd";
char str2[] = "acbd";

如果我们需要比较两个字符串是否相等,我们不能这样直接比较

str1 == str2

我们有专门的函数(strcmp)去比较两个字符串,该函数会逐个地比较两个字符串的内容,直到发现不匹配为止。
该函数的原型是:

int strcmp(char const *s1,char const *s2);
//返回值类型是int
//C语言规定,如果第一个字符串小于第二个字符串,将会返回一个小于0的值
//如果第一个字符串大于第二个字符串,将会返回一个大于0的值
//如果两个字符串相等,那么将会返回0

利用该函数去比较上面提到的两个字符串:

#include<stdio.h>
#include<string.h>
int main()
{
    
    
	char str1[] = "abcd";
	char str2[] = "acbd";
	int ret = strcmp(str1, str2);
	printf("%d", ret);
	return 0;
}//打印了-1

在这里插入图片描述

  • 注意:C语言在返回值问题上只是规定了大于或者小于0,并没有具体设置返回值的数值,但是再vs环境下就被设置为-1与1(不同的环境可能返回值设置的不同)。我们在使用的时候尽量用下面的方法:
if(strcmp(str1,str2) > 0)//更合适
if(strcmp(str1,str2) < 0)//更合适
if(strcmp(str1,str2) == 1)//不推荐
if(strcmp(str1,str2) == -1)//不推荐

长度受限制的字符串函数

库里面还有一些函数,他们不以'\0'为结束标志,而是多了一个参数,以此来限定进行赋值或者比较的字符数,这样可以防止难以预测的长字符串从数组中溢出。

它们的函数原型如下:

char *strncpy(char *dst, char const *src, size_t len);//字符串复制
char *strncat(char *dst, char const *src, size_t len);//字符串连接
char *strncmp(char const *s1, char const *s2, size_t len);//字符串比较

strncpy:

strcpy相同,strncpy将源字符串的字符拷贝到目标数组,但是它只会向目标数组拷贝过去len个字符。

有以下两中情况:

1.当strlen(src)小于len时,dst数组就会用额外的'\0'字符填充到len长度:
在这里插入图片描述

2.当strlen(src)大于或者等于len时,那么只有len个字符被拷贝到dst中,这样dst数组就不会以'\0'结尾

在这里插入图片描述

出现这种情况后,str1数组就不是以\0结束,会影响很多字符串函数的使用,为了避免出现这样的情况,我们可以考虑下面这个代码段

#define MAX 5
... 
int main()
{
    
    
    char str1[MAX];
    char str2[] = "*********";
    ...
    strncpy(str1,str2,MAX);
    str[MAX-1] = '\0';
}
//如果strlen(str2) < MAX, 那么这段代码没有什么效果
//如果strlen(str2) >= MAX, 那么这段代码可以将目标数组的最后一个元素赋值为\0,保证了这是一个正常的字符串数组

strncat

strcat的功能相同,但是它总是会在结果字符串后面添加一个'\0'字符,所以我门不用担心最后是否有'\0'存在,我们在使用的时候最应该注意的是:dst数组剩下的空间应该足以放下我们需要拷贝的字符串。

strncmp

strcmp唯一的区别就是:strcmp会比较所有的字符,但是strncmp只会比较len个字符,
他们的返回值设置规则是相同的。

字符串查找函数—strstr

为了在字符串中查找一个字串,可以使用strstr函数,它的函数原型:

char *strstr(char sonst *s1,char const *s2);

该函数使用时会有三种情况:

  1. 在s1字符串中可以完整的找到s2字符串,那么函数会返回一个指向该位置的指针。
  2. 在s1字符串中不能完整的找到s2字符串,那么函数会返回一个空指针。
  3. 如果第二个参数是一个空字符串,那么函数会返回指向s1的指针。

此函数的使用

#include<stdio.h>
#include<string.h>
int  main()
{
    
    
	char str1[] = "abbbcdef";
	char str2[] = "bbc";
	char str3[] = "aab";
	char str4[2] = {
    
     0 };
	printf("%s\n", strstr(str1, str2));//情况1--找的到完整的s2
	printf("%s\n", strstr(str1, str3));//情况2--找不到完整的s3
	printf("%s\n", strstr(str1, str4));//情况3--s4是空字符串
	return 0;
}

在这里插入图片描述

切割字符串—strtok

一个字符串常常包含几个单独的部分,他们彼此被分隔开,为了单独处理这些部分,就首先要把它们从字符串中抽取出来,而strtok函数就可以完成这个任务

该函数的原型:

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

如果我们要将[email protected]分割成pikaqiu 123456 com三部分并打印出来,我们应该怎样使用这个函数呢?

在使用该函数前我们应该注意下面几点:

  • sep参数(第二个参数)是一个字符串,定义了用作分隔符的集合

    这里我们的分隔符是@.,所以我们需要定义一个指针,指向一个包含@.的字符串

    const char *p = "@.";
    
  • 第一个参数指定一个字符串,它包含了0或者多个由sep字符串中一个或者多个分隔符的标记。
    说明第一个参数就是我们需要分割的字符串[email protected] ,这样两个参数都确定下来了。

    const char *p = "@.";//参数2
    char arr[] = "[email protected]";//参数1
    strtok(arr,p);
        
    
  • strtok函数找到str中的下一个标记,并将其用’\0’结尾,返回一个指向这个标记的指针。(strtok函数会改变被操作的字符串)
    函数的第一次调用
    在这里插入图片描述

  • strtok的第一个参数:

    strtok的第一个参数为NULL,函数将找到str中的第一个标记,strtok将保存他在字符串中的位置

    strtok的第一个参数不为NULL,函数将在同一个字符串中 被保存的位置开始,查找下一个标记

    也可以理解为:
    strtok找第一个标记的时候,函数的第一个参数不是NULL
    strtok找后面的标记的时候,函数的第一个参数是NULL

  • 如果字符串中不存在更多标记,则返回NULL指针

完整的使用过程

//可以使用for循环去配合该函数
#include<stdio.h>
#include<string.h>
int main()
{
    
    
	char arr[] = "[email protected]";
	char* p = "@.";
	char* str = NULL;
	for (str = strtok(arr, p); str != NULL; str = strtok(NULL, p))
	{
    
    //第一次使用的时候第一个函数不是NULL,后面每次调用该函数第一个参数都是NULL
		printf("%s\n", str);
	}
	return 0;
}

在这里插入图片描述

错误信息查找报告strerror

当调用一些函数,请求操作系统执行一些功能的时候,如果出现错误,操作系统是通过设置一个外部的整形变量errno进行错误代码报告的,strerror函数把其中的一个错误代码作为参数并返回一个指向用于描述错误的字符串的指针,这个函数的原型如下:

char *strerror(int error_num);

C语言中规定了一些错误信息,,一个错误码对应一个错误信息,这个函数就是把错误码翻译成错误信息。

有以下的错误信息

在这里插入图片描述

举个实际例子:

//C语言可以操作文件,fopen函数可以打开文件。
//当库函数使用的时候,发生错误时会把errno这个全局变量设置为本次错误的错误码。
//errno时C语言提供的一个全局变量,可以直接使用,放在errno.h文件中
#include<string.h>
#include<stdio.h>
#include<errno.h>
int main()
{
    
    
    //打开文件
   FILE*pf =  fopen("test.txt","r");//实际上没有这个文件,所以在打开文件的时候会报错。
    //fopen函数第一个参数是文件名,第二个参数是打开方式,该函数如果调用失败会返回一个空指针
    if(NULL == pf)
    {
    
    
        printf("%s",strerror());
    }
    //读文件
    //...
    //关闭文件
    fclose(pf);
    pf = NULL;
	return 0;
}

//结果会打印出: no such file or directory

2.字符操作

C语言标准库包含了两组函数,用于操作单独的字符,他们的原型位于头文件ctype.h中,一组函数用于字符分类,一组函数用于字符转换。

对字符分类的函数

每一个分类函数接收一个包含字符值的整形参数。函数测试这个字符并返回一个整型值,表示真或者假

函数 如果他的参数符合下列条件就返回真(不满足条件就返回0)
iscntrl 任何控制字符
isspace 空白字符:空格、换页、换行、回车、制表符、垂直制表符
isdigit 十进制数字0~9
isxdigit 十六进制数字,包括大小写字母A~F以及所有的十进制数字
islower 小写字母a~z
isupper 大写字母A~Z
isalpha 所有的字母
isalnum 所有的字母或者数字
ispunct 标点符号,任何不属于数字或者字母的图形字母(可打印)
isgraph 任何图形字符
isprint 任何科打印字符

分类函数使用熟练可以很轻松的应对需要区别字符的情况,下面是几个例子:

eg1:

#include<ctype.h>
#include<stdio.h>
int main()
{
    
    
    printf("%d\n",isspace(' '));//打印8
    printf("%d\n",isspace('!'));//打印0
    return 0;
}

eg2:

#include<ctype.h>
#include<stdio.h>
int main()
{
    
    
    char ch = 'w';
    if(isspace(ch))
    {
    
    //是空白字符得情况
        
    }
    else
    {
    
    //不是空白字符的情况
        
    }
    return 0;
}

使用该函数可以有效提高代码的可移植性,例如:

//考虑这样的一个代码,它试图测试`ch`是否是一个大写字符
if(ch >= 'A' && ch <= 'Z')
    /*这条语句在使用ascll字符集的机器上可以运行,但是在使用ebcdic字符集的机器上就不能使用,所以我们可以考虑这样的语句:*/
if(isupper(ch))
    //无论机器是使用的哪种字符集,都可以顺利运行
转换函数

有两个转换函数,分别是:
1.toupper,可以将小写字母转换成大写字母
2.tolower,可以将大写字母转换成小写字母

它们的函数原型是:

int toupper (int ch);
int tolower (int ch);

toupper函数返回参数对应的大写形式,tolower函数返回参数对应的小写形式,如果参数是一个处于适当大小写状态的字符,函数就不会修改参数,而是直接返回。

简单的运用:

//转换大小写
#include<stdio.h>
#incldue<ctype.h>
int main()
{
    
    
    char ch = 0;
    ch = getchar();
    if(islower(ch))
    {
    
    
        ch = toupper(ch);
    }
    else
    {
    
    
        ch = tolower(ch);
    }
    printf("%c\n",ch);
    return 0;
}

3.常用内存函数

根据定义,字符串由一个NULL字节结尾,所以字符串内部不能包含任何NULL字符,但是,非字符串数据内部包含零值的情乱并不罕见,我们不能使用字符串函数来处理这种类型的数据,因为他们遇到第一个NUL字节就会停止工作,

所以我们可以使用另外一组相关的函数,他们的操作和字符串函数类似,但是这些函数可以处理任意的字节序列,下面介绍这几个函数:

memcpy:

该函数和strcpy的区别就是该函数可以处理任意类型的数据

该函数的原型是:

void *memcpy(void *dst,const void *src, size_t num);
//第三个参数的单位是字节

该函数的简单运用

#include<stdio.h>
#include<string.h>
int main()
{
    
    
	int arr[] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = {
    
     0 };
	memcpy(arr2, arr, sizeof(int)*5);
	int i = 0;
	for (i = 0; i < 5; i++)
	{
    
    
		printf("%d\t", arr2[i]);
	}
	
	return 0;
}
//打印出1	2	3	4	5

memmove

函数原型是

void *memmove(void *dst,const void *src, size_t num);
//第三个参数单位是字节

memmove的功能和memcpy差不多,只是它的原操作数和目标操作数可以重叠。

memcmp

这个函数和strcmp比较相似,但是该函数可以比较任意类型的数据,并且限定了比较的字节数.

简单的运用:

int main()
{
    
    
    int arr1[] = {
    
    1,2,3,4,5};
    int arr2[] = {
    
    1,2,3,4,5};
    int ret = memcmp(arr1,arr2,8);
    printf("%d",ret);
    return 0;
}//打印了0

memset

该函数可以对指定的内存空间赋值。

该函数原型是:

void *memset(void *dst,int c,size_t num);
//第一个参数是指向需要设置的空间的指针
//第二个参数是被设置的值
//第三个参数是指需要设置的空间大小(单位是字节)

该函数的运用

#include<stdio.h>
int main()
{
    
    
    int arr[10] = {
    
    1,2,3,4,5,6,7,8,9,10};
    //在vs环境中(小端排序),该数组在内存中是这样排列的
    //01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 .....
    memset(arr,0,10);
    //将前面十个字节修改为0
    //新的排列:
    //00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 .....
    //所以前面三个元素的值变成了0
    int i = 0;
    for(i = 0; i < 10; i++)
    {
    
    
        printf("%d\t",arr[i]);
    }
    return 0;
}
//打印了0	0	0	4	5	6	7	8	9	10

`

这个函数和strcmp比较相似,但是该函数可以比较任意类型的数据,并且限定了比较的字节数.

简单的运用:

int main()
{
    
    
    int arr1[] = {
    
    1,2,3,4,5};
    int arr2[] = {
    
    1,2,3,4,5};
    int ret = memcmp(arr1,arr2,8);
    printf("%d",ret);
    return 0;
}//打印了0

猜你喜欢

转载自blog.csdn.net/cainiaochufa2021/article/details/122966518