字符串和字符函数

在C语言里有string.h这个头文件,但是C语言里没有string这个类型。字符串通常放在常量字符串中或者字符数组中,字符串常量适用于那些对她不做修改的字符串函数。string.h这个头文件里声明的函数原型也全是针对char数组的种种操作。直到C++中才出现了string这个类。这篇文章就简单整理一下C语言中处理字符、字符串的库函数,以及一些函数的简单实现。

1.strlen

size_t strlen ( const char * str );

功能:计算字符串长度,不包含’\0’
返回值:返回字符串的字符数
说明:

(1)字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )。

(2)参数指向的字符串必须要以 '\0' 结束。

(3)注意函数的返回值为size_t,是无符号的( 易错 )

注:size_t 是一些C/C++标准在stddef.h中定义的,size_t 类型表示C中任何对象所能达到的最大长度,它是无符号整数。

strlen的实现方式也不难,如下所示:

//方法一:
int my_strlen(const char * str)
{
 int count = 0;
 while(*str)
 {
  count++;
  str++;
 }
 return count;
}
//方法二:(利用递归)
int my_strlen(const char * str)
{
 if(*str == '\0')
  return 0;
 else
  return 1+my_strlen(str+1);
}
//方法三:
int my_strlen(char *s)
{
   char *p = s;
   while(*p != ‘\0’ )
       p++;
   return p-s;
}

2.strcpy

char* strcpy(char* dest,char* src)

功 能: 将参数src字符串拷贝至参数dest所指的地址
返回值: 返回参数dest的字符串起始地址
说明: 

(1)Copies the C string pointed by source into the array pointed by destination, including the
terminating null character (and stopping at that point).
(2)源字符串必须以 '\0' 结束。
(3)会将源字符串中的 '\0' 拷贝到目标空间。
(4)目标空间必须足够大,以确保能存放源字符串。

(5)目标空间必须可变。

模拟实现:

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

3.stract

char* strncat (char* dest,const char* src,size_t num)

功能: 字符串拼接
返回值:返回dest字符串起始地址
说明: 

(1)strncat将会从字符串src的开头拷贝n个字符到dest字符串尾部

(2)dest要有足够的空间来容纳要拷贝的字符串

(3)如果n大于字符串src的长度,那么仅将src全部追加到dest的尾部

(4)strncat会将dest字符串最后的’\0’覆盖掉,字符追加完成后,再追加’\0’

模拟实现:

char *my_strcat(char *dest, const char*src)
{
 char *ret = dest;
 assert(dest != NULL);
 assert(src != NULL);
 while(*dest)
 {
  dest++;
 }
 while((*dest++ = *src++))
 {
  ;
 }
 return ret;
}

4.strcmp

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

 功能:字符串比较
返回值:若参数s1和s2字符串相同则返回0,s1若大于s2则返回大于0的值,s1若小于s2则返回小于0的值

模拟实现:

int my_strcmp(const char* str1, const char* str2)//加上const是为了保证函数过程中*str的值不被其他语句修改
{
    assert(str1 && str2);//此处是为了保证这两个指针均不为空指针(同为空指针时系统会崩掉)
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
	}
	return *str1 -*str2;
}

5.strncpy、strncat、strncmp

1)strncpy

char* strncpy(char* dest,const char* src,size_t num)

功能:拷贝src字符串的前num个字符至dest
返回值:dest字符串起始地址
说明:

(1)如果src字符串长度小于num,则拷贝完字符串后,在目标后追加0,直到num个
(2)strncpy不会向dest追加’\0’
(3)src和dest所指的内存区域不能重叠,且dest必须有足够的空间放置n个字符
2)strncat

char* strncat (char* dest,const char* src,size_t num)

 

功能:将n个字符追加到字符串结尾
返回值:返回dest字符串的起始地址
说明:

(1)strncat将会从字符串src的开头拷贝n个字符到dest字符串尾部
(2)dest要有足够的空间来容纳要拷贝的字符串
(3)如果n大于字符串src的长度,那么仅将src全部追加到dest的尾部
(4)strncat会将dest字符串最后的’\0’覆盖掉,字符追加完成后,再追加’\0’

3)strncmp

