字符串与内存函数详解

  在C语言中,由于字符串特别常用,所以C语言提供了一堆关于字符串的函数,同时由于拷贝,比较等常用功能,C语言也提供了一些关于内存操作的函数,而这些函数都包含在一个叫做string.h的头文件中,使用之前需要先包含头文件string.h

目录

一  字符串函数

1  strlen函数 

1) 使用

2) 模拟实现

(1) 计数器方式

(2) 指针减去指针

(3) 递归方式

 2  strcat函数

1) 使用

2) 模拟实现

 3  strcmp函数

1) 使用

2) 模拟实现 

4  strcpy函数 

1) 使用

2) 模拟实现

 5  strncat函数

1) 使用

2) 模拟实现 

6  strncmp函数

1) 使用 

2) 模拟实现

​编辑 7  strncpy函数 

1) 使用

2) 模拟实现

8  strstr函数

1) 使用

2) 模拟实现 

​编辑

 9  strtok函数

10  strerror函数 

二  内存函数

1  memcpy函数

1) 使用

​编辑

 2) 模拟实现

2  memmove函数 

1) 使用

2) 模拟实现 

3  memset函数 

4  memcmp函数


一  字符串函数

  首先我们来讲解关于字符串的操作函数,一共包含了strlen,strcat,strcmp,strcpy,strncat,

strncmp,strncpy,strstr,strtok,strerror10个函数,接下来,我们一一讲解这几个函数。

1  strlen函数 

1) 使用

  我们先来看一下strlen函数的参数和返回值:

  strlen函数我们在之前文章的相关代码中使用过,其主要功能是返回字符串长度的,由于字符串是以'\0'为返回标志的,所以其实strlen返回的是'\0'之前的字符个数,而如果没有'\0'的话,返回的是个随机值,如:

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

int main()
{
  char arr1[] = {'a', 'b', 'c', 'd'};
  char arr2[] = "abcd";

  int ret1 = strlen(arr1);
  int ret2 = strlen(arr2);

  printf("%d %d\n", ret1, ret2);

  return 0;
}

运行结果是:

在上面那个代码中,arr1数组那种初始化是没有'\0'的,而在arr2那种初始化方式中,默认是在最后追加一个'\0'字符的,所以arr1数组的长度为随机值,arr2数组的长度为4。

  这里有一个需要注意的一点就是strlen函数的返回值是size_t类型的,是一个无符号整数,返回值想减的话也是一个无符号整数,比如下面这个代码:

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

int main()
{
  char arr1[] = "abcdef";
  char arr2[] = "abcde";

  if ((strlen(arr2) - strlen(arr1)) > 0)
    printf(">");
  else
    printf("<");

  return 0;
}

运行结果是:

从上面那个代码来看,arr1数组的字符串长度为6,arr2数组字符串的长度为5,正常来看,arr2的字符串长度减去arr1字符串的长度应该是-1,是小于0的,但由于返回值是无符号整数,所以相减的值也是无符号整数,所以是大于0的,打印的就是'>'。

2) 模拟实现

  接下来我们来模拟实现一下strlen函数,这里我们一共用三种方法来实现一下strlen函数。

(1) 计数器方式

  strlen函数主要是用来数字符串长度,所以我们可以采用循环,循环一次计数器加1,直到碰到'\0'  为止,所以计数器方式的话代码是这样的:

#include<stdio.h>
#include<assert.h>

size_t my_strlen(const char* str)
{
  int count = 0;
  assert(str);
  while (*str != '\0')
  {
    str++;
    count++;
  }

  return count;
}

int main()
{
  char arr[] = "abcdef";
 
  int ret = my_strlen(arr);

  printf("%d\n", ret);

  return 0;
}

运行结果是:

在上述代码中,my_strlen函数里面的参数是加了const的,因为使用strlen函数我们只期望得到其字符串长度,而不是期望其被改变,所以加了const,而加上assert(str)这个语句的作用是为了保证使用的str指针不为空指针,否则使用起来会十分危险。 

