深入理解并模拟实现函数

前言:

        我们前面已经学习了函数、指针等一系列知识,接下来,咱们一起来模拟实现strlen、qsort、strcpy、strcat、strcmp、strstr、memmove、memcpy、atoi,以及strncat、strncmp、strtok、strerror、memset、memcmp的使用。

一、使用并模拟实现qsort

        qsort可以对函数进行排序,使函数从无序变成有序。以下是官网解释:

        对其使用代码如下:

#include <stdio.h>      /* printf */
#include <stdlib.h>     /* qsort */

int values[] = { 40, 10, 100, 90, 20, 25 };

int compare(const void* a, const void* b)
{
    return (*(int*)a - *(int*)b);
}

int main()
{
    int n;
    qsort(values, 6, sizeof(int), compare);
    for (n = 0; n < 6; n++)
        printf("%d ", values[n]);
    return 0;
}

        运行结果如下: 

         相信大家对此函数有了初步了解,那下面进行模拟实现(冒泡实现)。

#include<stdio.h>
#include<assert.h>
int cmp(const void* p1,const void* p2)
{
	return ((*(int*)p1) - (*(int*)p2));
}
void sweap(void* p1, void* p2, int sz)
{
	for (int i = 0; i < sz; i++)
	{
		int cmp = *((char*)p1+i);
		*((char*)p1 + i) = *((char*)p2 + i);
		*((char*)p2 + i) = cmp;
	}
}
void bubble(int* arr, int n, int sz, int(*cmp)(void*, void*))//使用void*为了适应各种指针类型
{
    assert(arr);
	for (int i = 0; i < n-1; i++)
	{
		for (int j = 0; j < n - 1 - i; j++)
		{
			if (cmp((char*)arr + j * sz, (char*)arr + (j + 1) * sz) > 0)
			{
				sweap((char*)arr + j * sz, (char*)arr + (j + 1) * sz,sz);//使用char*能够访问到每个比特位
			}
		}
	}
}
int main()
{
	int arr[10] = { 1,2,3,8,9,4,5,1,3,10 };
	bubble(arr, sizeof(arr)/sizeof(arr[0]),sizeof(int), cmp);
	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d", arr[i]);
	}
	return 0;
}

二、使用模拟实现strlen 

        strlen可以实现统计数组中元素个数(包括空格)。官网解释如下:

        字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前⾯出现的字符个数(不包 含 '\0' )。

         参数指向的字符串必须要以 '\0' 结束。

        注意函数的返回值为size_t,是⽆符号的( 易错 )

         strlen的使⽤需要包含头⽂件 

        使用代码如下:

/* strlen example */
#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;
}

        对其模拟如下:

#include<stdio.h>
//法一:使用计数器方式
my_strlen(char* arr)
{
	int count = 0;
	while (*arr != 0)
	{
		count++;
		arr++;
	}
	return count;
}
//法二:使用递归方式
int my_strlen(char* arr)
{
	if (*arr == '\0')
	{
		return 0;
	}
	else
		return 1 + my_strlen(arr + 1);
}
//法三:使用指针-指针方式
int my_strlen(char* arr)
{
	char* s = arr;
	while (*arr != '\0')
	{
		arr++;
	}
	return arr - s;
}
int main()
{
	char arr[] = "12345";
	int ret = my_strlen(arr);
	printf("%d", ret);
	return 0;
}

        以上三种方式供大家学习。 

 三、使⽤和模拟实现strcpy 函数

        strcpy可将一个字符串拷贝到一个数组中。官网解释如下: 

        源字符串必须以 '\0' 结束。

        会将源字符串中的 '\0' 拷⻉到⽬标空间。

         ⽬标空间必须⾜够⼤,以确保能存放源字符串。

        ⽬标空间必须可修改。

        代码实现如下:

/* strcpy example */
#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;
}

        模拟实现如下: 

#include<stdio.h>
char* my_strcpy(char* dest, const char* src)
{
	char* str = dest;
	while (*dest++ = *src++)
	{
		;
	}
	return str;
}
int main()
{
	char arr1[] = "123456";
	char arr2[] = "abcdef";
	my_strcpy(arr1, arr2);
	printf("%s", arr1);
	return 0;
}

四、使用并模拟实现strcat 函数

          strcat函数是字符串追加函数,也就是在字符串后面追加另一个字符串。官网解释如下:

        使用 该函数代码如下:

#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;
}

        源字符串必须以 '\0' 结束。

        ⽬标字符串中也得有 \0 ,否则没办法知道追加从哪⾥开始。

        ⽬标空间必须有⾜够的⼤,能容纳下源字符串的内容。

        ⽬标空间必须可修改。 

        模拟实现如下:

#include<stdio.h>
char* my_strcat(char* destination, const char* source)
{
	char* ret = destination;
	while (*destination)
	{
		destination++;
	}
	while (*destination++ = *source++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[] = "hello";
	char arr2[] = " world";
	my_strcat(arr1,arr2);
	printf("%s", arr1);
	return 0;
}

五、使用并模拟实现strcmp函数 

        strcmp函数是C语言中字符串比较函数,用于比较两个字符串的大小。官网解释如下:

        如果相等则,返回零,若str>str2,则大于零,反之小于零。

        使用该函数如下:

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

int main()
{
    char key[] = "apple";
    char buffer[80];
    do 
    {
        printf("Guess my favorite fruit? ");
        fflush(stdout);
        scanf("%79s", buffer);
    } while (strcmp(key, buffer) != 0);
    puts("Correct answer!");
    return 0;
}

         模拟实现该函数如下:

#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1++ == *str2++)
	{
		if (*str1 == *str2)
		{
			return 0;
		}
	}
	return *str1 - *str2;
}
int main()
{
	char arr1[] = "abcd";
	char arr2[] = "ace";
	int ret = my_strcmp(arr1, arr2);
	if (ret > 0)
	{
		printf("arr1>arr2\n");
	}
	else if (ret < 0)
	{
		printf("arr1<arr2\n");
	}
	else
	{
		printf("arr1=arr2\n");
	}
	return 0;
}

六、使用并模拟实现strstr函数 

        strstr函数是在字符串str1中查找是否含有字符串str2,如果存在,返回str2在str1中第一次出现的地址;否则返回NULL。官网解释如下:

        对其使用代码如下:

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

int main()
{
    char str[] = "This is a simple string";
    char* pch;
    pch = strstr(str, "simple");
    if (pch != NULL)
        strncpy(pch, "sample", 6);
    puts(str);
    return 0;
}

        Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1. (函数返回字符串str2在字符串str1中第⼀次出现的位置)。

         The matching process does not include the terminating null-characters, but it stops there.(字符 串的⽐较匹配不包含 \0 字符,以 \0 作为结束标志)。 

         模拟实现该函数如下:

#include<stdio.h>
char* my_strstr(const char* str1, const char* str2)
{
	char* p = (char*)str1;
	if (!*str2)
	{
		return str1;
	}
	while (*p)
	{
		char* s1 = p;
		char* s2 = (char*)str2;
		while (*s1 && *s2 && !(*s1 - *s2))
		{
			s1++;
			s2++;
		}
		if (*s2)
		{
			return p;
		}
		p++;
	}
	return NULL;
}
int main()
{
	char arr1[] = "This is a simple string";
	char arr2[] = "simple";
	my_strstr(arr1, arr2);
	printf(arr2);
	return 0;
}

七、使用strtok函数

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

         sep参数指向⼀个字符串,定义了⽤作分隔符的字符集合

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

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

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

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

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

        官网解释如下:

        使用该函数:

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

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;
}

八、使用strerror函数 

        strerror函数可以把参数部分错误码对应的错误信息的字符串地址返回来。 在不同的系统和C语⾔标准库的实现中都规定了⼀些错误码,⼀般是放在 errno.h 这个头⽂件中说明 的,C语⾔程序启动的时候就会使⽤⼀个全⾯的变量errno来记录程序的当前错误码,只不过程序启动 的时候errno是0,表⽰没有错误,当我们在使⽤标准库中的函数的时候发⽣了某种错误,就会讲对应 的错误码,存放在errno中,⽽⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是 有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。

         官网解释如下:

        代码使用函数:

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

int main()
{
	FILE* pFile;
	pFile = fopen("unexist.ent", "r");
	if (pFile == NULL)
		printf("Error opening file unexist.ent: %s\n", strerror(errno));
	return 0;
}

九、使用strncmp函数 

        strcmp函数与strncmp函数从函数名看来基本一样,确实,它们确实大体上相同,区别为:n确定了比较多少个元素,官网解释如下:

        其返回值也是相同。         对其使用如下:

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

int main()
{
	char str[][5] = { "R2D2" , "C3PO" , "R2A6" };
	int n;
	puts("Looking for R2 astromech droids...");
	for (n = 0; n < 3; n++)
		if (strncmp(str[n], "R2xx", 2) == 0)
		{
			printf("found %s\n", str[n]);
		}
	return 0;
}

 十、使用strncat函数

        strncat函数与strcat函数的区别也类似与上述函数,只不过区别是拼接字节的个数仅此而已。官网解释如下:

        使用该函数如下:

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

int main()
{
	char str1[20];
	char str2[20];
	strcpy(str1, "To be ");
	strcpy(str2, "or not to be");
	strncat(str1, str2, 6);
	puts(str1);
	return 0;
}

十一、使用并模拟实现memcpy函数 

         memcpy函数是C语言中内存函数,其功能和strcpy函数类似,以下为官网解释:

        函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。

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

        如果source和destination有任何的重叠,复制的结果都是未定义的。 

        对其使用结果如下:

#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;
}

         对其模拟代码如下:

#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* destination, const void* source, size_t num)
{
	assert(destination && source);
	void* p = destination;
	while (num--)
	{
		*(char*)destination = *(char*)source;
		destination = (char*)destination + 1;
		source = (char*)source + 1;
	}
	return (p);
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	my_memcpy(arr2, arr1,40);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

十二、使用并模拟实现memmove函数

        memmove函数也是内存函数,它的作用是可以实现对目标字符串的拷贝并放入指定位置。官网解释如下:

        和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;
}

        模拟实现如下:

#include<stdio.h>
#include<assert.h>
void* my_memmove(void* dest, const void* sour, size_t num)
{
	void* p = dest;
	if (dest < sour)
	{
		while (num--)
		{
			*(char*)dest = *(char*)sour;
			dest = (char *)dest+1;
			sour = (char*)sour + 1;
		}
	}
	else
	{
		dest = (char*)dest + num - 1;
		sour = (char*)sour + num - 1;

		while (num--)
		{
			*(char*)dest = *(char*)sour;
			dest = (char*)dest - 1;
			sour = (char*)sour - 1;
		}
	}
	return p;
}
int main()
{
	char str[] = "memmove can be very useful......";
	my_memmove(str + 20, str + 15, 11);
	puts(str);
	return 0;
}

十三、使用memset函数

        memset是⽤来设置内存的,将内存中的值以字节为单位设置成想要的内容。官网解释如下:

        使用函数如下:

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

int main()
{
	char str[] = "almost every programmer should know memset!";
	memset(str, '-', 6);
	puts(str);
	return 0;
}

十四、使用 memcmp函数

        该函数即为比较函数与上文strncmp函数类似。官网解释如下:

        使用该函数如下: 

#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;
}

 十五、使用并模拟实现atoi函数

它的功能:
解析将其内容解释为整数的 C 字符串,该整数作为 类型的值返回。
该函数首先根据需要丢弃尽可能多的空格字符(如 ),直到找到第一个非空格字符。然后,从此字符开始,取一个可选的首字母加号或减号,后跟尽可能多的 10 进制数字,并将它们解释为数值。
字符串可以在构成整数的字符之后包含其他字符,这些字符将被忽略,并且对此函数的行为没有影响。
如果 中的第一个非空格字符序列不是有效的整数,或者由于为空或仅包含空格字符而不存在此类序列,则不执行转换并返回零。

         官网解释:

        使用该函数如下:

#include <stdio.h>
#include <string.h>
int main()
{
	int i;
	char buffer[256];
	printf("Enter a number: ");
	fgets(buffer, 256, stdin);
	i = atoi(buffer);
	printf("The value entered is %d. Its double is %d.\n", i, i * 2);
	return 0;
}

        模拟实现如下:

#include<stdio.h>
#include<assert.h>
#include<ctype.h>
#include<stdlib.h>
enum State
{
	INVAILD,
	VAILD
}a;
int my_atoi(const char* str)
{
	assert(str);
	if (*str == '\0')
	{
		return 0;
	}
	while (isspace(*str))
	{
		str++;
	}
	int flag = 1;
	if (*str == '+')
	{
		flag = 1;
		str++;
	}
	if (*str == '-')
	{
		flag = -1;
		str++;
	}
	long long ret = 0;
	while (*str != '\0')
	{
		if (isdigit(*str))
		{
			ret = ret * 10 + flag * (*str - '0');
			if (ret > INT_MAX)
			{
			return INT_MAX;
			}
			if (ret < INT_MIN)
			{
			return INT_MIN;
			}
		}
		else
		{
			 (int)ret;
		}
		str++;
	}
	if (*str == '\0')
	{
		 a =VAILD;
	}
	return (int)ret;
}
int main()
{
	char arr[20] = "-123456";
	int ret = my_atoi(arr);
	if (a == VAILD)
	{
		printf("合法转化:%d\n", ret);
	}
	else if(a == INVAILD)
	{
		printf("非法转化:%d\n", ret);
	}
	return 0;
}

完! 

相关推荐

  1. 深入理解 c++ 函数模板

    2024-03-28 15:32:07       45 阅读
  2. 深入理解使用 MySQL 的 SUBSTRING_INDEX 函数

    2024-03-28 15:32:07       22 阅读
  3. 理解实现C语言中的strcpy函数

    2024-03-28 15:32:07       59 阅读
  4. 深入理解测试HttpResponse —— 关键知识和实践

    2024-03-28 15:32:07       47 阅读
  5. 【C++】从list模拟实现深入理解iterator

    2024-03-28 15:32:07       32 阅读
  6. 深入理解 Linux 函数参数

    2024-03-28 15:32:07       47 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-28 15:32:07       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-28 15:32:07       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-28 15:32:07       82 阅读
  4. Python语言-面向对象

    2024-03-28 15:32:07       91 阅读

热门阅读

  1. 突破编程_C++_查找算法(二叉树查找)

    2024-03-28 15:32:07       39 阅读
  2. Spring全家桶涉及的注解

    2024-03-28 15:32:07       35 阅读
  3. Element-UI中el-cascader级联选择器获取label值

    2024-03-28 15:32:07       42 阅读
  4. Bean对象拷贝工具封装

    2024-03-28 15:32:07       40 阅读
  5. 若依分离版 —引入echart连接Springboot后端

    2024-03-28 15:32:07       42 阅读
  6. openGauss的索引组织表

    2024-03-28 15:32:07       40 阅读