史上最全C语言7种字符函数和4种内存操作函数的使用和模拟实现

一、字符串操作函数
(1)strlen()

求字符个数,不计算’\0’。

函数作用:

1、当不完全初始化的数组求字符个数时,后面未初始化的默认为0,ascill码为0的是\0,所以这种情况区别于完全初始化是可以求出字符个数的。

2、strlen的返回类型是无符号整数,当两个strlen相减时,可能会出现负数,这个时候它是以无符号形式表现出来,所以它会是一个很大的正数。

函数原型:

size_t strlen ( const char * str );

头文件:

#include <string.h>

模拟实现:

//1、计数器的方法
#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char *str)
{
	int i = 0;
	assert(str != NULL);
	while (*str != '\0')
	{
		str++;
		i++;
	}
	return i;
}

//2.指针减去指针
/*
int my_strlen(char* s)
{
	char* p = s;
	while (*p != '\0')
		p++;
	return p - s;
}*/

//3.递归,不创建临时变量计数器
/*int my_strlen(const char * str)
{
 if(*str == '\0')
 return 0;
 else
 return 1+my_strlen(str+1);
}
*/
int main()
{
	char arr[] = "abcdef";
	printf("%u", my_strlen(arr));
	return 0;
}
(2)strcpy()

函数作用:

字符串复制到对应的空间

  • 源字符串必须以’\0’结束。
  • 会将源字符串中的‘\0’拷贝到目标空间。
  • 空间必须足够大,以确保能存放源字符串。否则程序奔溃,栈空间被破坏。
  • 目标空间必须可变。比如字符指针的字符是常量,不可以被修改,这样就不能用这个函数。
  • 返回值的类型可以是void但是他之所以设置为cahr*是为了实现链式访问。

函数原型:

char* strcpy(char * destination, const char * source);

头文件:

#include <string.h>

模拟实现:

#include <stdio.h>
#include <assert.h>
char* my_strcpy(char *dest,const char *src)
{
	assert(dest != NULL && src != NULL);//assert(dest && src );
	//while (*src != '\0')
	//{
	//	*dest = *src;
    //       src++;
	//	dest++;
	//}
	//*dest = '\0';
	char* ret = dest;
	while (*dest++ = *src++)
	{		
	}
	return ret;
}
int main()
{
	char arr[] = "xxxxxxxxxxx";
	char *arr1 = "heelo bit";
	printf("%s", my_strcpy(arr, arr1));
    return 0;
}
(3)strcat()

函数作用:

在dest指向的字符串追加一串src指向的字符串,并返回dest的地址,追加失败应该没有返回值(以后再看看,不太清楚)

  • 源字符串必须以’\0’结束

  • 目标空间必须足够大,能容纳下字符串的内容

  • 目标空间必须可以修改

  • 字符串自己给自己追加,如何?下面代码是不能自己给自己追加,会死循环,但是库函数里的strcat可以自己追加。要看编译器的代码是否支持。

函数原型:

char * strcat ( char * destination, const char * source );

头文件:

#include <string.h>

模拟实现:

#include <assert.h>
#include <stdio.h>
#include <string.h>
char* my_strcat(char* dest,const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest!=0)
	{
		dest++;
	}
	while (*dest++ = *src++)
	{
	}
	return ret;
}
#include <stdio.h>
#include <assert.h>
int main()
{
	char arr1[20] = "abcdef";
	char arr2[10] = "ghijk";
	printf("%s", my_strcat(arr1, arr2));
	return 0;
}

//abcdefghjk
(4)strcmp()

函数作用:

判断两个字符串的大小,根据ascill值,相同往后推送直到判断出来

  • 第一个字符串大于第二个字符串,则返回大于0的数字

  • 第一个字符串等于第二个字符串,则返回0

  • 第一个字符串小于第二个字符串,则返回小于0的数字

函数原型:

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

头文件:

#include <string.h>

模拟实现:

#include <assert.h>
#include <stdio.h>
#include <string.h>
int my_strcmp(char* dest, char* src)
{
	assert(src && dest);
	while (*dest == *src)
	{
		if (*dest == 0)
			return 0;
		dest++;
		src++;
	}
	return *dest - *src;	
}
int main()
{
	char arr1[] = "abcd";
	char arr2[] = "abcf";
	printf("%c",my_strcmp(arr1, arr2));
	return 0;
}
(5)长度受限制的字符串函数

strncpy n是位数。n大于数组位数复制过去就是补0

strncmp 前几个和前几个比较 “abcd”>"afhkld"这里是比较大小 char* p = “abcd” ,其中“abcd”时a的地址。

strncat 这里位数不够时不会补

按字节追加

char *strncat( char *strDest, const char *strSource, size_t count );

模拟实现:

#include <assert.h>
#include <stdio.h>
#include <string.h>
char* my_strncat(char* dest, const char* src,int n)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest != 0)
	{
		dest++;
	}
	while (n--)
	{
		*dest++ = *src++;
	}
	*dest = '\0';
	return ret;
}

//abcdefghjk

int main()
{
	char arr1[20] = { 'a','b','c','\0' };
	char arr2[20] = "ghjklmn";
	my_strncat(arr1, arr2, 6);
	printf("%s", arr1);
	
	return 0;
}

详情看看课件。

(6)strstr()

函数作用:

在一个字符串中(str2)找是否存在另外字符子串(str1),找到返回子串在这个字符串中的地址,找不到返回NULL。

函数原型:

char * strstr ( const char *str1, const char * str2);

头文件:

<string.h>

使用例子:

情景1:

用于单次匹配,返回的是匹配成功的字符串以及后面的字符串

#include <stdio.h>
#include <string.h>
main()
{
    char *s="GoldenGlobalView";
    char *l="lob";
    char *p;
    p=strstr(s,l);
    if(p)
        printf("%s",p);
    else
        printf("NotFound!");
    return 0;
}
//lobalView

情景2:

用于单次匹配,返回的是子串在母串的位置

#include <stdio.h>
#include <string.h>
main()
{
    char *s="GoldenGlobalView";
    char *l="lob";
    char *p;
    p=strstr(s,l);
    if(p)
        printf("%d",p-s+1);
    else
        printf("NotFound!");
    return 0;
}
//8
//lob在GoldenGlobalView的第8位上。

情景3:

用于多次匹配知道母串结束,记录子串在母串中出现的次数

#include<stdio.h>
#include<string.h>

int main()
{
    int i,n,j,k=0;
    char a1[1001],a2[1001];
    scanf("%s %s",a1,a2);
    char *p;
    p=a1;
    while( ( p=strstr(p,a2) ) != NULL)//p为子串与母串匹配成功
    {								  //时,子串第一个符号在母串
        k++;						  //中出现的位置地址
        p++; //p++后才能匹配下一个,否则无法退出循环
    }
    printf("%d",k);
}
//abababababa
//aba
//5

//aba出现了5次

模拟实现:

#include <assert.h>
#include <stdio.h>
#include <string.h>
char* my_strstr(const char* dest,const char* src)
{
	assert(dest && src);
	const char* s1 = dest;//两个源地址在后面字符匹配不成功时需要复原,用个假身替代它改变
	const char* s2 = src;
    
	const char* cur = dest;//被查字符串(dest)的起始地址
	while (*cur)//当查字符串(dest)起始地址不为0时
	{
		s1 = cur;
		s2 = src;

		while (*s1 && *s2 && (*s1 == *s2))//三者均不能为0
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return (char*)cur;//定义的是const char*不能改,函数定义是char*,所以要强转
		}
		cur++;
	}
	return NULL;
}

int main()
{
	const char arr1[20] = "abcdefghj";
	const char arr2[20] = "efghj";
	if (my_strstr(arr1, arr2) == NULL)
		printf("找不到");
	else
		printf("%s",my_strstr(arr1, arr2));
    return 0;
}
(7)strtok()

charstrtok(****charstr,constcharsep);**

  • sep参数是个字符串,定义用作分隔符的字符集合。

  • 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。

  • strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)

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

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

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

    使用例子:

image-20231004215140072.png

(8)strerror()

char * strerror ( int errnum );

返回错误码对应的信息。

常见错误(可以背背)

没有错误

No error

不允许操作

Operation not permitted

没有这样的文件或目录

No such file or directory

没有这样的过程

No such process

中断函数调用

Interrupted function call

输入/输出错误

Input/output error

没有这样的设备或地址

No such device or address

参数列表太长

Arg list too long

执行文件格式错误

Exec format error

错误的文件描述符

Bad file descriptor

没有子进程

No child processes

资源暂时不可用

Resource temporarily unavailable

空间不够

Not enough space

没有权限

Permission denied

错误的地址

Bad address

未知的错误

Unknown error

资源设备

Resource device

文件是否存在

File exists

不当的链接

Improper link

没有这样的装置

No such device

不是目录

Not a directory

是一个目录

Is a directory

无效的参数

Invalid argument

系统中打开的文件太多

Too many open files in system

打开的文件太多

Too many open files

I/O控制操作不当

Inappropriate I/O control operation

未知的错误

Unknown error

文件太大

File too large

设备上没有剩余空间

No space left on device

无效的寻求

Invalid seek

只读文件系统

Read-only file system

链接太多

Too many links

破碎的管

Broken pipe

域的错误

Domain error

结果太大

Result too large

未知的错误

Unknown error

避免资源死锁

Resource deadlock avoided

未知的错误

Unknown error

文件名太长

Filename too long

没有可用的锁

No locks available

二、字符分类函数(分类/转换):