(2) 指针减去指针

  我们知道指针减去指针代表的是指针中间元素的个数,所以我们只需要让指向'\0'的指针减去指向首元素的指针,那就是中间字符的个数了,也就是字符串的长度,所以代码可以这样写:

#include<stdio.h>
#include<assert.h>

size_t my_strlen(const char* str)
{
  assert(str);

  char* start = str;
  while (*str != '\0')
  {
    str++;
  }

  return str - start;
}

int main()
{
  char arr[] = "abcdef";

  int ret = my_strlen(arr);
  printf("%d\n", ret);
  
  return 0;
}

  运行结果为:

(3) 递归方式

  strlen函数也可以通过递归的方式来实现,关于递归我们可以这样想,如果指针指向字符不是'\0',那么就让长度加1,然后指针++,直到遇到'\0',返回0,就可以了,递归公式就是这样的:

所以代码就可以这样写了:

#include<stdio.h>
#include<assert.h>

size_t my_strlen(const char* str)
{
  assert(str);

  if (*str == '\0')
    return 0;
  else
    return 1 + my_strlen(str + 1);
}

int main()
{
  char arr[] = "abcdef";
  int ret = my_strlen(arr);
  printf("%d\n", ret);

  return 0;
}

 运行结果是:

 2  strcat函数

1) 使用

  我们先来看一下strcat函数的原型:

返回值:

再来看一下参数:

这个函数主要是用来给字符串追加字符的,第一个参数是destination字符指针,就是要追加到的数组的指针,第二个参数是source字符指针,指向要追加到数组的内容的字符串的指针,如:

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

int main()
{
  char arr1[20] = "abcd\0xxxxx";
  char arr2[] = "efgh";

  strcat(arr1, arr2);

  printf("%s\n", arr1);

  return 0;
}

运行结果是:

我们再通过调试看一下:

 通过调试可以看到追加呢是从要追加到的数组的第一个'\0'开始追加,然后最后也会把原字符串的'\0'追加到目标数组

  使用strcat函数还有几个要注意的点:

(1) 原字符串必须以'\0'结尾

(2) 目标字符串也必须有'\0'

(3) 目标字符串必须足够大,能把原字符串的内容放到目标字符串里面

(4) 目标空间必须是可修改的

  知道了这些就可以很好的使用strcat函数了,接下来我们就来模拟实现一下。

2) 模拟实现

  模拟实现strcat函数,我们就得从他的功能上入手,首先这个函数是从要追加到的数组(dest数组)的第一个'\0'开始追加,所以在实现过程中,就得首先找到dest数组的'\0',然后把source数组的每一个字符(包括'\0')复制到destination数组的从第一个'\0'开始的后面的位置,还有一点需要注意的是strcat函数会返回destination数组的起始地址,所以我们在实现过程中,也会返回destination数组的起始地址。

  代码实现为:

#include<stdio.h>
#include<assert.h>

char* my_strcat(char* dest, const char* src)
{
  assert(dest && src);
  
  char* start = dest;
  while (*dest)
  {
    dest++;
  }
  //dest为指向'\0'的指针
  while(*dest++ = *src++)
  {
    ;
  }

  return start;
}

int main()
{
  char arr1[20] = { 0 };
  char arr2[] = "abcdef";

  char* ret = my_strcat(arr1, arr2);
  printf("%s\n", ret);

  return 0;
}

运行结果为:

 3  strcmp函数

1) 使用

  我们先来看一下原型:

参数:

 返回值:

  strcmp函数全称叫做string compare,一看这个名字就是用来比较两个字符串大小的,但是这个比较并不是通过比较字符串的长度确定大小,而是通过比较两个字符串中,对应字符ASCII码值的大小来比较大小的,比如"abcdef"和"abcf"这两个字符串,由于f的ASCII码值是比d的ASCII码值大的,所以"abcf"字符串是比"abcdef"字符串大的。

  再看返回值,当str1大于str2时,返回大于0的数字,当str1等于str2时,返回0,当str1小于str2时,返回小于0的数字,知道了这些,我们就可以来使用一下了。

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

