C语言进阶——字符函数

news2025/2/27 19:48:20

目录

一.前言

二.strlen

1.函数介绍

2.三种模拟实现

三.长度不受限制函数

1.strcpy

模拟实现

2.strcat

模拟实现

3.strcmp

模拟实现

四.长度受限制函数

1.strncpy

模拟实现

2.strncat

模拟实现

3.strncmp

模拟实现

五.字符串查找

1.strstr

模拟实现

2.strtok

六.错误信息报告

strerror

七.字符操作

1.字符分类函数

2.字符转换

八.内存操作函数

1.memcpy

模拟实现

2.memmove

模拟实现

3.memsrt

4.memcmp

结束语


一.前言

我们在C语言的从程序代码编写中,对字符和字符串的处理相当频繁,但是我们还知道,C语言本身并没有字符串类型。而字符串通常放在【常量字符串】或者【字符数组】中。其中,字符串常量适用于那些对它不做修改的字符串函数。

二.strlen

1.函数介绍

功能求字符串长度

#include<stdio.h>
#include<string.h>
int main()
{
	printf("%d\n", strlen("abcdef"));//6
	return 0;
}

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

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

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

size_t strlen ( const char * str ); 

strlen库函数的返回类型是size_t,是无符号数。
sizeof -- 操作符 -- 计算大小的。而size_t本质:unsigned int,size_t是专门为sizeof的返回类型设计的 。既然size_t是无符号整型的,所以值恒>0

2.三种模拟实现

计算器

#include <stdio.h>
#include<assert.h>
int my_strlen(const char* str)
{
	assert(str);
	int count = 0;
	while (*str != '\0')//判断字符串是否结束
	{
		count++;
		str++;
	}
	return count;
}
int main()
{
	int len = my_strlen("abcdef");
	printf("%d\n", len);  // 6
	return 0;
}

递归

#include<stdio.h>
int my_strlen(char* s)
{
	if (*s == '\0')
		return 0;
	else
		return 1 + my_strlen(s + 1);
}
int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n", len);  // 6
	return 0;
}

指针减指针 

#include<stdio.h>
int my_strlen(char* s)
{
	char* p = s;
	while (*p != '\0')
		p++;
	return p - s;
}
int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);  //6
	printf("%d\n", len);
	return 0;
}

三.长度不受限制函数

1.strcpy

       strcpy 函数(string copy)的作用是,可以将字符串从源地址复制至目的地址,通俗来讲就是用来实现字符串的复制和拷贝。并且它会将源地址内的结束标识符 ' \0 ' 一并拷贝过去,因此源地址必须以 ' \0 ' 结尾,且目的地址也将以结束标识符结尾。并且,因为其作用为拷贝字符串,因此目标地址内的空间必须足够大,要有足够的空间容纳下源地址内的字符串,同时目的地址的空间必须是可变、可修改的。

注意事项

  1. 源字符串必须以 '\0' 结束。
  2. 会将源字符串中的 '\0' 拷贝到目标空间。
  3. 目标空间必须足够大,以确保能存放源字符串。
  4. 目标空间必须可变。
#include<stdio.h>
#include<string.h>
 
int main()
{
	const char arr1[] = "Hellow!";
	const char arr2[10] = { 0 };
 
	printf("Before copy , the char inside arr1 are %s\n", arr1);
	printf("Before copy , the char inside arr2 are %s\n", arr2);
	printf("\n");
 
	strcpy(arr2, arr1);
    //strcpy函数使用格式为:strcpy(目的地址, 源地址);
 
	printf("After copy , the char inside arr1 are %s\n", arr1);
	printf("After copy , the char inside arr2 are %s\n", arr2);
 
	return 0;
}

 我们初始化字符数组 arr 中的内容,并使 arr2 中内容为空。首先我们将两个字符串进行打印,验证拷贝前两字符串中的存储内容。接着我们使用 strcpy 函数将字符数组 arr1 中的字符串拷贝至 arr2 中,并在此打印两数组中的内容对拷贝结果进行验证。

模拟实现

基础模拟

char* my_strcpy(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest && src);
	while (*dest++ = *src++)//拷贝过去,往后慢慢走加一跳地址
	{
		;
	}
	return ret;//返回改完的起始地址,所以前面ret保留一下
}

