剖析字符查找,错误信息报告,内存操作函数的功能与实现

目录

一. 前言

二. 正文

2. 1 字符查找

2.11 strstr

2.12 strtok

2.2  错误信息报告

2.3  内存操作 函数

2.31  memcpy

2.32   memmove-- 内存移动

2.33   memcmp--内存比较

2.34  memset---内存设置


一. 前言

     本小节将跟大家分享有关字符查找,错误信息报告,内存操作函数的功能与实现。

二. 正文

2. 1 字符查找

2.11 strstr

char * strstr ( const char * , const char * )
  •  参数一: 被查找的主字符串
  •  参数二:  需要查找的子字符串

功能:从主字符串中,寻找子字符串,如果不存在返回空指针;若存在则返回子字符串在主字符串中首元素的地址

代码实现如下:

char* my_strstr(const char* p1, const char *p2)
{
	assert(p1 && p2);
	char* cnt = p1;
	char* cur = p2;
	while (*cnt)
	{
		char* room = cnt;
		while (*cnt && *cnt == *cur)
		{
			cnt++;
			cur++;
			if (*cur == '\0')
			{
				return room;
			}
		}
		cnt = room;  // 如果不匹配就重新指向进入判断的起点
		cur = p2;    // 如果不匹配就重新指向进入的起点
		cnt++;
	}
	return NULL;
}
int main()
{
	char a[20] = "ahuangCCChinaahuang";
	char b[20] = "China";
	printf("%s\n", my_strstr(a, b));
	return 0;
}

2.12 strtok

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

返回类型是char* ,如果找到分割符后会返回这一段字符串的首字母的地址。

  • 参数一: 是要分割的字符串。(一般要创建一个临时字符数组,因为需要将标记字符替换为“\0”,会修改数据,但我感觉会有性能损失)

值得注意的是: 第一次进行strtok时,输入字符串指针,分割一次;第二次使用时,则参数一输入NULL,因为里面会有一个static 修饰的变量,会保存分割符下一个字符的地址。

  • 参数二: sep是分割符的集合字符串。

运用代码可以这样写:

int main()
{
    char a[30] = "[email protected]";
    char copy[30] = { 0 };
    strcpy(copy, a);
    char* tok = "@.";
    char *pro = NULL;  
    // 利用该函数会保存分割符号下一个字符指针的特性
    for (pro = strtok(copy, tok); pro != NULL; pro = strtok(NULL, tok))
    {
        printf("%s\n", pro);
    }
    return 0;
}

2.13 atoi函数

int atoi( const char *string );

  • 功能: 将字符串转化为数字,例如:“123455” 转化为123455; “-126432”转为-126432;  “123.432" 转为 123。
  • 实现思路:如果遇到空格符跳过,然后遇到正负符号保存,接着读取字符内容,如果不在'0' ~'9'就不是数字字符,即停止循环,返回0。

 功能实现:

#include<stdio.h>
#include<limits.h>

int my_atoi(const char* str)
{
	int sign = 1;
	// 跳过空格
	while (*str == ' ')
	{
		str++;
	}
	// 找出正负
	if (*str == '-' || *str == '+')
	{
		if (*str == '-')
		{
			sign = -1;
		}
		str++;
	}
  //  开始转化
	int sum = 0;
	while (*str <= '9' && *str >= '0')
	{
		sum = 10 * sum + (*str - '0');
		if (sum < 0 && sign == 1)   // 正数超出
		{
			sum = INT_MAX;
			break;
		}
		else if (sum < 0 && sign == -1)    // 负数超出
		{
			sum = INT_MIN;
			break;
		}
		str++;
	}
	return sum * sign;
}
int main()
{
	char* s1 = "333640";
	char* s2 = "-12345";
	char* s3 = "123.3113";
	char* s4 = "-8362865623872387698";
	char* s5 = "+246653278";

	printf("%d\n", my_atoi(s1));
	printf("%d\n", my_atoi(s2));
	printf("%d\n", my_atoi(s3));
	printf("%d\n", my_atoi(s4));
	printf("%d\n", my_atoi(s5));
	return 0;
}

 结果:


