千家信息网

C语言中字符串和内存函数的示例分析

发表于:2025-01-19 作者:千家信息网编辑
千家信息网最后更新 2025年01月19日,这篇文章将为大家详细讲解有关C语言中字符串和内存函数的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。前言:字符串是一种非常重要的数据类型,但是C语言不存在显
千家信息网最后更新 2025年01月19日C语言中字符串和内存函数的示例分析

这篇文章将为大家详细讲解有关C语言中字符串和内存函数的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

前言:

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

一、求字符串长度

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 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;}

执行结果:

关于"C语言中字符串和内存函数的示例分析"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

0