字符操作:strlen、strcpy、strcat、strcmp、strstr、strtok。内存操作:memset、memcpy、memmove。用法、细节、及部分模拟实现。

strlen的用法和细节、以及自己模拟实现的strlen。

strlen是对字符串大小求长度,只能用来求字符串的大小,可不能和sizeof混淆哦,strlen是一个函数,sizeof是一个操作符!具体细节,前面有文章讲了。
strlen求字符串的大小的时候,遇到\0就终止,但是\0不算在字符串长度里哦
举个例子:
下面三个是模拟实现的strlen。
assert函数是为了确保指针合法,不是野指针。

```c
#include <stdio.h>
#include <windows.h>
#include<assert.h>
#pragma warning (disable:4996)
//方法一:创建临时变量计数
int My_strlen(char *arr)
{
    
    
	char *p = arr;
	assert(arr!= NULL);
	assert(p != NULL);
	int size = 0;
	while (*p)
	{
    
    
		*p++;
		size++;
	}
	return size;
}
//方法二:用指针相减得到中间元素的个数
int My_strlen2(char *arr)
{
    
    
	char* q = arr;
	assert(arr != NULL);
	assert(q != NULL);
	while (*q != 0)
	{
    
    
		q++;
	}
	return q - arr;
}
//方法三:递归方法
int My_strlen3(char *arr)
{
    
    
	assert(arr != NULL);
	if (*arr == 0)
	{
    
    
		return 0;
	}
	else
	{
    
    
		return 1 + My_strlen3(arr + 1);
	}
}

来看看我们模拟实现的运行结果。
在这里插入图片描述
另外出一道有趣的题目:涉及数据存储和strlen。
记住两点:
1、strlen遇到\0会停止。
2、strlen的返回值是一个无符号数。

int main()
{
    
    
  char a[1000] = {
    
    0};
  int i=0;
  for(i=0; i<1000; i++)
  {
    
    
    a[i] = -1-i;
  }
  printf("%d",strlen(a));
  return 0;
}

a是字符型数组,strlen找的是第一次出现尾零(即值为0)的位置。考虑到a[i]其实是字符型,如果要为0,则需要-1-i的低八位要是全0,也就是问题简化成了“寻找当-1-i的结果第一次出现低八位全部为0的情况时,i的值”(因为字符数组下标为i时第一次出现了尾零,则字符串长度就是i)。只看低八位的话,此时-1相当于255,所以i==255的时候,-1-i(255-255)的低八位全部都是0,也就是当i为255的时候,a[i]第一次为0,所以a[i]的长度就是255了。

strcpy的用法,和模拟实现的strcpy。

strcpy是进行拷贝,string copy的缩写,即字符拷贝。

char* My_strcpy(char *dst,const char* str)//str为被拷贝的字符,为了防止误修改,加上了const修饰,还要记住dst的空间要大于等于str。
{
    
    
	char* cp = dst;
	assert(dst != NULL);
	assert(str != NULL);
	while (*cp++ = *str++)
	{
    
    
	}
	return (dst);
}

细节:while( * cp++ = * str++)当完成最后一个赋值操作, ”\0“。
判断为假,循环结束。那么”\0”也被拷贝了。
来看看例子的运行结果:
在这里插入图片描述
这里要记得只能是字符数组类型的字符串,不能是字符指针类型的字符串。至于为什么,我在小白初学指针《一》里面讲述了。附上链接。
https://blog.csdn.net/Zhou000815/article/details/109690352

strcat的用法、细节、模拟实现。

strcat是字符拼接。将一个字符串完全拼接在另外一个字符串的后面。

char* My_strcat(char *dst, const char* str)
{
    
    
	char*cp = dst;
	assert(dst != NULL);
	assert(str != NULL);
	while (*cp != '\0')
	{
    
    
		cp++;
	}
	while (*cp++ = *str++ ;)
	{
    
    
	}
	return dst;
}

细节:将str拼接在dst的后面,是吧dst结尾的“\0”换成str的第一个字符。然后依次复制过去,所以从这种角度来看,也算一种复制。
来看例子和运行结果:
在这里插入图片描述
所以函数返回值的设置,也很重要。这里函数返回dst,意味着它可以链式调用。

strcmp的用法、细节、模拟实现。

strcmp,字符串比较。string compare。它是从两字符串的第一个字符开始比较,依次向后,直到/0 。期间只要字符不相同,立马结束比较。
比较结果:
str1>str2 返回1,str1<str2 返回-1,str1=str2 返回0;
听到这里是不是很疑惑?为什么字符可以比较大小?这就有关ascll码表,存储在计算机里的字符是按一定顺序排列的,用二进制的数字标上序号。比较字符大小,就是比较他们的序号大小。
模拟实现:

int My_strcmp(const char* str1, const char* str2)
{
    
    
	int ret = 0;
	assert(str1 != NULL);
	assert(str2 != NULL);
	while (!(ret = *(unsigned char*)str1 - *(unsigned char*)str2) && *str1)///无符号字符比较,即ascll比较。
	{
    
    
		str1++;
		str2++;
	}
	if (ret > 0)
	{
    
    
		return 1;
	}
    
    else if (ret<0)
	{
    
    
		return -1;
	}
	else
	{
    
    
		return 0;
	}
	return ret;
}

在这里插入图片描述
相信你看完这个结果图就能理解了。不再赘述。

strstr用法、细节、模拟实现。

strstr是从字符串中寻找一个特定的字符串,找到这个特定字符串第一次出现的位置,输出这个字符(包括这个特定字符)往后的字符。没有找到返回NULL。
来看模拟实现的代码:

char* My_strstr(const char *src,const char* dst)
{
    
    
	assert(dst != NULL);
	assert(src != NULL);
	char *p = (char*)dst;
	char *ret = (char*)src;
	while (*src)
	{
    
    
		if (*dst != *src)
		{
    
    
			src++;
			ret++;
		}
		else
		{
    
    
			while (*dst)
			{
    
    
				dst++;
				src++;
				if (*dst == '\0')
				{
    
    
					return ret;
				}
			}
			dst = p;
			src = ret + 1;
		}
	}
	return 0;
}

来看例子结果:
在这里插入图片描述
这没有链式调用,如果我们想找一个特定字符串在整个字符串里出现了多少次,我们可以写一个循环,用指针检验返回值,不为null时继续寻找,给计数器+1 。

strtok的用法

char * strtok( char * str, const char * sep );

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

int main ()
{
    
    
  char str[] ="- This, a sample string.";
  char * pch;
  printf ("Splitting string \"%s\" into tokens:\n",str);
  pch = strtok (str," ,.-");
  while (pch != NULL)
 {
    
    
    printf ("%s\n",pch);
    pch = strtok (NULL, " ,.-");
 }
  return 0;
}
#include <stdio.h>
int main()
{
    
    
   char *p = "[email protected]";
 const char* sep = ".@";
 char arr[30];
 char *str = NULL;
 strcpy(arr, p);//将数据拷贝一份,处理arr数组的内容
 for(str=strtok(arr, sep); str != NULL; str=strtok(NULL, sep))
 {
    
    
 printf("%s\n", str);
 }
}

memset的用法。

这个函数比较简单,直接来看代码:

 char arr[ROW][COL];

    //InitBoard(board, ROW, COL)

    memset(arr,' ', sizeof(arr));

它的作用是,把这个地址往开始往后用第二个参数去填写,填写量的大小由第三个参数决定。
来看看定义:void * memset ( void * ptr, int value, size_t num );

Sets the first num bytes of the block of memory pointed by ptr to the specified value (interpreted as an unsigned char).

翻译:将ptr指向的内存块的第一个字节数设置为指定值(解释为无符号字符)。

Value to be set. The value is passed as an int, but the function fills the block of memory using the unsigned char conversion of this value.
要设置的值。该值作为int传递,但函数使用此值的无符号字符转换填充内存块。
Number of bytes to be set to the value.
要设置为的字节数值大小
size_t is an unsigned integral type.
是无符号整数类型。

memcpy的用法、细节、模拟实现。

memcpy是内存级别的复制,它可以复制任何类型的数据,只要给它起始地址,和要复制的字节数,它就能按字节去复制。但也有小缺陷,一会再提。
来看模拟实现的代码:

void* My_memcpy(void* dst, const void* src, size_t size)
{
    
    
	assert(dst != NULL);
	assert(src != NULL);
	void* ret = dst;
	while (size--)
	{
    
    
		*(char*)dst = *(char*)src;
		dst = (char*)dst + 1;
		src = (char*)src + 1;
	}
	return ret;
}

例子和运行结果:
在这里插入图片描述
不难理解,它是将str+20开始往后11个字节的内容赋值给从str+15开始往后的11个字节,也就是把地址高的赋给地址低的。
那么将地址低的赋值给地址高的会发生什么呢?
来看看:
在这里插入图片描述

是不是很奇怪?这就是上面提到的缺陷,为什么呢?怎么解决?
来看图:
在这里插入图片描述
相信你一定能看明白!
来看看解决这个问题的memmove如何使用!

memmove的用法、细节、模拟实现。

memove它包含了上述拷贝的所有情况,它可以从左向右复制,当发生覆盖丢失重复拷贝时,又从右向左复制。
if (dst <= src || ((char*)src+size <= (char*)dst))
这个条件是上述图黑色小框里的满足条件,满足则从左向右拷贝,src在这里代表首地址,+size就是被拷贝部分的大小,也就是src的最后地址。
而不满足时,就从右向左拷贝。
看模拟实现代码。

void* My_memmove(void* dst, const void* src, size_t size)
{
    
    
	assert(dst != NULL);
	assert(src != NULL);
	void* ret = dst;
	if (dst <= src || ((char*)src+size <= (char*)dst))
	{
    
    
		while (size--)
		{
    
    
			*(char*)dst = *(char*)src;
			dst = (char*)dst + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
    
    
		dst = (char*)dst + size-1;//因为拷贝的内容是从src开始包含src,只+size会拷贝src往后的size个,跳过了src,所以要减一。
		src = (char*)src + size-1;
		while (size--)
		{
    
    
			*(char*)dst = *(char*)src;
			dst = (char*)dst - 1;
			src = (char*)src - 1;
		}
	}
	return ret;
}

来看例子和运行结果:

在这里插入图片描述
要记得,memmove和memcpy都对dst产生了修改,并且返回值是地址,传入的参数也是地址。可以链式和嵌套使用哦!

最后最后!!!最重要的一点!!!

因为编译器的不断优化,本人在vs2013中对比了memcpy和memmove,它俩在使用上是没有任何差别的!!!但是,要了解它俩的区别。以防止在不同的编译器环境下,造成错误使用。另外,请理解那六种情况,更好的区别他俩。
在这里插入图片描述
文章并不完美,若有错误,欢迎指正。

猜你喜欢

转载自blog.csdn.net/Zhou000815/article/details/110070371
今日推荐