int main()
{
	//char* p = "abcdefghiqwer";//保证空间能修改,这样都不能被修改,是错误的

	char arr1[20] = "";//保证空间足够
	char arr2[] = "hello bit";
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

对应上面的用法来写 

#define _CRT_SECURE_NO_WARNINGS 1
 
#include<stdio.h>
#include<assert.h>
 
char* my_strcpy(char* p2, const char* p1)
{
	assert(p2 != NULL);
	assert(p1 != NULL);
	//等价于assert(p2 && p1);
 
	char* ret = p2;
    //将目的地址作为返回值
 
	while (*p2++ = *p1++)
	{
		;
	}
 
    //返回目的地址的作用是为了实现链式访问
    return ret;
}
 
int main()
{
	const char arr1[] = "Welcome!";
	const char arr2[10] = { 0 };
 
	printf("Before copy , the char inside arr1 are %s\n", arr1);
	printf("Before copy , the char inside arr2 are %s\n", arr2);
	printf("\n");
 
	my_strcpy(arr2, arr1);
 
	printf("After copy , the char inside arr1 are %s\n", arr1);
	printf("After copy , the char inside arr2 are %s\n", arr2);
 
	return 0;
}

2.strcat

  strcat 函数(string catenate)的作用是,将源地址的字符串追加补充至目的地址处。与字符串拷贝函数相同,它在进行补充追加时是从目的地址的结束标识符处 ' \0 ' 开始追加的,追加至源地址的结束标识符处停止。且它同样要求目标地址内的空间必须足够大,要有足够的空间容纳下源地址内的字符串,同时目的地址的空间必须是可变、可修改的。

注意事项

  1. 源字符串必须以 '\0' 结束。
  2. 目标空间必须有足够的大,能容纳下源字符串的内容。
  3. 目标空间必须可修改。
#include<stdio.h>
#include<string.h>
 
int main()
{
	const char arr1[20] = "Hellow!";
	const char arr2[20] = "Welcome!";
 
	printf("Before catenate , the char inside arr1 are %s\n", arr1);
	printf("Before catenate , the char inside arr2 are %s\n", arr2);
	printf("\n");
	
	strcat(arr2, arr1);
	
	printf("After catenate , the char inside arr1 are %s\n", arr1);
	printf("After catenate , the char inside arr2 are %s\n", arr2);
 
	return 0;
}

  我们同样首先定义并初始化两个字符数组,接着打印他们验证他们各自内部的存储内容。然后通过使用stract 函数,我们将字符数组 arr21中的内容,成功的追加补充到了字符数组 arr1 中,并再次进行了打印,验证追加补充的结果。

    注意,strcat 函数无法追加自己。原因很好理解,我们在定义时就已经固定了字符数组的储存空间了,当追加自己时,相当于将自己与和与自己相同大小的字符数组,即两倍自身大小的数据放入自己的储存空间中,可想而知一定是不可行的。

模拟实现

基础实现,切记不能自己给自己追加

char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;

	//1. 找目标空间的\0
	while (*dest)
	{
		dest++;
	}
	//2. 追加
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

int main()
{
	char arr[20] = "hello ";
	my_strcat(arr, "word");

	char arr[20] = "hello ";
	char arr2[] = "world";
	my_strcat(arr, arr2);

	char arr[20] = "hello ";
	char* p = "world";
	my_strcat(arr, p);

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

	return 0;
}

对应上面用法的题目来写

#define _CRT_SECURE_NO_WARNINGS 1
 
#include<stdio.h>
#include<assert.h>
 
char* my_strcat(char* p2, char* p1)
{
	assert(p2 && p1);
	char* ret = p2;
	//查找目标空间中的结束标识符,找到后停止
	while (*p2)
	{
		p2++;
	}
	//从结束标识符开始追加
	//追加:
	while (*p2++ = *p1++)
	{
		;
	}
	return ret;
}
 
int main()
{
	const char arr1[20] = "Hellow!";
	const char arr2[20] = "Welcome!";
 
	printf("Before catenate , the char inside arr1 are %s\n", arr1);
	printf("Before catenate , the char inside arr2 are %s\n", arr2);
	printf("\n");
	
	my_strcat(arr2, arr1);
	
	printf("After catenate , the char inside arr1 are %s\n", arr1);
	printf("After catenate , the char inside arr2 are %s\n", arr2);
 
	return 0;
}

3.strcmp

 strcmp 函数(string compare)的作用为按照顺序依次比较两字符串对应位置字符的 ASCII 码值(注意不是比较两字符串的长度),直到结束标识符 ' \0 ' 或对应位置的字符不同。若比较至结束标识符都没有不同则两字符串相等,若两字符串对应位置字符有不同,对应位置上字符 ASCII 码值小的字符串小于另一个字符串。

注意

  1. 第一个字符串大于第二个字符串,则返回大于0的数字
  2. 第一个字符串等于第二个字符串,则返回0
  3. 第一个字符串小于第二个字符串,则返回小于0的数字
#include<stdio.h>
#include<string.h>
 
int main()
{
	const char arr1[] = "abcd";
	const char arr2[] = "abz";
 
	int ret = strcmp(arr1, arr2);
	//若arr1大于arr2,则返回大于零的数
	//若arr1等于arr2,则返回等于零的数
	//若arr1小于arr2,则返回小于零的数
 
	if (ret > 0)
	{
		printf("arr1 > arr2\n");
	}
	else if (ret = 0)
	{
		printf("arr1 = arr2\n");
	}
	else
	{
		printf("arr1 < arr2\n");
	}
 
	return 0;
}

首先我们定义并初始化两个字符数组,接着对两个数组进行比较,根据 strcmp 函数的比较结果得到返回值,再根据返回值反馈我们想要的字符串比较结果。

       该函数的作用并不是比较字符串的长短,而是比较对应位置字符 ASCII 码值的大小

模拟实现

基础实现

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')//相等的时候,看是不是\0
			return 0;
		str1++;
		str2++;
	}
	if (*str1 > *str2)
		return 1;
	else
		return -1;

	return *str1 - *str2;//上面if——else一段可以这样写,很简便
}


