资讯专栏INFORMATION COLUMN

【C语言进阶】字符串&内存函数

pingan8787 / 3364人阅读

摘要:字符串常量适用于那些对它不做修改的字符串函数。同时,语言提供了一系列库函数来对操作字符串,这些库函数都包含在头文件中。目标空间必须足够大,以确保能存放源字符串。拷贝个字符从源字符串到目标空间。

前言:

        字符串是一种非常重要的数据类型,但是C语言不存在显式的字符串类型,C语言中的字符串都以字符串常量的形式出现或存储在字符数组中。字符串常量适用于那些对它不做修改的字符串函数。同时,C 语言提供了一系列库函数来对操作字符串,这些库函数都包含在头文件 string.h 中。 

目录

一、求字符串长度

strlen

strlen函数的模拟实现

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

strcpy

strcpy函数的模拟实现

strcat

strcat函数的模拟实现

strcmp

strcmp函数的模拟实现

三、长度受限制的字符串函数

strncpy

strncpy函数的模拟实现

strncat

strncat函数的模拟实现

strncmp

strncmp函数的模拟实现

四、字符串查找

strstr

strstr函数的模拟实现

strtok

strtok函数的模拟实现

五、错误信息报告

strerror

六、字符操作

字符分类函数

 字符转换

七、内存操作函数

memcpy

memcpy函数的模拟实现

memmove

memmove函数的模拟实现

memset

memcmp



一、求字符串长度

strlen

size_t strlen ( const char * str );
  • 字符串已经 "/0" 作为结束标志,strlen函数返回的是在字符串中 "/0" 前面出现的字符个数(不包含 "/0" )
  • 参数指向的字符串必须要以 "/0" 结束
  • 注意函数的返回值为size_t,是无符号的( 易错 )
  • 学会strlen函数的模拟实现

源字符串必须以 "/0" 结束。 会将源字符串中的 "/0" 拷贝到目标空间。 目标空间必须足够大,以确保能存放源字符串。 目标空间必须可变。 学会模拟实现。

例1:

#include #include int main(){    int len1 = strlen("abcdef");    printf("%d/n", len1); //6    char arr[] = {"a","b","c","d","e","f"};    //错误写法    int len2 = strlen(arr);    printf("%d/n", len2); //随机值    return 0;}

执行结果: 

例2:

int main(){    if(strlen("abc") - strlen("abcdef") > 0)        printf("hehe/n");    else        printf("haha/n");    system("pause");    return 0;}

执行结果:

 由于此时的strlen没有进行定义,它的默认类型为无符号类型,那么两个无符号数相加减最后得出的数是一个很大的数,所以最后的结果必然是大于0的

strlen函数的模拟实现

(1)计数器

#include #include int my_strlen(const char *str){    int count = 0;    assert(str != NULL);    while(*str != "/0")    {        count++;        str++;    }    return count;}int main(){    int len = my_strlen("abcdef");    printf("%d/n", len);    return 0;}

 (2)指针-指针

#include int my_strlen(const char *str){	  const char *p = str;	  while(*p != "/0")	  {	  	  p++;	  }	  return p-str;}int main(){	  int len = 0;	  char arr[10]="abcdef";	  len = my_strlen(arr);	  printf("%d/n", len);	  return 0;}

(3)递归

不创建临时变量求字符串长度

#includeint my_strlen(const char *str){	  if(*str=="/0")		    return 0;	  else  	    return 1+my_strlen(str+1);}int main(){    int len = 0;	  char arr[10]="abcdef";	  len = my_strlen(arr);	  printf("%d/n", len);    return 0;}

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

strcpy

char* strcpy(char * destination, const char * source);
  • 将按源指向的 C 字符串复制到按目的地指向的阵列中,包括终止空字符 (并停止在那个时候) 
  • 源字符串必须以 "/0" 结束
  • 会将源字符串中的 "/0" 拷贝到目标空间
  • 目标空间必须足够大,以确保能存放源字符串
  • 目标空间必须可变。 学会模拟实现

例:

#include int main(){    char arr1[] = "abcdefghi";    char arr2[] = "bit";    //错误示范    //char *arr1 = "abcdefghi";    //p指向常量字符串,而常量字符串无法被修改    //char arr2[] = {"b","i","t"};    //由于此时没有给"/0",由于找不到"/0"会导致向后越界访问    strcpy(arr1, arr2);     printf("%s/n", arr1);    return 0;}

strcpy函数的模拟实现