int strncmp(const char* str1,const char* str2,size_t num)

功能:指定长度大小比较
返回值:同strcmp

6.strstr

前面提到的字符串函数在没学习这一块之前或多或少接触过,后面这一块可能有点陌生了,但是也很重要。

char* strstr(const char* str,const char* substr)

功能:检索子串在字符串中首次出现的位置
返回值:返回字符串str中第一次出现子串substr的地址;如果没有检索到子串,则返回NULL

对于这个函数可以简单实现,方法就是暴力查找,当然也有更好的方法,这里先不提及。

char *my_strstr(const char *str1,const char *str2)
{
    assert(str1&&str2);
    if(*str2=='\0')
    return str1;
    const char *s1=NULL;
    const char *s2=NULL;
    const char *cp=str1;
    while(*cp)
    {
        s1=cp;
        s2=str2;
        while(*s1!='\0'&&*s2!='\0'&&*s1==*s2)
        {
            s1++;
            s2++;
        }
        if(*s2=='\0')
            return (char*)cp;
        cp++;
    }
    return NULL;
}

 7.strtok

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

功能:根据分隔符将字符串分隔成一个个片段
返回值:返回下一个分割后的字符串指针,如果已无从分割则返回NULL
说明:
(1)sep参数是个字符串,定义了用作分隔符的字符集合
(2)第一个参数指定一个字符串,它包含了一个或者多个由sqp字符串中一个或者多个字符分割的标记
(3)第一次调用时将字符串首地址传进去,之后调用不用传地址,内部会有static函数保存指向地址
(4)分隔符不作为输出内容,只做分界符
(5)当strtok在参数s的字符串中发现到参数sep的分割字符时则会将该字符改为’\0’字符
(6)在第一次调用时,strtok必须赋予参数str字符串,往后的调用则将参数s设置成NULL
(7)strtok会修改原字符串,所以必须放至栈上

使用示例:

int main()
{
    char s1[] ="yxlm@666.com";
    char sep[]="@.";
    printf("%s\n", strtok(s1,sep));
    printf("%s",strtok(NULL,sep)); 
    return 0;
}

 

8.strerror

char * strerror ( int errnum );

功能:返回指向错误信息字符串的指针
说明:

必须包含头文件errno.h

该函数会有不同的错误提示。

接下来再介绍一个与之相关的函数:perror

该函数集转换和打印功能于一身,它会自动读取之前产生的错误码,将其转换成错误信息并且将它打印出来。
示例如下:

#include <stdio.h>
int main ()
{
   FILE *fp;
   /* 首先重命名文件 */
   rename("file.txt", "newfile.txt");
   /* 现在让我们尝试打开相同的文件 */
   fp = fopen("file.txt", "r");
   if( fp == NULL ) 
   {
      perror("Error: ");
      return(-1);
   }
   fclose(fp); 
   return(0);
}

 9.memcpy

void * memcpy ( void * destination, const void * source, size_t num );

返回值:函数返回一个指向dest的指针。

说明:

  1)source和destin所指内存区域不能重叠,函数返回指向destin的指针。

  2)与strcpy相比,memcpy并不是遇到'\0'就结束,而是一定会拷贝完n个字节。

我们可以模拟实现这个函数:

通过从低地址到高地址

void my_memcpy(void *dest,void *src,size_t num)
{
    void *ret=dest;
    assert(dest&&src);
    while(num--)
    {
        *(char*)dest=*(char*)src;
        dest=(char*)dest+1;
        src=(char*)src+1;
    }
    return ret;
}

10.memmove

void * memmove ( void * destination, const void * source, size_t num );

作用:

1)和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
2)如果源空间和目标空间出现重叠,就得使用memmove函数处理

考虑这种情况,如果dst和src内存地址有重叠,且dst位于src之后,如果从前往后拷贝,那么src中和dst重叠的部分就会先被覆盖,造成拷贝错误,因此需要考虑拷贝顺序的问题:

1)如果dst在src之后,从后往前拷贝

2)如果dst在src之前,从前往后拷贝

这样便可以避免发生拷贝错误。

可以这样实现:

void *my_memmove(void *dest,void *src,size_t num)
{
    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);
        }
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值