int main()
{
	char arr1[] = "abq";
	char arr2[] = "abcdef";
	int ret = strcmp(arr1, arr2);//库函数使用,对应字符位置去比较,比较大小的,对应字符串位置上的内容

	int ret = my_strcmp(arr1, arr2);//模拟实现

	if (ret>0)
		printf("arr1>arr2\n");

	return 0;
}

根据上面用法写法 

#define _CRT_SECURE_NO_WARNINGS 1
 
#include<stdio.h>
#include<assert.h>
 
//模拟实现strcmp函数:
int my_strcmp(const char* p1, const char* p2)
{
	assert(p1 && p2);
	//找到字符不同的对应位:
	while (*p1 == *p2)
	{
		if (*p1 == '\0')
		{
			return 0;
		}
		p1++;
		p2++;
	}
	if (*p1 > *p2)
	{
		return 1;
	}
	else
	{
		return -1;
	}
}
 
int main()
{
	const char arr1[] = "abcd";
	const char arr2[] = "abz";
	
	int ret = my_strcmp(arr1, arr2);
	//若arr1大于arr2,则返回大于零的数
	//若arr1等于arr2,则返回等于零的数
	//若arr1小于arr2,则返回小于零的数
	
	if (ret = 1)
	{
		printf("arr1 > arr2\n");
	}
	else if (ret = 0)
	{
		printf("arr1 = arr2\n");
	}
	else
	{
		printf("arr1 < arr2\n");
	}
 
	return 0;
}

四.长度受限制函数

1.strncpy

  strncpy函数(string number copy)的作用为将指定长度的字符串复制到字符数组中,即表示把源地址中字符串开始的前n个字符拷贝到目的地址中。与 strcpy 相同,它同样会将源地址内的结束标识符 ' \0 ' 一并拷贝过去,因此源地址必须以 ' \0 ' 结尾,且目的地址也将以结束标识符结尾。并且,因为其作用为拷贝字符串,因此目标地址内的空间必须足够大,要有足够的空间容纳下源地址内的字符串,同时目的地址的空间必须是可变、可修改的。

注意

  1. 此函数不受到 '\0' 的影响,拷贝num个字符从源字符串到目标空间。
  2. 目标空间必须有足够的大,能容纳下源字符串的内容。
  3. 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
#include<stdio.h>
#include<string.h>
 
//strncpy函数的使用:
int main()
{
	const char arr1[] = "Hellow!Welcome!";
	const char arr2[10] = { 0 };
 
	printf("Before copy , the char inside arr1 are %s\n", arr1);
	printf("Before copy , the char inside arr2 are %s\n", arr2);
 
	strncpy(arr2, arr1, 7);
 
	printf("After catenate , the char inside arr1 are %s\n", arr1);
	printf("After catenate , the char inside arr2 are %s\n", arr2);
 
	return 0;
}

 我们看到,使用该函数,我们成功的将字符数组 arr1 中的前七个字符拷贝到了 字符数组 arr2 中,并且通过前后两次的反馈打印验证了按指定长度拷贝操作成功完成。

        注意:若源字符串的长度小于我们传递过去的参数,则拷贝完源字符串之后,将会在目标后追加字符 ' 0 ',直到拷贝至参数规定个数。
 

模拟实现

#include<stdio.h>
#include<assert.h>
char* my_strncpy(char* dest, const char* str, size_t n)
{
	assert(dest && str);
	char* ret = dest;
	while (n--)
	{
		*dest++ = *str++;
	}
	return ret;
}
int main()
{
	char arr1[] = "xxxxxxxxxx";
	char arr2[] = "abcde";
	printf("%s\n", my_strncpy(arr1, arr2, 4));  // abcdxxxxxx
	return 0;
}

2.strncat