int main()
{
  char arr1[] = "abcdef";
  char arr2[] = "abcf";
  int ret = strcmp(arr1, arr2);

  if (ret > 0)
    printf(">");
  else if (ret == 0)
    printf("=");
  else
    printf("<");

  return 0;
}

运行结果是:

2) 模拟实现 

  模拟实现strcmp函数,我们可以这样想,只要我们用一个循环,当str1指针指向的字符等于str2指向的字符相等时,就进入循环,然后让来两个字符指针分别自增,如果str1指向的字符为'\0'时,说明两个字符串都比较完了,字符全都相等,如果没有到'\0'就跳出循环了,说明两个字符串必然不等,只要返回str1指向的字符减去str2指向的字符就可以了。

代码实现:

#include<stdio.h>
#include<assert.h>

int my_strcmp(const char* str1,const char* str2)
{
  assert(str1 && str2);

  while (*str1 == *str2)
  {
    if (*str1 == '\0')
      return 0;

    str1++;
    str2++;
  }

  return *str1 - *str2;
}

int main()
{
  char arr1[] = "abcdef";
  char arr2[] = "abce";
  int ret = my_strcmp(arr1, arr2);
  if (ret > 0)
    printf(">");
  else if (ret == 0)
    printf("=");
  else
    printf("<");

  return 0;
}

运行结果是:

4  strcpy函数 

1) 使用

我们先来看一下函数原型:

参数与返回值:

 

strcpy函数的全称叫做string copy,顾名思义就是用来进行字符串拷贝的,如:

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

int main()
{
  char arr1[] = "hello world";
  char arr2[20] = "abcdefxxxxxxxx";
  
  strcpy(arr2, arr1);

  printf("%s\n", arr2);

  return 0;
}
  
  

我们通过调试来看一下:

 

可以看到当把源字符串拷贝到目标字符串的时候,会把'\0'也拷贝进去,所以源字符串必须是以'\0'为结束的,使用strcpy函数还有需要注意的几个点:

(1) 要拷贝到的目标空间必须足够大,能够放下拷贝的源字符串

(2) 目标空间必须是可修改的,不能是不可修改的常量,比如"abcdef"这样的字符串

2) 模拟实现

  其实strcpy函数的模拟实现与strcat函数的复制功能那里的实现逻辑是一样的,只要让src指向的字符赋给dest指向的字符,就能实现复制了,所以代码实现是这样的:

#include<stdio.h>
#include<assert.h>

char* my_strcpy(char* dest, const char* src)
{
  assert(str1 && str2)

  char* start = dest;
  while (*dest++ = *src++)
  {
    ;
  }
  
  return start;
}
int main()
{
  char arr1[] = "abcdef";
  char arr2[20] = { 0 };
  
  my_strcpy(arr2, arr1);
  
  printf("%s\n", arr2);

  return 0;
}

运行结果是:

 5  strncat函数

1) 使用

  我们来看一下strncat函数的原型:

参数及返回值:

 可以看到strncat函数只是比strcat函数多了第三个参数num,这个num就是要限定拷贝的几个字符,所以strncat函数是比strcat函数可控性更加高的,如:

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

int main()
{
  char arr1[] = "abcdef";
  char arr2[20] = "xxx\0xxxxxxxx";

  strncat(arr2, arr1, 4);

  printf("%s\n", arr2);

  return 0;
}

通过调试看一下结果:

可以看到strncat函数的作用类似于strcat函数,也是从目标空间里的第一个'\0'开始追加,并在最后追加一个'\0',只不过追加的字符个数从原来的全部字符改为了追加num个字符,使用的注意事项同strcat函数。

  还有一种情况,就是假如num的个数如果大于要拷贝的源字符串长度呢?如:

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

