部分字符串函数和内存函数的介绍及模拟实现

1.字符串函数

1.1strlen

size_t strlen ( const char * str );
1.11strlen函数的基本介绍
  • strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )
  • 参数所指向的字符串必须是由'\0'结尾的
  • 函数的返回值类型为size_t,是无符号的
#include <stdio.h>
#include <string.h>

int main ()
{
  char szInput[256];
  printf ("Enter a sentence: ");
  gets (szInput);
  printf ("The sentence entered is %u characters long.\n",(unsigned)strlen(szInput));
  return 0;
}
1.12strlen函数的模拟实现

通过分析我们只需计算出参数指向的字符串的'\0'前的字符数量即可,函数实现如下:

//模拟实现strlen
//方法1 计算器法
size_t my_strlen(const char* str) {
	int count = 0;
	while (*str!= '\0') {
		count++;
		str++;
	}
	return count;
}
----------------------------------------
//方法2 指针-指针
size_t my_strlen(const char* str) {
	char* p = str;
	while (*p) {
		p++;
	}
	return p - str;
}
----------------------------------------
//方法3 递归方法
size_t my_strlen(const char* str) {
	if (*str=='\0') {
		return 0;
	}
	else {
		return 1 + my_strlen(1 + str);
	}
}

实现strlen函数我们可以使用上述三种不同的方法,根本思想就是找到'\0';

1.2strcmp

int strcmp ( const char * str1, const char * str2 );
1.21strcmp函数的基本介绍
  • 第一个字符串大于第二个字符串,则返回大于0的数字
  • 第一个字符串等于第二个字符串,则返回0
  • 第一个字符串小于第二个字符串,则返回小于0的数字

那么字符串比较的规则是什么呢?

——我们在比较字符串大小时会逐个比较字符串的每一位字符,直到一方被比较完了(即有一方到了'\0'),当双方都已经比较到'\0'那么两个字符串的大小相等。

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

int main ()
{
  char key[] = "apple";
  char buffer[80];
  do {
     printf ("Guess my favorite fruit? ");
     gets(buffer);
  } while (strcmp (key,buffer) != 0);
  puts ("Correct answer!");
  return 0;
}
1.22strcmp函数的模拟实现
#include<assert.h>
int my_strcmp(const char* s1, const char* s2) {
	assert(s1 && s2);
	while (*s1 == *s2) {
		if (*s1 == '\0') {
			return 0;
		}
		s1++;
		s2++;
	}
	if (*s1 > *s2)
		return 1;
	else
		return -1;
}

1.3strcpy

char * strcpy ( char * destination, const char * source );
1.31strcpy函数的基本介绍
  • 源字符串必须以 '\0' 结束
  • 会将源字符串中的 '\0' 拷贝到目标空间
  • 目标空间必须足够大,以确保能存放源字符串
  • 目标空间必须可变
#include <stdio.h>
#include <string.h>

int main ()
{
  char str1[]="Sample string";
  char str2[40];
  char str3[40];
  strcpy (str2,str1);
  strcpy (str3,"copy successful");
  printf ("str1: %s\nstr2: %s\nstr3: %s\n",str1,str2,str3);
  return 0;
}
1.32strcpy函数的模拟实现

函数将src首地址一直到'\0'中间的元素从dest地址处开始拷贝,最后再将末尾的'\0'拷贝过来

char* my_strcpy(char* dest, char* src) {
	char* ret = dest;

	while (*src != '\0') {
		*dest = *src;
		dest++;
		src++;
	}
	//最后要把'\0'放进去
	*dest = *src;
	return ret;
}

//简化版
#include<assert.h>
char* my_strcpy(char* dest, char* src) {
	char* ret = dest;
	assert(dest != NULL);
	assert(src != NULL);
	while (*dest++ = *src++) {
		;
	}
	最后要把'\0'放进去
	*dest = *src;
	return ret;
}

1.4strcat

char * strcat ( char * destination, const char * source );
1.41strcat函数的基本介绍
  • 源字符串必须以 '\0' 结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
#include <stdio.h>
#include <string.h>

int main ()
{
  char str[80];
  strcpy (str,"these ");
  strcat (str,"strings ");
  strcat (str,"are ");
  strcat (str,"concatenated.");
  puts (str);
  return 0;
}
1.42strcat函数的模拟实现

我们先找到dest地址后的第一个'\0'作为拼接的首元素,将src中的全部元素(包括'\0')从改地址处依次向后拼接