strncat 函数(string num catenate)的作用为从源地址处将指定长度的字符串追加补充到目的地址中。与 strcat 函数类似,它在进行补充追加时也是从目的地址的结束标识符处 ' \0 ' 开始追加的,不同的是追加至参数限制的字符数处停止。但它同样要求目标地址内的空间必须足够大,要有足够的空间容纳下出家补充的字符串,同时目的地址的空间必须是可变、可修改的。

#include<stdio.h>
#include<string.h>
 
int main()
{
	const char arr1[] = "Hellow!Welcome!";
	const char arr2[20] = "Welcome!";
 
	printf("Before copy , the char inside arr1 are %s\n", arr1);
	printf("Before copy , the char inside arr2 are %s\n", arr2);
 
	strncat(arr2, arr1, 7);
 
	printf("After catenate , the char inside arr1 are %s\n", arr1);
	printf("After catenate , the char inside arr2 are %s\n", arr2);
 
	return 0;
}

  我们可以看到,首先定义并初始化两个字符数组,接着打印追加补充之前它们各自空间内的内容进行确认,然后我们使用了 strncat 函数有限制的从数组 arr1 向 arr2 中追加补充了七个字符,最后进行了打印反馈,验证了我们的有限制追加补充结果。

模拟实现

#include<stdio.h>
#include<assert.h>
char* my_strncat(char* dest, const char* str, size_t n)
{
	assert(dest && str);
	char* ret = dest;
	while (*dest)
	{
		dest++;
	}
	while (n--)
	{
		*dest++ = *str++;
	}
	*dest = '\0';
	return ret;
}
int main()
{
	char arr1[20] = "hello\0xxxxx";
	char arr2[] = "bitxxxxx";
	printf("%s\n", my_strncat(arr1, arr2, 3)); //hellobit
	return 0;
}

3.strncmp

    strncmp 函数(string number compare)的作用为有限制的按照顺序依次比较两字符串对应位置字符的 ASCII 码值(注意不是比较两字符串的长度),直到参数限制位数位置上全部比较结束或对应位置的字符不同。若参数限制位数位置上的字符都比较结束且都没有不同则两字符串相等,若两字符串对应位置字符有不同,对应位置上字符 ASCII 码值小的字符串小于另一个字符串。

#include<stdio.h>
#include<string.h>
 
int main()
{
	const char arr1[] = "abcd";
	const char arr2[] = "abz";
 
	int ret = strncmp(arr1, arr2, 3);
    //比较前 3 个字符
	//若arr1大于arr2,则返回大于零的数
	//若arr1等于arr2,则返回等于零的数
	//若arr1小于arr2,则返回小于零的数
 
	if (ret > 0)
	{
		printf("arr1 > arr2\n");
	}
	else if (ret = 0)
	{
		printf("arr1 = arr2\n");
	}
	else
	{
		printf("arr1 < arr2\n");
	}
 
	return 0;
}

 其作用原理与作用过程,与 strncmp 函数十分类似,唯一不同便是限制了字符比较的位数,所以在此不再做过多的阐述,可以参考 strncmp 函数进行理解。

    该函数的作用也不是比较字符串的长短,而是比较对应位置字符 ASCII 码值的大小

模拟实现

#include<stdio.h>
#include<assert.h>
int my_strncmp(char* dest, const char* str, size_t n)
{
	int ret = 0;
	assert(dest && str);
	while (n && !(*dest - *str))
	{
		n--;
		dest++;
		str++;
	}
	if (n && *dest - *str > 0)
		return 1;
	else if (n && *dest - *str < 0)
		return -1;
	return ret;
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcqqqqq";
	printf("%d\n", my_strncmp(arr1, arr2, 3)); //0
	return 0;
}

五.字符串查找

1.strstr

 strstr 函数(string string)的作用为从一个字符串中寻找其字串,通俗来讲就是从一个字符串中寻找另一个字符串。若找到目标字串则返回指向目标字串的指针,若没有找到目标字串则返回空指针(不返回)。

#include<stdio.h>
#include<string.h>
 
int main()
{
	const char arr1[] = "abcdefg";
	const char arr2[] = "cde";
 
	char* ret = strstr(arr1, arr2);
	//从ar1中寻找arr2
	if (ret == NULL)
	{
		printf("找不到该字符串!\n");
	}
	else
	{
		printf("成功找到该字符串'%s'!\n", ret);
	}
 
	return 0;
}

 我们定义并初始化了两个字符数组,并使用 strstr 函数进行字串寻找处理,并使用字符型指针接收汉函数的返回值,最后,对接收了返回值的指针 ret 进行判断,若为空则确定为没有从数组 arr1 中找到字串 arr2,否则便通过返回值,使用指针对字串地址内的数据进行访问。