#include #include char *my_strcpy(char *dest, const char *src){    assert(dest != NULL);    assert(src != NULL);    char *ret = dest;    //拷贝src指向的字符串到dest指向的空间,包含"/0"    while(*dest++ = *src++)    {        ;    }    //返回目的的空间的初始地址    return ret;//"/0"}int main(){    char arr1[] = "abcdefgh";    char arr2[] = "bit";    my_strcpy(arr1, arr2);     printf("%s/n", arr1);    return 0;}

strcat

char * strcat ( char * destination, const char * source );
  • 将源字符串的副本附加到目的地字符串。终止无效字符在目的地由源的第一个字符覆盖,并在源的末尾包含空字符由目的地两者的连结形成的新字符串
  • 源字符串必须以 "/0" 结束
  • 目标空间必须有足够的大,能容纳下源字符串的内容
  • 目标空间必须可修改
  • 字符串自己如何给自己追加

例: 

#include int main(){    char arr1[30] = "hello/0xxxxxxx";    char arr2[] = "wolrd";    strcat(arr1, arr2);    printf("%s/n", arr1);    //错误示范    char arr3[] = "hello";    char arr4[] = "world";    strcat(arr3, arr4);    printf("%s/n", arr3);    //由于arr3数组没有定义空间大小    //此时就开辟了"hello/0"6个字节    //当arr4数组追加过去后就产生了越界访问,产生报错    return 0;}

调试结果: 

执行结果:

总结:此时我们可以看到,arr2数组内的的字符串从arr1数组中的"/0"开始进行追加

当arr2数组内的字符串追加过去后,后面的‘/0’也一并追加了过去

当目标空间不够大时,就会造成访问越界

strcat函数的模拟实现

#include #include char *my_strcat(char *dest, const char *src){    assert(dest != NULL);    assert(src);    char *ret = dest;     //1.找到目的字符串的"/0"    while(*dest != "/0")    {        dest++;    }    //2.追加    while(*dest++ = *src++)    {        ;    }    return ret;} int main(){    char arr1[30] = "hello";    char arr2[] = "wolrd";    my_strcat(arr1, arr2);    printf("%s/n", arr1);    return 0;}

strcmp

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

此函数开始比较每个字符串的第一个字符。 如果它们彼此相等,则继续使用以下对,直到字符不同或到达终止空字符为止。 

标准规定:

  • 第一个字符串大于第二个字符串,则返回大于0的数字
  • 第一个字符串等于第二个字符串,则返回0
  • 第一个字符串小于第二个字符串,则返回小于0的数字
  • 那么如何判断两个字符串?
#include #include int main(){    char *p1 = "qbc";    char *p2 = "abc";    // int ret = strcmp(p1, p2);    // printf("%d/n", ret);    if(strcmp(p1, p2) > 0)    {        printf("p1 > p2/n");    }    else if(strcmp(p1, p2) == 0)    {        printf("p1 == p2/n");    }    else if(strcmp(p1, p2) < 0)    {        printf("p1 < p2/n");    }    return 0;}

strcmp函数的模拟实现

#include #include int my_strcmp(const char *str1, const char *str2){    assert (str1 && str2);    // 比较    while(*str1++ == *str2++)    {        if(*str1 == "/0")        {            return 0;//等于        }     }    if(*str1 > *str2)        return 1;//大于    else        return -1;//小于    //return (*str1 - *str2);    //通过相减判断大于或小于}int main(){    char *p1 = "abcdef";    char *p2 = "abqwe";    int ret = my_strcmp(p1, p2);    printf("ret = %d/n", ret);    return 0;}

三、长度受限制的字符串函数

strncpy

char * strncpy ( char * destination, const char * source, size_t num );//单位是字节
  •  将源的前 num 个字符复制到目标。 如果在复制 num 个字符之前找到源 C 字符串的结尾(由空字符表示),则用零填充目标,直到总共写入了 num 个字符。
  • 拷贝num个字符从源字符串到目标空间。
  • 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

 例1:

int main(){    char arr1[10] = "abcdefgh";    char arr2[] = "bit";    stcncpy(arr1, arr2, 6);    return 0;}

调试结果: 

 例2:

#include #include int main(){    char arr1[10] = "abcdefgh";    char arr2[] = "bit";    strncpy(arr1, arr2, 6);    return 0;}

调试结果: 

 由此可见,strncpy函数能拷贝任意长度的字符,当拷贝的字符长度不够拷贝数时,用 "/0" 进行补充,直到拷贝数相等

strncpy函数的模拟实现