//模拟实现
#include<assert.h>
char* my_strcat(char* dest, const char* src) {
	char* ret = dest;
	assert(dest);
	assert(src);
	while (*dest != '\0') {
		dest++;
	}
	while (*dest++ = *src++) {
		;
	}
	return ret;
}
//注意不要使用两个相同的字符串连接,因为会将'\0'的值改变,会一直持续不停

1.5strncpy/strncmp/strncat

char * strncpy ( char * destination, const char * source, size_t num );
int strncmp ( const char * str1, const char * str2, size_t num );
char * strncat ( char * destination, const char * source, size_t num );
1.51strn类函数的基本介绍

相较于前面的几种函数我们发现这种函数名称中间多了一个n的函数在参数上比他们多了一个size_t num,参数的含义是字符数(可以理解为字节数),这个参数相当于我们对其所做的限制条件,我们限制其对我们所规定的字符数进行相应的操作。

      值得注意的一点是,strncpy如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

1.6strstr

const char * strstr ( const char * str1, const char * str2 );
      char * strstr (       char * str1, const char * str2 );
1.61strstr函数的基本介绍
  • strstr(s1,s2)找到s2在s1中第一次出现的位置
  • 如果没找到则返回空指针
#include<stdio.h>
#include<stringh.h>
int main() {
	char s1[] = "i love you but...";
	char s2[] = "you";
	char* ret = strstr(s1, s2);
	if (ret != NULL)
		printf("%s\n", ret);
	else
		printf("找不到\n");
	return 0;
}
1.62strstr函数的模拟实现
//模拟实现
char* my_strstr(char* str1, char* str2) {
	char* cp = str1;
	char* s1 = cp;
	char* s2 = str2;
	while (*cp) {
		s1 = cp;
		s2 = str2;
		while (*s1 && *s2 && *s1 == *s2) {
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return cp;
		cp++;
	}
	return NULL;
}

在模拟实现的时候我们定义了一个标志变量cp来保证逐渐向后比较字符时str1的地址不被改变

比较s1和s2的所指向的值两者不相等则cp++,当找到第一个相同的元素后进入第二个while循环,依次向后比较直到s2指到'\0'了,则找到子字符串,若s1到'\0'而s2还没比较完那么str1当中没有我们所找的子字符串。

1.7strtok

char * strtok ( char * str, const char * delimiters );
1.71strtok函数的基本介绍
  • sep参数是个字符串,定义了用作分隔符的字符集合
  • 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标 记。
  • strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。)
  • strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串 中的位置。
  • strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标 记。
  • 如果字符串中不存在更多的标记,则返回 NULL 指针。
#printf<stdio.h>
#include<string.h>
int main() {
	char str[] = "IsLiuzy@qq.com#6628";
	char cpy[50];
	strcpy(cpy, str);

	char sep[] = "@.#";
	char* ret = NULL;

	for (ret = strtok(str, sep); ret != NULL; ret = strtok(NULL, sep)) {
		printf("%s\n",ret);
	}
	return 0;
}

1.8strerror

char * strerror ( int errnum );
1.81strerror函数的基本介绍
  • 传入错误码errnum返回错误信息(errno是一个全局变量,系统会将错误码保存在里面,每个错误码都应一种错误信息)
//strerror返回错误码对应的错误信息
int main() {
	/*int i = 0;
	for (i = 0; i < 10; i++) {
		printf("%d:%s\n",i, strerror(i));
	}*/

	//C语言操作文件的步骤
	//1.打开文件
	//2.读/写文件
	//3.关闭文件


	FILE* pf = fopen("data.txt","r");

	if (pf == NULL) {
		printf("%s\n", strerror(errno));
		//errno是一个全局变量,系统会将错误码保存在里面
		perror("fopen");
		//perror可以直接帮我们打印出错误信息
		return 1;
	}
	//....

	fclose(pf);
	return 0;
}

1.9字符函数

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

int tolower ( int c );

int toupper ( int c );

#include<ctype.h>
int main() {
	/*printf("%d\n", isupper('S'));
	printf("%d\n", isdigit('2'));
	printf("%c\n", tolower('A'));
	printf("%c\n", toupper('s'));*/

	//读取一段字符串将字符串中的大写字母改为小写
	char str[20];
	gets(str);
	//gets可以读取空格
	
	char* p = str;
	while (*p) {
		if (isupper(*p)) {
			*p = tolower(*p);//*p=*p+32
		}
		p++;
	}
	printf("%s\n", str);
	return 0;

}

2.内存函数

2.1memcpy