模拟实现

char* my_strstr(const char* str1, const char* str2)
{

}
	assert(str1 && str2);
	if (*str2 == '\0')
	{
		return (char*)str1;
	}
	const char* s1 = NULL;
	const char* s2 = NULL;
	const char* cp = str1;

	while (*cp)
	{
		s1 = cp;
		s2 = str2;
		while (*s1 !='\0' && *s2!='\0' && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return (char*)cp;
		}
		cp++;
	}

	return NULL;
}

int main()
{
	char arr1[] = "abbbcdbbcef";
	char arr2[] = "bbc";
	char* ret = my_strstr(arr1, arr2);//模拟实现

	if (ret == NULL)
	{
		printf("找不到\n");
	}
	else
	{
		printf("%s\n", ret);
	}

	return 0;
}

最开始不相等的时候,str1往后走,然后相等然后需要记住这个相等的地址,因为成功了要返回这个地址,不成功了,str1要回来这个地址str1和str2同时往后走,走到不相等的时候,我的str2要回到开始的地址所以我们写s1和s2,去代替str1和str2往后走,如果失败了,可以重新让s2接收str2初始的地址再写一个cp去保存开始相等的那个地址,这样失败了,可以让s1接收cp地址,再往后找,成功了,就返回cp开始相等的地址

2.strtok

  strtok 函数(string token)的作用为将字符串分解为一组字符串。听起来似乎很难理解,其实很简单。该函数有两个数组作为参数,它的实际作用便是将其中一个数组组为分割数组,在另一个数组中寻找这些“分割符”,并在分割符处将这个数组内的字符串加上结束标识符 ' \0 ' ,将其分割成一组(多个)字符串。若第一个参数不为 NULL ,将找到字符数组中的第一个标记并保存它在字符串中的位置;若第一个参数为 NULL ,将在同一个字符串中被保存的位置开始,查找下一个标 记。

#include<stdio.h>
#include<string.h>
 
int main()
{
	char arr1[] = "1254594572@QQ.COM";
	char arr2[30] = { 0 };
	strcpy(arr2, arr1);
 
	const char* arr3 = "@.";
 
	printf("账号:%s\n", strtok(arr2, arr3));
	//找到第一个标记停止
	printf("服务商:%s\n", strtok(NULL, arr3));
	//从保存好的位置开始往后找
	printf("网址后缀:%s\n", strtok(NULL, arr3));
	//从保存好的位置开始往后找
 
	return 0;
}

 首先我们要知道,strtok 函数是会对数组本身进行操作的,所以我们为了保护原始数据,在定义并初始化好字符数组之后,又定义了一个新的数组并将原始数据拷贝过去,作为临时拷贝供我们进行操作。接着我们定义并初始化了分割符数组,函数将根据分割符数组 arr3 对 临时拷贝 arr2 进行分割。第一次执行函数时之前没有标记,于是直接进行操作找到第一个标记并分割打印。此时就已经存在标记了,再连续两次找到前一次执行作下的标记按照分割符将数组分割完毕并打印。

        但是上面的代码风格较为简陋、复杂,我们可以将上面段代码优化为:

#include<stdio.h>
#include<string.h>
 
int main()
{
	char arr1[] = "1254594572@QQ.COM";
	char arr2[30] = { 0 };
	strcpy(arr2, arr1);
 
	const char* arr3 = "@.";
 
	char* str = NULL;
	for (str = strtok(arr2, arr3); str != NULL; str = strtok(NULL, arr3))
	{
		printf("%s\n", str);
	}
 
	return 0;
}

六.错误信息报告

strerror

  strerror函数(string error)的作用为返回错误码对应的信息。即根据接收到的错误码(错误码 errno 为全局变量),返回错误原因的详细信息。

#include<stdio.h>
#include<string.h>
 
int main()
{
	int i = 0;
	for (i = 0; i <= 4; i++)
	{
		printf("错误原因为:%s\n", strerror(i));
	}
 
	return 0;
}

当库函数使用的时候,发生错误会把errno这个全局的错误变量设置为本次执行库函数产生的错误码,errno是C语言提供的一个全局变量,可以直接使用,放在errno.h文件中的

int main()
{
	//打开文件 ——fopen提供文件没和打开方式,打开成功返回非空指针,打开失败就是NULL
	//我们用strerror,看错误信息
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		perror("fopen");
		return 1;
	}
	//读文件
	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

 还有个函数是perror,是打印错误信息,相当于printf+strerror

七.字符操作

1.字符分类函数

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

例如:isdigit

	char ch = '0';
	if (ch >= '0' && ch <= '9')
	{
        //复杂
	}
	if (isdigit(ch))
	{
        //方便快捷
	}