#include #include void my_strncpy(char *dest, const char *src, int n){    assert(src);    char* p1 = dest;    const char* p2 = src;    while (n--)    {        *p1++ = *p2++;    }}int main(){    char arr1[20] = "hello"    char arr2[] = "world";    my_strncpy(arr1, arr2, 3);    return 0;}

strncat

char * strncat ( char * destination, const char * source, size_t num );
  • 将源的前 num 个字符附加到目标,再加上一个终止空字符
  • 如果 source 中 C 字符串的长度小于 num,则只复制终止空字符之前的内容。

例1:

#include #include int main(){    char arr1[30] = { "hello/0xxxxxx" };    char arr2[] = "world";    strncat(arr1, arr2, 4);    return 0;}

调试结果: 

例2:

int main(){    char arr1[30] = { "hello/0xxxxxxxxx" };    char arr2[] = "world";    strncat(arr1, arr2, 8);    return 0;}

调试结果:

 由此可见,不管追加几个数,都会在追加字符串后加上 "/0",而一旦追加数超过了追加字符串的长度,在追加完整字符串后面再加上"/0"后便结束了

strncat函数的模拟实现

#include#include#includevoid my_strncat(char* dest, const char* src, int len1, int len2, int n){	char* ret = dest + len1;	assert(src);	assert(n <= len2);	while ((n--) && (*ret++ = *src++))    {		;    }}int main(){	char arr1[20] = "abcd";	char arr2[] = "efghi";	int len1 = strlen(arr1);	int len2 = strlen(arr2);	int n = 0;	scanf("%d", &n);	my_strncat(arr1, arr2, len1, len2, n);	return 0;}

strncmp

int strncmp ( const char * str1, const char * str2, size_t num );
  • 比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。

例:

#include #include int main(){    const char* p1 = "abcdef";    const char* p2 = "abcqwer";    int ret = strncmp(p1, p2, 4);    printf("%d/n", ret);}

执行结果:

strncmp函数的模拟实现

#include#include#includeint my_strncmp(const char *dest, const char *src, int n){	int ret = 0;	assert(dest);	assert(src);	assert(n);	while( (n--) && !(ret = (unsigned char)*dest-(unsigned char)*src) && *dest )	{		dest++;		src++;	}	return ret;}int main(){	char arr1[] = "abcdef";	char arr2[] = "abcedef";	int n = 0;	int ret = 0;	int i = 0;	scanf("%d",&n);	ret = my_strncmp(arr1, arr2, n);	if(ret == 0)	{		for(i=0; i");		for(i=0; i

四、字符串查找

strstr

char * strstr ( const char *, const char * );
  • 返回指向 str1 中第一次出现 str2 的指针,如果 str2 不是 str1 的一部分,则返回空指针。

例:

int main(){    char *p1 = "abcdefabcdef";    char *p2 = "def";    char * ret = strstr(p1, p2);    if(ret == NULL)    {        printf("子串不存在");    }    else    {        printf("%s/n", ret);    }    system("Pause");    return 0;}

执行结果:

 由此可得出,当有主字符串中存在两个及以上子串时,优先按第一次出现相同的地址进行打印,并且会一直打印完剩下的字符串

strstr函数的模拟实现

#include #include //KMP 算法char *my_strstr(const char *p1, const char *p2){    assert(p1 != NULL);    assert(p2 != NULL);    char *s1 = NULL;    char *s2 = NULL;    char *cur = (char*)p1;//当前指针current    if(*p2 == "/0")    {        return (char*)p1;    }    while(*cur)    {        s1 = cur;        s2 = (char*)p2;        while(*s1 && *s2  && (*s1 == *s2))        {            s1++;            s2++;        }        if(*s2 == "/0")        {            return cur;//找到子串        }        cur++;    }    return NULL;//找不到子串}int main(){    char *p1 = "abcdef";    char *p2 = "def";    char * ret = my_strstr(p1, p2);    if(ret == NULL)    {        printf("子串不存在/n");    }    else    {        printf("%s/n", ret);    }    return 0;}

strtok

char * strtok (char *str, const char *sep);
  • sep参数是个字符串,定义了用作分隔符的字符集合
  • 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
  • strtok函数找到str中的下一个标记,并将其用 /0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改 变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
  • strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
  • strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
  • 如果字符串中不存在更多的标记,则返回 NULL 指针。

例:

#include #include int main(){    char arr[] = "qpzyahxf@163.com";    char *p = "@.";    char buf[1024] = {0};    //strtok会改变字符串内容,buf防止原字符串被切割(保护原始数据)    strcpy(buf, arr);    //切割buf中的字符串    char *ret = NULL;    for(ret = strtok(arr, p); ret != NULL; ret = strtok(NULL, p))    {        printf("%s/n", ret);    }    // char *ret = strtok(arr, p);    // printf("%s/n", ret);    // ret = strtok(NULL, p);    // printf("%s/n", ret);    // ret = strtok(NULL, p);    // printf("%s/n", ret);    return 0;}

执行结果:

strtok函数的模拟实现

#include char *my_strtok(char *str1 ,char *str2){    static char *p_last = NULL;    if(str1 == NULL && (str1 = p_last) == NULL)    {        return NULL;    }       char *s = str1;    char *t = NULL;    while(*s != "/0")    {        t = str2;        while(*t != "/0")        {            if(*s == *t)            {                p_last = s + 1;                if( s - str1 == NULL)                {                    str1 = p_last;                    break;                }                *s = "/0";                return str1;            }            t++;        }        s++;      }      return NULL;}int main() {     char arr[] = "qpzyahxf@163.com";    char *ret = NULL;    char *p = "@.";    for(ret =  my_strtok(arr, p); ret != NULL; ret = my_strtok(NULL, p))    {        printf("%s/n", ret);    }    return 0; } 

五、错误信息报告

strerror

char * strerror(int errum);

返回错误码,所对应的错误信息。

 ​​​​​​例1:

#include #include int main(){    int i = 0;    //1-10错误码的返回返回信息    for(i = 0; i <= 10; i++)    {        printf("%d     %s/n", i, strerror(i));    }    system("pause");    return 0;}

执行结果: 

在实际在使用的时候,错误码并非由我们来控制的,而是接收系统返回的错误信息

例2:

#include #include string.h>#include     char *str = strerror(errno);//需引用头文件errno.h    printf("%s/n", str);    //errno 是一个全局的错误码的变量    //当c语言的库函数在执行过程中,发生了错误,就会把对应的错误码,复制到errno中

例3:

#include #include int main(){    //打开文件    FILE *pf = fopen("test.txt", "r");    if(pf == NULL)    {        printf("%s/n", strerror(errno));//知道错误的原因    }    else    {        printf("open file success./n");    }    return 0;}

执行结果: 

六、字符操作

字符分类函数

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

注:0位假,非0为真


 字符转换

int tolower ( int c ); //tolower 转小写字母int toupper ( int c );//toupper 转大写字母

例1:

#include #include int main(){    char ch = tolower("Q");    putchar(ch);    system("pause");    return 0;}

执行结果: 

例2:

#include #include int main(){    //大写字母转小写    char arr[] = "No Mercy";    int i = 0;    while(arr[i])    {        if(isupper(arr[i]))        {            arr[i] = tolower(arr[i]);        }        i++;    }    printf("%s/n", arr);    return 0;}

执行结果: 

七、内存操作函数

在之前的学习中,我们了解了字符串拷贝可以使用strcpy函数,但是strcpy函数具有局限性。

当拷贝的数据不是字符串时,比如说int类型、float类型,还能使用strcpy函数吗?

strcpy函数在拷贝的时候是以/0为字符串拷贝的结束标志,那么在拷贝其它类型数据的时候,拷贝该结束的时候不一定存在/0。所以使用strcpy函数肯定是行不通的。那怎么办呢?

此时我们就可以使用memcpy函数-- - 内存拷贝函数,用来拷贝任意类型数据。

memcpy

void *memcpy ( void * destination, const void * source, size_t num );//void* - 通用类型的指针-无类型指针//dest destination 表示内存拷贝的目的位置//src source 表示内存拷贝的起始位置//size_t num 表示内存拷贝的字节数
  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置
  • 这个函数在遇到 "/0" 的时候并不会停下来
  • 如果source和destination有任何的重叠,复制的结果都是未定义的。

例:

int main(){    int arr1[] = { 1,2,3,4,5 };    int arr2[5] = { 0 };    memcpy(arr2, arr1, sizeof(arr1));    return 0;}

调试结果:

memcpy函数的模拟实现

