文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

C语言进阶教程之字符串&内存函数

2024-04-02 19:55

关注

前言:

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

一、求字符串长度

strlen


size_t strlen ( const char * str );

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

例1:


#include <stdio.h>
#include <string.h>
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 <stdio.h>
#include <assert.h>
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 <stdio.h>
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)递归

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


#include<stdio.h>
int 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);

例:


#include <stdio.h>
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 <stdio.h>
#include <assert.h>
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 );

例:


#include <stdio.h>
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 <stdio.h>
#include <assert.h>
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 );

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

标准规定:


#include <stdio.h>
#include <string.h>
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 <stdio.h>
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 );//单位是字节

 例1:


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

调试结果: 

 例2:


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

调试结果: 

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

strncpy函数的模拟实现


#include <stdio.h>
#include <assert.h>
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 );

例1:


#include <stdio.h>
#include <string.h>
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<stdio.h>
#include<assert.h>
#include<string.h>
void 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 <stdio.h>
#include <string.h>
int main()
{
    const char* p1 = "abcdef";
    const char* p2 = "abcqwer";
    int ret = strncmp(p1, p2, 4);
    printf("%d\n", ret);
}

执行结果:

strncmp函数的模拟实现


#include<stdio.h>
#include<assert.h>
#include<string.h>
int 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<n; i++)
		printf("%c",arr1[i]);
		printf("=");
		for(i=0; i<n; i++)
		printf("%c",arr2[i]);
	}
	else if(ret < 0)
	{
		for(i=0; i<n; i++)
		printf("%c",arr1[i]);
		printf("<");
		for(i=0; i<n; i++)
		printf("%c",arr2[i]);
	}
	else
	{
		for(i=0; i<n; i++)
		printf("%c",arr1[i]);
		printf(">");
		for(i=0; i<n; i++)
		printf("%c",arr2[i]);
	}
	return 0;
}

四、字符串查找

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 <stdio.h>
#include <assert.h>
//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);

例:


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

例3:


#include <stdio.h>
#include <errno.h>
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 <stdio.h>
#include <ctype.h>
int main()
{
    char ch = tolower('Q');
    putchar(ch);
    system("pause");
    return 0;
}

执行结果: 

例2:


#include <stdio.h>
#include <ctype.h>
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 表示内存拷贝的字节数

例:


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<stdio.h>
#include<string.h>
int 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<stdio.h>
#include<string.h>
int 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语言进阶教程之字符串&内存函数的文章就介绍到这了,更多相关C语言字符串&内存函数内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