2.字符转换

int tolower ( int c ); //把大写转为小写
int toupper ( int c ); //把小写转为大写

#include<stdio.h>
#include <ctype.h>
int main()
{
	char ch = 0;
	while (ch = getchar())
	{
		if (islower(ch))
		{
			ch = toupper(ch);
		}
		else
		{
			ch = tolower(ch);
		}
		printf("%c", ch);
	}
	return 0;
}

 大写改为小写,小写改为大写

八.内存操作函数

1.memcpy

    memcpy函数(memory copy)的作用为从源内存空间向目的内存空间拷贝限制数量(单位是字节)的数据。它与 strcpy 函数类似,作用均为拷贝数据,不同的是 strcpy 仅仅只操作字符串故会在结束标识符 ' \0 ' 处停止,而 memcpy 函数操作的是内存,内存中的数据是相邻的,故不会在结束标识符处停止。

 注意

  1. 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
  2. 这个函数在遇到 '\0' 的时候并不会停下来。
  3. 如果source和destination有任何的重叠,复制的结果都是未定义的。
#include<stdio.h>
#include<string.h>
 
int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9 };
	int arr2[5] = { 0 };
 
	printf("Before copy , the char inside arr2 are :>");
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf(" %d", arr2[i]);
	}
	printf("\n");
 
	memcpy(arr2, arr1, 20);
 
	printf("After copy , the char inside arr2 are :>");
	for (i = 0; i < 5; i++)
	{
		printf(" %d", arr2[i]);
	}
	printf("\n");
 
	return 0;
}

 我们首先创建并初始化了两个整形数组,接着打印出数组 arr2 内数据的存放情况。紧接着我们通过内存拷贝函数,将数组 arr1 内的前20个字节的数据拷贝给了 arr2数组。整型数组内每个数据元素所占的内存空间为4个字节,20个字节即将数组 arr1 中的前五个数据元素拷贝给了数组 arr2。最后再次打印数组 arr2 中的数据,验证拷贝结果。

如果源内存空间和目标内存空间有任何的重叠,复制的结果都是未定义的

模拟实现

//小细节——这里事void指针,我们拷贝的时候转化为什么,要转化为char类型,如果int的话,我们拷贝17个字节,int一次四个,所以用char,一个一个
void* my_memcpy(void* dest, const void* src, size_t num)
{
	void* ret = dest;//返回目标的起始地址,所以保持一下
	assert(dest && src);
	while (num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;//强制类型转换,不能用++,不然类型不符合
		src = (char*)src + 1;
	}
	return ret;
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	int ret = my_memcmp(arr2, arr1+2, 20);
	printf("%d\n", ret);

	return 0;
}

 但是当我们出现内存重叠的时候怎么拷贝,就是下面test2函数

void* my_memcpy(void* dest, const void* src, size_t num)
{
	void* ret = dest;//返回目标的起始地址,所以保持一下
	assert(dest && src);
	while (num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;//强制类型转换,不能用++,不然类型不符合
		src = (char*)src + 1;
	}
	return ret;
}
void test1()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	my_memcpy(arr2, arr1 + 2, 17);

}
void test2()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memcpy(arr1 + 2, arr1, 20);//12345拷贝的34567,自己给自己拷贝
}

 上面自己给自己,我们拷贝到3的时候,1已经把3给覆盖了,内存重叠的时候,出现问题,加一个判断,重叠的时候从后往前,正常的时候就从前往后,当dest在src前面的时候从前向后——当dest在src后面的时候从后向前

但是我们库函数memmove就是提供给我们拷贝重复内存的,我们来看下面memmove的模拟实现,就是对我们模拟实现memcpy改造 

2.memmove

当我们拷贝时候出现内存空间重复的时候,就用到我们的memmove,就是拷贝重复空间的函数

 memmove函数(memory move)的作用为弥补 memcpy 函数的不足,主要用于处理内存的重叠部分。即如果源空间和目标空间出现重叠,就得使用 memmove 函数来进行处理。

注意:

  1. 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
  2. 如果源空间和目标空间出现重叠,就得使用memmove函数处理。
#include<stdio.h>
#include<string.h>
 
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9 };
	
	printf("Before copy , the char inside arr are :>");
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf(" %d", arr[i]);
	}
	printf("\n");
	
	memmove(arr+2, arr, 20);
	
	printf("After copy , the char inside arr are :>");
	for (i = 0; i < 5; i++)
	{
		printf(" %d", arr[i]);
	}
	printf("\n");
 
	return 0;
}

这个函数与 memcpy 函数除处理对象不同外,语法结构和使用场景等都极其类似,这里也就不再做过多赘述。

模拟实现