2.2  错误信息报告

     我们在编写程序时,调用库函数失败后,会返回错误码(errno), 一般我们想要获取错误码时,需要一个头文件(#inlcude <errno.h>),  里面会有一个全局变量 errno 用来接收错误码的。

strerror功能是将编号转化为对应错误信息的首字母地址。

运用代码如下:

#include<stdio.h>
#include<errno.h>  // 当我们需要用errno接收错误码时,需要的头文件
#include<stdlib.h>  
#include<string.h>
#include<limits.h>   // 可以调出整型数据范围的头文件
int main()
{
    printf("%s\n", strerror(errno));
    printf("%s\n", strerror(2));
    printf("%s\n", strerror(3));
    printf("%s\n", strerror(4));
    int* a = (int*)malloc(sizeof(int) * INT_MAX);
    if (a == NULL)
    {
        perror("malloc");   //perror("自定义内容")
    }
    return 0;
}

这里对strerror函数和perror函数作一些区别

      strerror函数的参数是错误码,然后转化为对应错误消息首字符的地址,而perror则是保存最近一次错误码(因此错误码会被迭代),然后打印 自定义字符串部分和错误码对应错误信息。


2.3  内存操作 函数

头文件:#include<string.h>

2.31  memcpy

void * memcpy ( void * destination, const void * source, size_t num );

destination :  拷贝开始的起始地址

source:         被拷贝的起始地址

num   :        拷贝内容的字节数

  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
  • 这个函数在遇到 '\0' 的时候并不会停下来。
  • 如果source和destination有任何的重叠,复制的结果都是未定义的

核心思维是在内存中一个字节一个字节拷贝数据。我们可以回想,strcpy其能拷贝字符串,但不能拷贝其他数据,而memcpy则是拷贝指定内存的任何数据

运用代码如下:

#include<stdio.h>
#include<string.h>
int main()
{
	int a[30] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int b[30] = { 0 };
	memcpy(b, a, 16);  // %s是对字符串数据进行打印,则进行监视。
	return 0;
}

结果:

实现代码如下:

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

// 自己模拟实现的函数不能实现p1与p2区域覆盖原地拷贝的情况
// 但c语言的库函数memcpy可以实现原地覆盖拷贝
void* my_memcpy(void* p1, const  void* p2, int count)
{
	assert(p1 && p2);
	void* ret = p1;
	while (count--)
	{
		*(char*)p1 = *(char*)p2;
		p2 = (char*)p2 + 1;
		p1 = (char*)p1 + 1;
	}
	return ret;
}
int main()
{
	int a[30] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int b[30] = { 0 };
	my_memcpy(b, a, 16);
	return 0;
}

2.32   memmove-- 内存移动

     void * memmove ( void * destination, const void * source, size_t num );

 memmove函数跟memmove函数差不多,只是能更好的解决本地覆盖拷贝情况

实现过程中我们会发现三种情况:

 

实现代码如下:

void* my_memmove(void* p1, void* p2, int count)
{
    assert(p1 && p2);
    void* ret = p1;
    if (p1 > p2) // 后->前
        {
            while (count)
            {
                *((char*)p1 + count) = *((char*)p2 + count);
                count--;
            }
        }
        else  // 前->后
        {
            while (count--)
            {
                *(char*)p1 = *(char*)p2;
                p1 = (char*)p1 + 1;
                p2 = (char*)p2 + 1;
            }
        }
    return ret;
}

int main()
{
    int a[30] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
                //3 4 5 6 5 6 7 8 9 10
    int b[30] = { 0 };
    my_memmove(a, a + 2, 16);  // %s是对字符串数据进行打印,这里我们通过监视查看。
    return 0;
}

2.33   memcmp--内存比较

int memcmp ( const void * ptr1, const void * ptr2, size_t num );

返回值:  ptr1  >  ptr2       返回1

                ptr1   <  ptr2      返回-1

                ptr1   ==  ptr2    返回0

其核心思想就是,数据在内存中存储(默认为小端),进行一个字节一个字节的比较。

运用代码如下:

#include<stdio.h>
#include<string.h>
int main()
{
	int a[10] = { 1, 2, 3,4 };
	int b[10] = { 1,2 ,3, 0x11223305};
	printf("%d\n", memcmp(a, b, 13));
	return 0;
}

我们查看内存如下:

 

 

 由以上两图可知在第12个字节时还相同,但在13个位时已经小于。

2.34  memset---内存设置

 void *__cdecl memset(void *_Dst, int _Val, size_t _Size);

 _Dst:  目标数据的起始地址。

_Val:    替换为的数

_Size:  字节数

功能: 将指定内存范围内的数据全部覆盖为_Val

功能应用:

#include<stdio.h>
#include<string.h>
int main()
{
	int a[10] = { 1, 2, 3, 4, 5,6 };
	memset(a, 1, 12);  // 在小端字节上运行
	return 0;
}

 内存如下:

 可知从首地址后的12个字节全部覆盖为1。

三. 结语

   本小节到此结束,感谢小伙伴的浏览,如果有什么疑问欢迎大家在评论区评论。

猜你喜欢

转载自blog.csdn.net/qq_72112924/article/details/129971539