int main()
{
  char arr1[] = "abc";
  char arr2[20] = "xxx\0xxxxxxxx";

  strncat(arr2, arr1, 6);

  printf("%s\n", arr2);

  return 0;
}

调试结果为:

所以通过调试来看,可以看到如果num大于源字符串长度的话,就只会把源字符串的'\0'之前的字符追加到目标空间,然后再追加一个'\0'。

2) 模拟实现 

  有了strncat函数的模拟实现之后,strncat函数的模拟实现就比较容易了,前面寻找目标空间第一个'\0'的字符都是一样的,追加的部分分为两种情况,一种是num小于或者等于源字符串长度,这时候就是把num个源字符串的字符追加到目标空间,最后再在后面追加一个'\0',如果num大于源字符串长度,直接把源字符串追加到目标空间。

代码实现:

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

char* my_strncat(char* dest, const char* src, size_t num)
{
  char* ret = dest;
  assert(dest && src);
  //找到起始'\0'
  while (*dest)
  {
    dest++;
  }
  //这里dest为指向目标空间第一个'\0'的指针
  int n = strlen(src);
  if (num <= n)
  {
    while (num--)
    {
      *dest++ = *src++;
    }
  }
  else
  {
    while (n--)
    {
      *dest++ = *src++;
    }
  }
  *dest = '\0';//给最后追加一个'\0'

  return ret;
}
int main()
{
  char arr1[] = "abcdef";
  char arr2[20] = "xx\0xxxxxx";
  char arr3[20] = "xx\0xxxxxxxx";

  my_strncat(arr2, arr1, 4);
  my_strncat(arr3, arr1, 10);

  printf("%s\n", arr2);
  printf("%s\n", arr3);

  return 0;
}

调试结果为:

6  strncmp函数

1) 使用 

  我们先来看一下函数原型:

参数及返回值:

 我们也可以看到strncmp函数也是类似于strcmp函数的,只不过最后多了一个num参数,就是要比较的字符个数,如:

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

int main()
{
  char arr1[] = "abcdef";
  char arr2[] = "abcdgh";

  int ret1 = strncmp(arr1, arr2, 4);
  int ret2 = strncmp(arr1, arr2, 6);

  printf("%d\n", ret1);
  printf("%d\n", ret2);

  return 0;
}

运行结果是:

  还有一个注意的点,就是只要strncmp只个字符不同,就会提前返回,不会比较到num个字符

2) 模拟实现

  模拟strcmp函数的实现方式,strncmp函数在实现过程中共有三种情况:

(1) num还没有循环完,提前碰到了'\0',那就提前返回0

(2) num还没循环完,但是碰到了两个不相等的字符,那就提前返回*str1 - *str2

(3) num循环完了,但是没有返回0,也没有返回*str1 - *str2,那就返回0,因为没有返回*str1 - *str2,就说明这两个字符串的前num个字符都相等。

代码实现:

#include<stdio.h>
#include<assert.h>

int my_strncmp(const char* str1, const char* str2, size_t num)
{
	assert(str1 && str2);
	while (num)
	{
		while (*str1 == *str2)
		{
			if (*str1 == '\0')
				return 0;
			str1++;
			str2++;
			num--;
		}
		//num还没为0,但是str1 != str2
		if (num != 0)
			return *str1 - *str2;
	}
	//num为0了,但是还没有返回*str1 - *str2
	return 0;
}
int main()
{
  char arr1[] = "abcdef";
  char arr2[] = "abcdgh";

  int ret1 = my_strncmp(arr1, arr2, 4);
  int ret2 = my_strncmp(arr1, arr2, 6);

  printf("%d\n", ret1);
  printf("%d\n", ret2);

  return 0;
}

运行结果:

 
7  strncpy函数 

1) 使用

  先来看一下原型:

参数及返回值:

 类比前两个函数,这个函数也是与strcpy函数类似,只不过多了一个num参数,也是要拷贝的字符个数,如:

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