void* my_memmove(void* dest, const void* src, size_t num)
{
	void* ret = dest;
	assert(dest && src);
	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;
}
void test3()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1+2, arr1, 20);
}

void test4()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	memcpy(arr1, arr1+2, 20);//也是可以搞定的,但是谁的工作谁做,又不是没员工
}

int main()
{
	test1();
	test2();
	test3();
	test4();
	return 0;
}

我们看test3和test4,所以库函数memcpy也可以,但是重叠的拷贝不是他干的活,还是交给memmove 

3.memsrt

 

把一块内存空间设置成你想要的值,以字节为单位来修改

#include<stdio.h>
#include<string.h>
int main()
{
	//char arr[20] = { 0 };
	//memset(arr, 'x', 10);
	//printf("%s\n", arr);  //xxxxxxxxxx
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	memset(arr, '\0', 10);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);  // 0 0 0 4 5 6 7 8 9 10
	}
	//01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 ...将前10个字节改为0
	//00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00
	return 0;
}

4.memcmp

 比较从ptr1和ptr2指针开始的num个字节,不在乎有无 '\0' ,你让它比较几个字节就比较几个字节。

#include<stdio.h>
#include<string.h>
int main()
{
	int arr1[] = { 1,2,7,4,5 };
	int arr2[] = { 1,2,3,4,5 };
	printf("%d\n", memcmp(arr1, arr2, 9)); //1  // 9表示比较前9个字节
	return 0;
}

结束语

这节课讲解很多重要的字符串函数和内存函数,有些模拟实现需要掌握,还有基本用法,参数等 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/161568.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手把手教你快速搞定4个职场写作场景

“ 【写作能力提升】系列文章&#xff1a; 为什么建议你一定要学会写作? 手把手教你快速搞定 4 个职场写作场景 5 种搭建⽂章架构的⽅法”免费赠送! ”一、前言 Hello&#xff0c;我是小木箱&#xff0c;今天主要分享的内容是: 写作小白需要避免的五个写作误区和灵魂五问。 二…

好家伙谷歌翻译又不能用了(有效解决方法)

今天打开idea想翻译单词发现谷歌翻译又又又挂了。为什么挂掉&#xff0c;可能是那个ip节点太多人用了&#xff0c;我也不懂我就是一个小白。不bb了说一下解决方法。一、手动Ping可以连接的ip这里我使用的是&#xff1a;https://ping.chinaz.com然后我们输入&#xff1a; transl…

YoloV8简单使用

我们坐在阳光下&#xff0c;我们转眼间长大&#xff0c;Yolo系列都到V8了&#xff0c;来看看怎么个事。目标检测不能没有Yolo&#xff0c;就像西方不能没有耶路撒冷。这个万能的目标检测框架圈粉无数&#xff0c;经典的三段式改进也是改造出很多论文&#xff0c;可惜我念书时的…

论文投稿指南——中文核心期刊推荐(植物学)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

English Learning - L1-12 情态动词 + 倒装 2023.1.12 周四

这里写目录标题9 情态动词9.2 may - mightmay(1) 表许可(2) 推测 --- 可能&#xff0c;或许&#xff08;50% 的概率&#xff09;might9.3 can - couldcan核心思维: 潜在的可能性&#xff0c;有某种知识和技能而能办到&#xff08;1&#xff09;能力&#xff08;有做 。。。的能…

java 计算机概述看这一篇文章就够了

第一章 计算机概述 第1节 计算机介绍 1广义上: 凡是可以帮助我们完成计算的工具统称为计算机(比如 算盘、计算器等...) 狭义上: 当前说计算机一般情况特指电子计算机(电脑)第2节 计算机历史 算盘(机械/手动) 源于中国具体作者不详,发明时间不详.我国第一颗原子弹的很多数据早期…

【论文速递】ACM2022 - 基于嵌入自适应更新和超类表示的增量小样本语义分割

【论文速递】ACM2022 - 基于嵌入自适应更新和超类表示的增量小样本语义分割 【论文原文】&#xff1a;Incremental Few-Shot Semantic Segmentation via Embedding Adaptive-Update and Hyper-class Representation 获取地址&#xff1a;https://arxiv.org/pdf/2207.12964.pd…

靶机测试djinn笔记

靶机地址https://www.vulnhub.com/entry/djinn-1,397/靶机测试信息收集nmap扫描nmap -p- -A 192.168.1.106 -oA dj 通过 nmap 扫描得到21 端口 可以匿名访问22 端口 ssh 但是被过滤了 1337 是一个游戏端口7331 是 python web测试 1337 端口访问端口nc -vv 192.168.0.177 1337这…

智慧工地安全帽智能识别检测 yolov5