返回的是随机值,非0就是真。以后要用到,可以背背用用

image-20231004230043786.png

三、内存操作函数
    字符操作函数只能操作字符,比如strcpy复制,strcmp对比,针对的对象都是字符,而内存操作函数的对象是内存,可以操作的对象更多。
(1)memcpy()
void * memcpy ( void * destination, const void * source, size_t num );
  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。

  • 这个函数在遇到 ‘\0’ 的时候并不会停下来。

  • 如果source和destination有任何的空间重叠,复制的结果都是未定义的,应该是两片空间,也就是不能在同一块内存,特别是还有重叠的,这时候memmove这个函数比较好。

使用方法:

/* memcpy example */
#include <stdio.h>
#include <string.h>
struct {
  char name[40];
  int age;
} person, person_copy;
int main ()
{
  char myname[] = "Pierre de Fermat";
  /* using memcpy to copy string: */
  memcpy ( person.name, myname, strlen(myname)+1 );
  person.age = 46;
  /* using memcpy to copy structure: */
  memcpy ( &person_copy, &person, sizeof(person) );
  printf ("person_copy: %s, %d \n", person_copy.name, person_copy.age );
  return 0;
}

模拟实现:

//方法一
void * memcpy ( void * dst, const void * src, size_t count)
{
        void * ret = dst;
 assert(dst&&src);
        while (count--) {
                *(char *)dst = *(char *)src;
                dst = (char *)dst + 1;
                src = (char *)src + 1;
       }
        return(ret);
}
(2)memmove()
void * memmove ( void * destination, const void * source, size_t num );
  • 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。

  • 如果源空间和目标空间出现重叠,就得使用memmove函数处理。

使用例子


#include <stdio.h>
#include <string.h>
int main ()
{
  char str[] = "memmove can be very useful......";
  memmove (str+20,str+15,11);
  puts (str);
  return 0;
}

模拟实现:

两种情况:

一种是dest在src前面、dest在src后面但是不重合,这个时候只需要首地址对首地址逐个更换即可。

一种是dest在src后面且重合,这个时候就是要尾地址对尾地址才可以,否则就会乱。

//方法一
#include <memory.h>
#include <stdio.h>
#include <assert.h>
void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(src && dest);
	void* ret = dest;
	if (src > dest)
	{
		while (count--)
		{
			*(char*)dest = *(char*)src;
			((char*)dest)++;			//dst = (char *)dst + 1;
             ((char*)src)++;     		// src = (char *)src + 1;          
		}
	}
	else
	{
		dest = (char*)dest + count - 1;
		src = (char*)src + count + 1;

		while (count--)
		{
			*(char*)dest = *(char*)src;
			((char*)dest)--;
			((char*)src)--;
		
	}
	return ret;
}
int main()
{

	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	memmove(arr, arr+2, 20);	
	int i = 0;
	for (i = 0; i < 9; i++)
	{
		printf("%d", arr[i]);
	}
	return 0;
}
//方法二,与法一类似
void * memmove ( void * dst, const void * src, size_t count)
{
        void * ret = dst;
        if (dst <= src || (char *)dst >= ((char *)src + count)) {
            
                while (count--) {
                        *(char *)dst = *(char *)src;
                        dst = (char *)dst + 1;
                        src = (char *)src + 1;
               }
       }
        else {           
                dst = (char *)dst + count - 1;
                src = (char *)src + count - 1;
                while (count--) {
                        *(char *)dst = *(char *)src;
                        dst = (char *)dst - 1;
                        src = (char *)src - 1;
               }
       }
        return(ret);
}

memcpy先出来,后来才有memmov,但是由之前程序员用mempy,不能随便删了,否则就编译不了了,但是现在编译器一般功能一样。

(3)memcmp()
int memcmp ( const void * ptr1, 
 const void * ptr2, 
 size_t num );
  • 比较从ptr1和ptr2指针开始的num个字节,它不是以int型比较,是按字节比较

返回值如下:

/* memcmp example */
#include <stdio.h>
#include <string.h>
int main ()
{
  char buffer1[] = "DWgaOtP12df0";
  char buffer2[] = "DWGAOTP12DF0";
  int n;
  n=memcmp ( buffer1, buffer2, sizeof(buffer1) );
  if (n>0) printf ("'%s' is greater than '%s'.\n",buffer1,buffer2);
  else if (n<0) printf ("'%s' is less than '%s'.\n",buffer1,buffer2);
  else printf ("'%s' is the same as '%s'.\n",buffer1,buffer2);
  return 0;
}
(4)memset()
void *memset( void *dest, int c, size_t count );

以字节为单位设置数字。

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	memset(arr, 6, 20);	
	int i = 0;
	for (i = 0; i < 9; i++)
	{
		printf("%d", arr[i]);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hhh __灏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值