void * memcpy ( void * destination, const void * source, size_t num );
2.11memcpy函数的基本介绍
  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置
  • 这个函数在遇到 '\0' 的时候并不会停下来
  • 如果source和destination有任何的重叠,复制的结果都是未定义的
  • 如果num不是规定元素类型的整数倍时我们可以分析拷贝的地址
#include<string.h>
#include<stdio.h>

int main() {
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
//将四个整形元素拷贝到arr2中
	memcpy(arr2, arr1, 40);
	for (int i = 0; i < 10; i++) {
		printf("%d ", arr2[i]);
	}
	return 0;
}
2.12memcpy的模拟实现

我们将目的地和起始地址强制类型转换为(char*)类型这样使用字节数num时会更方便。

//模拟实现memcpy
#include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t num) {
	assert(dest && src);
	void* ret = dest;
	while (num--) {
		*(char*)ret = *(char*)src;
		ret = (char*)ret + 1;
		src = (char*)src + 1;

	}
	return dest;
}

int main() {
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	
	my_memcpy(arr2, arr1, 40);
	//my_memcpy(arr1+3, arr1, 40);
	//memcpy函数是用来处理不重叠的内容拷贝
	//可以考虑使用memmove
	for (int i = 0; i < 10; i++) {
		printf("%d ", arr2[i]);
	}
	return 0;
}

2.2memmove

void * memmove ( void * destination, const void * source, size_t num );
2.21memmove函数的基本介绍
  • 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的
  • 如果源空间和目标空间出现重叠,就得使用memmove函数处理
2.22memmove函数的模拟实现

memmove能够实现重叠拷贝的原因是,memmove在拷贝同一个字符串时会将两者的地址进行比较,来选择对应的拷贝方式(从前->后\从后->前)

当dest<=src,我们可以直接从首地址开始拷贝(标红位置为第一处开始拷贝的地址)

若dest>=src,为了避免将要拷贝的数给覆盖我们选择从后面开始拷贝

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

for (int i = 0; i < 10; i++) {
		printf("%d ", arr1[i]);
	 }
 return 0;
}

2.3memcmp

int memcmp ( const void * ptr1, const void * ptr2, size_t num );
2.31memcmp函数的基本介绍

比较从ptr1和ptr2指针开始的num个字节 返回值如下:

Returns an integral value indicating the relationship between the content of the memory blocks:

return valueindicates
<0the first byte that does not match in both memory blocks has a lower value in ptr1 than in ptr2 (if evaluated as unsigned char values)
0the contents of both memory blocks are equal
>0the first byte that does not match in both memory blocks has a greater value in ptr1 than in ptr2 (if evaluated as unsigned char values)
/* 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;
}
2.32memcmp函数的模拟实现
//模拟实现memecmp
int my_memcmp(const void* ptr1, const void* ptr2, size_t num) {
	int i=0;
	for (i = 0; i < num; i++) {
		if (*(char*)ptr1 > *(char*)ptr2)
			return 1;
		else if (*(char*)ptr1 < *(char*)ptr2)
			return -1;
		ptr1 = (char*)ptr1+1;
		ptr2 = (char*)ptr2+1;
	}
	return 0;
}
int main() {
	int arr1[] = { 1,2,3,4,5,6 };
	int arr2[] = { 1,2,3 };
	int ret = my_memcmp(arr1, arr2, 12);
	printf("%d\n", ret);
	return 0;
}

2.4memset

void * memset ( void * ptr, int value, size_t num );
2.41memset函数的基本介绍
  • 将指定地址处之后num个字节的值设置为value
/* memset example */
#include <stdio.h>
#include <string.h>

int main ()
{
  char str[] = "almost every programmer should know memset!";
  memset (str,'-',6);
  puts (str);
  return 0;
}
2.42memset函数的模拟实现
//模拟实现memset
void* my_memset(void* src, int value, size_t num) {
	int i = 0;
	void* ret = src;
	for (i = 0; i < num; i++) {
		*(char*)src = value + '0';
		src = (char*)src + 1;
	}
	return src;
}
int main() {
	char str[] = "my love";
	my_memset(str+1, 3, 1);
	printf("%s\n", str);
	
	/*int arr[10] = { 0 };
	memset(arr, 1, 10);
	for (int i = 0; i < 10; i++) {
		printf("%d ", arr[i]);
	}*/
	//memset是将每个字节设置为1而不是值
	//1 ---01 00 00 00 memset 01 01 01 01
	return 0;
}

总结

        内存函数和类似的字符串函数相比只是多增加了参数 size_t num 这使得我们使用函数更有针对性。

  • 35
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值