int main()
{
  char arr1[] = "abcdef";
  char arr2[10] = "xxxxxxxx";
  char arr3[10] = "xxxxxxxxx";

  strncpy(arr2, arr1, 4);
  strncpy(arr3, arr1, 8);

  printf("%s\n", arr2);
  printf("%s\n", arr3);

  return 0;
}

调试结果为:

所以我们可以看到strncpy函数如果num大于源字符串的长度,多余的字符个数是会继续追加'\0'的

2) 模拟实现

  跟strcpy函数的模拟实现很像,只不过这里需要两个循环,第一个是用来判断拷贝字符的个数num是否变为了0,并判断源字符串在拷贝过程中是否到了'\0'字符,第二个循环用来判断num多于字符串长度的情况,在第一个循环的基础上,如果是因为num减减到0而退出循环的,那么第二个循环就不执行,直接返回目标空间的起始地址,如果是由于字符串达到了末尾,那就让目标空间后面的num个字符变为'\0',再返回目标空间的起始地址。

  代码实现:

#include<stdio.h>
#include<assert.h>

char* my_strncpy(char* dest, const char* src, size_t num)
{
	assert(dest && src);

	char* ret = dest;
	while (num && *src)
	{
		*dest++ = *src++;
		num--;
	}
	while (num--)//如果num多于源字符串个数,那就给后面复制'\0'
	{
		*dest++ = '\0';
	}
	return ret;
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[10] = "xxxxxxxx";
	char arr3[10] = "xxxxxxxxx";

	my_strncpy(arr2, arr1, 4);
	my_strncpy(arr3, arr1, 8);

	printf("%s\n", arr2);
	printf("%s\n", arr3);

	return 0;
}

  调试结果为:

8  strstr函数

1) 使用

  同样的,我们先来看一下函数原型,参数及返回值:

这个函数是用来返回字符串str2在字符串str1中的起始位置的,如果找不到就返回NULL,如:

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

int main()
{
  char arr[] = "You are very beautiful";
  char* str = "beautiful";

  char* ret = strstr(arr, str);

  printf("%s\n", ret);

  return 0;
}

 运行结果是:

2) 模拟实现 

  对于这个函数的模拟实现,首先这个函数是返回str1字符串中包含str2字符串的其实位置,所以我们肯定要有一个指针用来记录那个起始位置,这个指针我把他称为当前位置的指针(cur指针),而后如果找到了那个起始位置,我们就要开始匹配了,那么在匹配过程中,我们就需要有两个指针来向后走,一个是从str1字符串中的起始位置开始向后走,另一个指针从str2字符串开始向后走,并判断每次匹配结果是否相等,共有以下几种情况:

1) 从起始位置开始,每次匹配都成功了,直到匹配到了str2字符串的'\0'字符,这种情况下,就直接返回cur指针

2) 在匹配过程中,匹配结果没有完全相等,那么这次匹配是失败的,就需要让在str2字符串的进行向后走的那个指针回归str2字符串的起始位置,所以从这我们看到需要有一个指针记录str2字符串的起始位置,所以我们重新创建一个那个进行str2字符串匹配的指针(s2指针),创建一个进行str1匹配的指针(s1指针),然后让cur指针++,并在下次匹配过程中,把cur指针赋给s1指针,再让s2指针回归起始位置(str2指针)。

代码实现:

#include<stdio.h>
#include<assert.h>

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	if (!*str2)//如果str2是空字符串,返回str1指向字符串的起始地址
		return str1;
	char* cur = str1;//当前位置的指针
	char* s1 = str1;//找到相同位置时,str1字符串向后走的指针
	char* s2 = str2;//找到相同位置时,str2字符串向后走的指针
	while (*cur)
	{
		s1 = cur;
		s2 = str2;//每次让s2回归str2字符串起始位置
		while (*s1 == *s2)
		{
			if (!*(s2+1))//匹配过程中,str2到达了'\0',匹配成功,返回cur指针
				return cur;
			s1++;
			s2++;
		}
		cur++;//如果s1指向字符与s2指向的字符不相等,就让当前指针+1
	}
	//说明str1指向的字符串都结束了,也没有返回与str2字符串相同的位置指向的起始地址
	return NULL;
}
int main()
{
	char arr[] = "You are very beautiful";
	char* str = "beautiful";

	char* ret = my_strstr(arr, str);
	printf("%s\n", ret);

	return 0;
}