void *my_memcpy ( void *dest, const void *src, size_t num) {     void *ret = dest;     assert(dest && src);     while (num--)     {         //*(char*)dest = *(char*)src;		//dest = (char*)dest + 1;//++(char*)dest		//src = (char*)src + 1;//++(char*)src        *((char*)dest)++ = *((char*)src)++;     }     return ret; }int main(){    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };	int arr2[10] = { 0 };	my_memcpy(arr2, arr1, 20);    return 0;}

memmove

void * memmove ( void * destination, const void * source, size_t num ); //void* - 通用类型的指针-无类型指针//dest destination 表表示内存移动的目的位置//src source 表示内存移动的起始位置//size_t num 表示移动内存的字节数
  •  和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的
  • 如果源空间和目标空间出现重叠,就得使用 比特科技 memmove函数处理

memmove函数的模拟实现

void *my_memmove( void *dest, const void *src, size_t num) {     void * ret = dest;     assert(dest && src);    if (dest <= src || (char *)dest >= ((char *)src + num))     {         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; } int main(){    int arr1[10] = {0,1,2,3,4,5,6,7,8,9};    int arr2[10] = {0};    my_memmove(arr1 + 2, arr1, 20);    return 0;}

memset

void* memset(void* dest, int c, size_t count);

作用:Sets buffers to a specified character.(将缓冲区设置为指定的字符)

以字节为内存设置单位

例:

#include#includeint main(){	char arr[] = "abcdefg";	memset(arr, "*", 4);	printf("%s", arr);	return 0;}

执行结果


memcmp

int memcmp ( const void * ptr1,              const void * ptr2,              size_t num ); 
  • 比较从ptr1和ptr2指针开始的num个字节
  • 返回值如下

例:

#include#includeint main(){	int arr1[5] = { 1,2,3,4,5 };	int arr2[5] = { 1,2,3,4,8 };	int ret = memcmp(arr1, arr2, sizeof(arr1));	if (ret > 0)	{		printf("arr1 > arr2");	}	else if (ret == 0)	{		printf("arr1 == arr2");	}	else	{		printf("arr1 < arr2");	}	return 0;}

执行结果:

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/118797.html

相关文章

  • C语言进阶】☀️数据类型&amp;amp;&amp;amp;整型在内存中的存储

    目录 ​  一、数据类型介绍 二、类型的意义 三、类型的基本归类 整型家族 浮点数家族 构造类型(自定义类型) 指针类型 空类型 四、整形在内存中的存储 原码、反码、补码 大小端字节序 为什么有大端和小端? 一道经典笔试题  一、数据类型介绍 数据从大的方向分为两类: 内置类型自定义类型内置类型我们前面已经学习过,如下: char            //字符数据类型 short      ...

    Xufc 评论0 收藏0
  • C语言进阶】自定义类型(2)枚举&amp;amp;联合

    目录 一、枚举 (一)枚举类型的定义 (二)使用枚举的原因  (三)枚举的优点  (四)枚举的大小 (五)枚举的使用 二、联合(共用体) (一)联合类型的定义 (二)联合的特点 (三)面试题 (四)联合大小的计算 一、枚举 枚举顾名思义就是:列举 。   即把可能的取值一一列举出来。 比如我们现实生活中: 一周当中从周一至周日的7天,可以一一列举;性别有:男、女、保密,可以一一列举;月份有...

    leiyi 评论0 收藏0
  • JavaScript进阶函数和对象知识点详解

      在过往学习的JavaScript都是在基础,现在为大家介绍更为深入的JavaScript知识。  JavaScript函数  JavaScript函数和Java函数是有一部分相似的,所以学习起来也会相对简单  基本构造  1.直接构造  //function代表函数标志,name为函数名称,参数可有可无   functionname(参数){   //...   return;   }  2....

    3403771864 评论0 收藏0
  • python数学建模Numpy应用介绍与Pandas学习

      小编写这篇文章的主要目的,主要是来给大家解答下关于python数学建模的一些相关的介绍,涉及到内容涵盖Numpy的一些相关的应用具体的一些介绍。另外,还会涉及到相关的Pandas学习知识,具体内容下面给大家详细解答下。  1 Numpy介绍与应用  1-1Numpy是什么  NumPy是一个运行速度非常快的数学库,一个开源的的python科学计算库,主要用于数组、矩阵计算,包含:  一个强大的...

    89542767 评论0 收藏0
  • python数学建模之Numpy应用介绍与Pandas学习

      小编写这篇文章的一个主要目的,主要是来给大家去做一个介绍。介绍的内容主要是关于建模知识的一些相关介绍,包括其Pandas的一些相关学习,就具体的操作内容,下面就给大家详细解答下。  Numpy学习  1 Numpy介绍与应用  1-1Numpy是什么  NumPy是一个运行速度非常快的数学库,一个开源的的python科学计算库,主要用于数组、矩阵计算,包含:  一个强大的N维数组对象ndarr...

    89542767 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<