智慧工地安全帽智能识别检测通过yolov5opencv深度学习技术&#xff0c;可自动对现场画面检测识别人员有没有戴安全帽。OpenCV基于C实现&#xff0c;同时提供python, Ruby, Matlab等语言的接口。OpenCV-Python是OpenCV的Python API&#xff0c;结合了OpenCV CAPI和Python语言的最…

【Spring(六)】彻底搞懂Spring的依赖注入

文章目录前言依赖注入setter注入构造器注入自动装配集合注入总结前言 在核心容器这一部分bean相关的操作&#xff0c;我们已经学完了&#xff0c;接下来我们就要进入到第二个大的模块&#xff0c;与我们的DI,也就是依赖注入相关知识的学习了&#xff0c;那我们先来学习第一个内…

ChatGPT!我是你的破壁人;比尔·盖茨不看好Web3与元宇宙;FIFA押中4届世界杯冠军;GitHub今日热榜 | ShowMeAI资讯日报

&#x1f440;日报合辑 | &#x1f3a1;AI应用与工具大全 | &#x1f514;公众号资料下载 | &#x1f369;韩信子 &#x1f3a1; 『GPTZero』用 ChatGPT 写论文糊弄老师&#xff1f;已经不灵了~ 语言生成模型的诞生与优化&#xff0c;给教育和学术界带来了不少困扰。继纽约教育…

前端工程化解决方案-Webpack编程

文章目录1. 前端工程化目前主流的前端工程化解决方案2.webpack2.1 主要供能2.2 webpack与webpack-cli的使用2.2.1 初始化项目2.2.2 安装2.2.3 配置2.2.3.1 webpack.config.js2.2.3.2 package.json2.2.3.3 打包构建2.2.3.4 项目中引入 dist/bundle.js2.3 动态部署2.3.1 webpack-…

微服务架构概述

微服务架构概述一、架构演变1.1 单体架构1.2 分布式架构1.3 微服务二、SpringCloud2.1 简介3.2 痛点三、SpringCloud Alibaba3.1 简介3.2 优点3.3 主要组件3.4 版本对应一、架构演变 1.1 单体架构 讲业务的所有功能集中在一个项目中开发&#xff0c;打成一个包部署。 优点&am…

SaaS架构实现理论(四)可伸缩多租户

目录1.伸缩性&#xff08;Scalable&#xff09;的概念2.应用服务器层的水平扩展2.1基于Session复制的水平扩展方式2.2基于Session Sticky的水平扩展方式2.3基于Cache的集中式Session实现水平扩展2.4三种水平扩展方式的比较3.数据库的水平扩展3.1数据库的垂直切分3.2数据库的读写…

ArcGIS基础实验操作100例--实验93插值模型的精度分析

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 空间分析篇--实验93 插值模型的精度分析 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;…

ESP-IDF:使用基本类型,指针,引用,指针的指针,指针的引用作为函数参数的几个例程

1.例程&#xff1a; /下面是使用基本类型&#xff0c;指针&#xff0c;引用&#xff0c;指针的指针&#xff0c;指针的引用作为函数参数的几个例程/ // 值拷贝 int add10(int a) { a 10; return a; } // 指针传参&#xff0c;是一种地址拷贝 void add101(int *a) { // int *…

(11)QWidget的使用(two)

目录 设置窗口图标和标题 设置窗口图标以及获取图标 设置窗口标题以及获取标题 窗口标题的特殊操作 设置窗口的状态 设置窗口的不透明度 设置窗口的状态 使用封装好的函数显示窗口 设置窗口标志 窗口标志简介 设置窗口图标和标题 设置窗口图标以及获取图标 void se…

Java在远程方法调用中运用反射机制

本案例将介绍反射机制在网络编程中的应用&#xff0c;实现如何在客户端通过远程方法调用服务器端的方法。 假定在服务器端有一个 HelloService 接口&#xff0c;该接口具有 getTime() 和 echo() 方法&#xff0c;具体代码如下&#xff1a; import java.util.Date; public int…

【C语言航路】第十一站:字符串、字符和内存函数

目录 一、字符串函数 1.strlen &#xff08;1&#xff09;strlen的库函数文档 &#xff08;2&#xff09;strlen的模拟实现 &#xff08;3&#xff09;strlen的注意事项 2.strcpy &#xff08;1&#xff09;strcpy的库函数文档 &#xff08;2&#xff09;strcpy的使用以…

如何使用机器学习进行图像识别|数据标注

什么是图像识别&#xff1f;图像识别是一种用于识别图像中的对象并将其分类为特定类别的机制&#xff0c;基于人类识别不同图像集中对象的方式。图像识别如何为人类工作&#xff1f;当我们看到一个物体或图像时&#xff0c;作为人类&#xff0c;我们能够立即准确地知道它是什么…