运行结果:

 9  strtok函数

   同样,看一下函数原型与参数和返回值:

这个函数可能用的比较少,这个函数主要是用来进行字符串分隔的。strtok函数需要两个参数,第一个参数指向了要进行分隔的字符串,而第二个参数是用来指向第一个参数指向的字符串中分隔的分隔符,例如,有一个字符串为"haha@qq.com",其分隔为'@'和'.',则第一个参数就是指向这个字符串的一个地址,第二个参数就是指向"@."字符串的,被分隔符分隔的一个部分成为一个标记,如,"haha"就是一个标记,然后strtok函数在进行分隔时,为这个标记最后加上一个'\0',然后返回指向这个标记的一个地址,strtok函数会保存这个位置,如果没有更多的标记的话,会返回NULL,而最重要的一点就是,如果第一个参数为NULL的话,strtok函数就会从上一个保存在字符串中的位置开始,继续寻找下一个标记,这为我们使用strtok函数提供了很大的便利,如:

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

int main()
{
  char arr[] = "192.398.274.256";
  char* sep = ".";

  char* str = NULL;

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

  return 0;
}

运行结果是:

10  strerror函数 

  函数原型,参数及返回值:

 使用这个参数的时候我们会使用一个叫做errno的全局变量:

简单来说,就是如果程序出错的话,会把一个错误码放到errno(需要包含头文件errno.h)全局变量里面,而strerror函数就会将这个错误码翻译成一个错误信息,打印在屏幕上,如:

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

int main()
{
	FILE* pf = fopen("test.txt", "r");

	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

 运行结果是:

  如果觉得strerror函数太麻烦的话,可以直接使用perror函数:

 

这个函数可以直接打印错误信息,会在参数里面的字符串后加上一个冒号,然后打印错误信息,如:

#include<stdio.h>
#include<errno.h>

int main()
{
  FILE* pf = fopen("test.txt", "r");
  if (pf == NULL)
  {
    perror("fopen");
    return 1;
  }

  fclose(pf);
  pf = NULL;
  return 0;
}

运行结果是:

二  内存函数

  在这一章节里面,我们将讲解四个有关内存操作的函数,包括memcpy,memmove,memcmp,memset四个函数,前两个函数会讲解使用以及模拟实现,后两个函数只讲解怎么使用。

1  memcpy函数

1) 使用

  函数原型,参数及返回值:

这个函数主要用来实现内存块的拷贝,是将从source指针开始的nums个字节的数据拷贝放到destination指针所指向的内存位置,如:

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

int main()
{
  int arr1[5] = {1, 2, 3, 4, 5};
  int arr2[5] = { 0 };
  //将2,3,4拷贝放到arr2的后三个元素位置
  memcpy(arr2 + 2, arr1 + 1, 3*sizeof(int));

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

运行结果是:

  但是注意使用memcpy函数不能实现重复空间的拷贝,要不然会造成内存数据的覆盖,如:

 2) 模拟实现

  如果看过前面qsort函数的模拟实现,那就很好实现了,在这里要实现memcpy函数,只需要将一个字节一个字节的数据拷贝,拷贝nums次,这样这个函数就实现了:

#include<stdio.h>
#include<assert.h>

void* my_memcpy(void* dest,const void* src, size_t nums)
{
  assert(dest && src);
  void* ret = dest;

  while (nums--)
  {
    *(char*)dest = *(char*)src;
    dest = (char*)dest + 1;
    src = (char*)src + 1;
  }

  return ret;
}

int main()
{
  int arr1[5] = {1, 2, 3, 4, 5};
  int arr2[5] = { 0 };
  my_memcpy(arr2 + 2, arr1 + 1, 3*sizeof(int));

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

  运行结果:

2  memmove函数 

1) 使用

  函数原型,参数及返回值

memmove函数类似于memcpy函数,只不过这个函数比memcpy函数有一个优点,就是可以实现重复重复空间的拷贝,如:

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

int main()
{
  int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  //将前五个元素拷贝到下标为2,3,4,5,6的位置
  memmove(arr+2, arr, 5*sizeof(int));

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

  return 0;
}

 运行结果:

2) 模拟实现 

  memmove的模拟实现有这么几种情况:

(1) 如果dest指针在src之前,那么为了防止覆盖数据,我们应该从前往后拷贝。

(2) 如果dest在src之后,且有覆盖的数据(也就是dest指针在src指针和src指针加上nums个字节之间),那应该从后往前拷贝。

(3) 如果dest指针指向空间和src指向空间没有覆盖的部分,那从后往前或者从前往后拷贝都可以。

所以,综合来说,如果dest指针在src之前,那么就采用从前往后拷贝,如果dest指针在src指针之后,那就采用从后往前拷贝。

代码实现:

#include<stdio.h>
#include<assert.h>

void* my_memmove(void* dest, const void* src, size_t num)
{
  assert(dest && src);

  void* ret = dest;
  if (dest < src)//dest在src前面,从前往后拷贝
  {
    while (num--)
    {
      *(char*)dest = *(char*)src;
      dest = (char*)dest + 1;
      src = (char*)src + 1;
    }
  }

  else//dest在src后面,从后往前拷贝
  {
    dest = (char*)dest + num - 1;
    src = (char*)src + num - 1;
    while (num--)
    {
      *(char*)dest = *(char*)src;
      dest = (char*)dest - 1;
      src = (char*)src - 1;
    }

  }
 
  return ret;
}

int main()
{
  int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  //将前五个元素拷贝到下标为2,3,4,5,6的位置
  my_memmove(arr+2, arr, 5*sizeof(int));

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

  return 0;
}

运行结果是:

3  memset函数 

  函数原型,参数及返回值:

memset函数是将ptr指向的内存空间中num个字节数的内存空间的值都设置为value值,如:

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

int main()
{
  int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

  memset(arr, 0, 10*sizeof(int));

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

  return 0;
}

运行结果:

   但是使用memset函数需要有一个注意的点,就是其设置的数值是以字节为单位的,如以下的代码:

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

int main()
{
  int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

  memset(arr, 1, 10*sizeof(int));

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

  return 0;
}

可能刚开始会理所当然的认为输出结果是10个1,其实不然,输出结果其实是:

 

原因就是因为memset函数设置数值是以字节为单位的,而一个整型是4个字节,所以其实每一个整型在内存内存储的是00000001000000010000000100000001,转为10进制就是16843009。

  所以一般memset函数都是应用在把一个数组初始化为全0的情景下。

4  memcmp函数

  函数原型,返回值以及参数:

memcmp函数与strcmp函数的使用特别像,只不过memcmp函数是内存里的数据之间的比较,如:

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

int main()
{
  int arr1[5] = {1, 2, 256, 4, 5};
  int arr2[5] = {1, 2, 2, 2, 2};

  char arr3[] = "abbbb";
  char arr4[] = "aabcc";

  int ret1 = memcmp(arr1, arr2, 9);
  int ret2 = memcmp(arr3, arr4, 3);

  printf("%d\n", ret1);
  printf("%d\n", ret2);

  return 0;
}

 运行结果是:

通过运行结果可以看到对于arr1和arr2数组,比较9个字节的时候,比较的结果是arr1小于arr2的,可是arr1的那个数字是256,arr2的那个数字是2啊,比较结果怎么会小于0呢?其实这个是跟整数在内存中是如何存储有关的,相关知识将在下一篇文章中进行讲解。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值