【ONE·C || 字符串和内存函数】

news2024/9/22 17:34:21

总言

  C语言:字符串和内存函数使用介绍。

文章目录

  • 总言
  • 1、求字符串长度:strlen
    • 1.1、基本介绍
    • 1.2、演示说明
      • 1.2.1、strlen输出
      • 1.2.2、strlen返回值
    • 1.3、模拟实现strlen
      • 1.3.1、计数器写法
      • 1.3.2、递归写法
      • 1.3.3、指针-指针写法
  • 2、长度不受限制的字符串函数
    • 2.1、字符串拷贝:strcpy
      • 2.1.1、基本介绍
      • 2.1.2、演示说明
      • 2.1.3、模拟实现
        • 2.1.3.1、写法1.0
        • 2.1.3.2、简化2.0
    • 2.2、字符串追加:strcat
      • 2.2.1、基本介绍
      • 2.2.2、演示说明
      • 2.2.3、模拟实现
        • 2.2.3.1、写法1.0
        • 2.2.3.2、简化2.0
    • 2.3、字符串比较:strcmp
      • 2.3.1、基本介绍
      • 2.3.2、演示说明
      • 2.3.3、模拟实现
  • 3、长度受到限制的字符串函数
    • 3.1、字符串拷贝:strncpy
      • 3.1.1、基本介绍
      • 3.1.2、使用演示
    • 3.2、字符串追加:strncat
      • 3.2.1、基本介绍
      • 3.2.2、使用演示
    • 3.3、字符串比较:strncmp
      • 3.3.1、基本介绍
      • 3.3.2、使用演示
      • 3.2.3、关于字符串比较的一些细节说明
  • 4、字符串查找
    • 4.1、字符串中找子串:strstr
      • 4.1.1、基本介绍
      • 4.1.2、使用演示
      • 4.1.3、模拟实现
    • 4.2、字符串拆分:strtok
      • 4.2.1、基本介绍
      • 4.2.2、使用演示
  • 5、错误信息报告
    • 5.1、strerror
      • 5.1.1、基本介绍
      • 5.1.2、使用演示(errno、perror)
  • 6、字串操作函数
    • 6.1、总览
    • 6.2、使用演示
  • 7、内存操作函数
    • 7.1、内存块拷贝:memcpy
      • 7.1.1、基本介绍
      • 7.1.2、使用演示
      • 7.1.3、模拟实现
    • 7.2、内存块挪动:memmove
      • 7.2.1、基本介绍
      • 7.2.2、使用演示
      • 7.2.3、模拟实现
    • 7.3、内存块比较:memcmp
      • 7.3.1、基本介绍
      • 7.3.2、使用演示
    • 7.4、内存块设置:memset
      • 7.4.1、基本介绍
      • 7.4.2、使用演示

  
  

1、求字符串长度:strlen

1.1、基本介绍

  1)、使用说明
  函数链接
在这里插入图片描述

  1、字符串以'\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )。
  2、参数指向的字符串必须要以 '\0' 结束,否则输出结果不一定正确。
  3、注意函数的返回值为size_t,是无符号整型。
  
  

1.2、演示说明

1.2.1、strlen输出

  2)、strlen正常输出举例(1)

  以常量字符串"abcdefg"初始化数组,数组中实际存储的是a、b、c、d、e、f、g、\0

#include<string.h>
int main()
{
	char arr[] = "abcdefg";
	printf("%d\n", strlen(arr));
	return 0;
}

在这里插入图片描述

  
  
  
  3)、strlen错误输出举例
  arr[] = {'a','b','c','d','e','f','g'}

#include<string.h>
int main()
{
	char arr[] = {'a','b','c','d','e','f','g'};
	printf("%d\n", strlen(arr));
	return 0;
}

在这里插入图片描述
  
  关于上述的修改方法:在数组中主动添加’\0’,arr[] = {'a','b','c','d','e','f','g','\0'}

#include<string.h>
int main()
{
	char arr[] = {'a','b','c','d','e','f','g','\0'};
	printf("%d\n", strlen(arr));
	return 0;
}

  
  
  
  4)、strlen正常输出举例(2)
  问:假如指定数组长度大于实际字符长度,这种情况下strlen的输出结果如何?为什么?

#include<string.h>
int main()
{
	char arr[10] = {'a','b','c','d','e','f','g'};
	printf("%d\n", strlen(arr));
	return 0;
}

在这里插入图片描述
  原因说明:此处字符数组规定其大小,对于未初始化的元素,默认初始化为0,ASCII为‘\0’,故strlen求得准确值。
  
  
  

1.2.2、strlen返回值

  1)、问题分析
  要注意strlen的返回值是无符号整型(unsigned int),如果不注意此细节会引起意想不到的错误:
  如下述这段代码,从数值角度,strlen("abc")=3strlen("abcdef")=6,两者相减后结果为负数。

#include<string.h>
int main()
{
	if (strlen("abc") - strlen("abcdef") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}
	return 0;
}

  我们来看实际输出结果:
在这里插入图片描述
  由于strlen返回无符号整型,在有符号类型中,3-6=-3-3对应一个很大的无符号整型,是正数。(无符号相减还是无符号数。)
  
  
  
  2)、解决方案
  其一:直接采用大于小于号判断

#include<string.h>
int main()
{
	if (strlen("abc") > strlen("abcdef") )
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}
	return 0;
}

在这里插入图片描述
  
  其二:强制类型转换为有符号的int

#include<string.h>
int main()
{
	if ((int)strlen("abc") - (int)strlen("abcdef") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}
	return 0;
}

在这里插入图片描述
  
  
  
  

1.3、模拟实现strlen

1.3.1、计数器写法

  1)、写法1.0

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

  
  
  2)、写法2.0
  1、对于空字符\0而言,其数值为0,故循环判断中, *str != '\0' , 或者 *str!=0 都可简写为 *str
  2、*str++,当前循环过程中不涉及该str的使用变量++是在条件判断后进行。

#include<assert.h>
size_t my_strlen(const char* str)
{
	assert(str != NULL);
	int count = 0;
	while (*str++)
	{
		count++;
	}
	return count;
}

  
  

1.3.2、递归写法

  使用递归写法时,一定要注意递归的两个条件。

#include<assert.h>
size_t my_strlen(const char* str)
{
	assert(str != NULL);
	if (*str == '\0')
	{
		return 0;
	}
	return 1 + my_strlen(++str);
}

  
  

1.3.3、指针-指针写法

  使用指针-指针的方法时,需要注意最后的返回值。

#include<assert.h>
size_t my_strlen(const char* str)
{
	assert(str != NULL);
	char* end = str;
	while (*end != '\0')
	{
		end++;
	}
	return end - str;
}

  对while循环的进一步简化。

#include<assert.h>
size_t my_strlen(const char* str)
{
	assert(str != NULL);
	char* end = str;
	while (*end++);
	return end - str-1;//此处-1是因为跳出while循环时s又自增了一次
}

  这里就能体现出前置自增和后置自增的区别。

#include<assert.h>
size_t my_strlen(const char* str)
{
	assert(str != NULL);
	char* end = str;
	while (*++end);
	return end - str;
}

  
  
  

2、长度不受限制的字符串函数

2.1、字符串拷贝:strcpy

2.1.1、基本介绍

  函数介绍
在这里插入图片描述

  1、strcpy拷贝时,源字符串source必须以 '\0' 结束。
  2、strcpy拷贝时会将源字符串source中的 ‘\0’ 拷贝到目标空间destination中。
  3、因此目标空间必须足够大,以确保能存放源字符串。
  4、char * destination, const char * source表明,目标空间必须可以修改。
  5、需要注意其返回值char *,返回的是目标空间的起始地址,这样做的目的是达到链式访问。比如:

	printf("%s\n", strcpy(destination, source));

  
  
  

2.1.2、演示说明

  1)、strcpy拷贝时,源字符串必须以 ‘\0’ 结束

  演示如下,假如我们使用char arr4[] = { 'a','b','c','d','e'};这类不含有’\0’的源字符串,结果出错。

#include<string.h>
int main()
{
	char arr1[] = "hello";
	char arr2[20];
	char arr3[10] = { 'f','r','e','e','d','o','m','\0'};
	char arr4[] = { 'a','b','c','d','e'};//错误演示一

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


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

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

	return 0;
}

在这里插入图片描述
  
  
  2)、strcpy拷贝时会将源字符串中的 ‘\0’ 拷贝到目标空间中

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "abcd";
	char arr2[20]="XXXXXXXXXXXXXXXX";

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

	return 0;
}

在这里插入图片描述
  
  
  
  3)、常量字符串不能使用strcpy等拷贝:目标空间必须可以修改

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "abcd";
	char* arr2 = "XXXXXXXXXXXXXXXX";

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

	return 0;
}

  arr2指向的空间是常量字符串,常量字符串是不能被修改的。
在这里插入图片描述

  
  
  
  
  

2.1.3、模拟实现

2.1.3.1、写法1.0

#include<assert.h>
char* my_strcpy(char* des, const char* sou)
{
	assert(des && sou);
	char* point = des;
	while (*sou != '\0')
	{
		*des = *sou;
		des++;
		sou++;
	}
	*des = '\0';//解决末尾的'\0'

	return point;
}

  1、assert(des && sou);assert(dest != NULL);assert(src != NULL);的简化写法。注意中间用的是逻辑与。
  2、*des = '\0';,也可以写成*des = *sou;,由于循环以判断’\0’结束,故需要再末尾将source中的'\0'拷贝到destination中。
  
  

2.1.3.2、简化2.0

#include<assert.h>
char* my_strcpy(char* des, const char* sou)
{
	assert(des && sou);
	char* point = des;
	while (*des++ = *sou++);

	return point;
}

  while (*des++ = *sou++);这串代码的执行逻辑为:
  1、解引用des、sou
  2、将其赋值*des = *sou
  3、得到结果非’\0’ 时为真,循环继续。
  4、des、sou分别自增。
  
  故当sou=='\0'时,它会先将其赋值给des,然后再判断是否进入while循环,最后都会自增。但因并没有对自增后的des、sou做修改,不会越界报错。
  
  
  

2.2、字符串追加:strcat

2.2.1、基本介绍

  相关函数描述
在这里插入图片描述

  1、源字符source串必须以 ‘\0’ 结束,且从目标空间destination中‘\0’开始追加字符。
  2、目标空间必须有足够的大,能容纳下源字符串的内容。
  3、目标空间必须可修改。
  
  
  

2.2.2、演示说明

  1)、strcat字符串追加演示

#include<string.h>
int main()
{
	char arr1[20] = "call me";
	char arr2[] = " up!";

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

	return 0;
}

在这里插入图片描述

  
  
  2)、目标空间中没有’\0’的一种错误追加举例:

#include<string.h>
int main()
{
	char arr1[] = { 'E','n','j','o','y',' '};
	char arr2[] = "yourself.";

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

	return 0;
}

在这里插入图片描述

  分析: 此代码存在两个问题,其一为数组未初始化长度时,会以实际初始化的元素个数来判定空间大小,故使用strcat时空间不够大。其二,这种初始化方式导致目标空间结尾没有'\0',会发生错误拼接。
  
  
  
  3)、验证strcat字符串追加是从'\0'位置处开始

#include<string.h>
int main()
{
	char arr1[20] = "Enjoy \0XXXXXXXXXX";
	char arr2[] = "yourself.";

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

	return 0;
}

在这里插入图片描述
  
  
  
  

2.2.3、模拟实现

  整体思路:
  1、找到destination中的'\0'
  2、将source源字符串拷贝过来,注意包含'\0'
  总体来说就是,strcpy的基础上多增加了一个找尾工作。

2.2.3.1、写法1.0

#include<assert.h>
char* my_strcat(char* des, char* sou)
{
	assert(des && sou);
	char* point = des;//解决返回值问题
	while (*des != '\0')//循环至目标空间末尾,找到目标空间处的'\0'
		++des;
	while (*sou != '\0')//拼接工作,将源字符数组拼接到目标字符数组中
	{
		*des = *sou;
		des++;
		sou++;
	}
	*des = *sou;//上述循环后,目标空间缺少末尾\0,故在此处补上

	return point;
}

  
  

2.2.3.2、简化2.0

#include<assert.h>
char* my_strcat(char* des, char* sou)
{
	assert(des && sou);
	char* point = des;
	while (*des)
		des++;
	while (*des++ = *sou++);

	return point;
}
#include<assert.h>
char* my_strcat(char* des, char* sou)
{
	assert(des && sou);
	char* point = des;
	while (*++des);//此处也可以用前置自增解决
	while (*des++ = *sou++);

	return point;
}

  
  上述代码存在问题:
  这里关键在于编译器如何实现该函数,使用我们模拟实现的my_strcat自己给自己追加时,由于'\0'被覆盖,追加不会停止,结果就是越界。
  
  
  
  

2.3、字符串比较:strcmp

2.3.1、基本介绍

  相关函数介绍
在这里插入图片描述

  1、strcmp比较的不是字符串长度,而是比较字符串对应位置的字符ASCII码大小,若相同,则比较下一对,直到不同或遇到\0
  2、对返回值:①第一个字符串大于第二个字符串,则返回大于0的数字;②第一个字符串等于第二个字符串,则返回0;③第一个字符串小于第二个字符串,则返回小于0的数字。
  3、C标准里只规定了>0、<0、==0,没有规定具体数值,例如vs下是1 0 -1,但不能以此惯性思维用于全部编译器,这样在使用判断语句时可能出错。
  
  
  

2.3.2、演示说明

  1)、strcmp使用演示和返回值说明

int main()
{
	char arr1[] = "freedom";
	char arr2[] = "free";
	printf("%d\n", strcmp(arr1, arr2));

	char arr3[] = "abcde";
	char arr4[] = "abcdef";
	printf("%d\n", strcmp(arr3, arr4));

	char arr5[] = "free";
	char arr6[] = "free";
	printf("%d\n", strcmp(arr5, arr6));

	return 0;
}

在这里插入图片描述
  此处需要注意其返回值:
  以下面这种写法来判断输出结果是错误的。

int main()
{
	char arr1[] = "freedom";
	char arr2[] = "free";
	int ret=strcmp(arr1, arr2);
	if (ret == 1)
	{
		printf(">\n");
	}
	else if (ret == -1)
	{
		printf("<n");
	}
	else if (ret == 0)
	{
		printf("==\n");
	}

	return 0;
}

  正确写法如下:

int main()
{
	char arr1[] = "freedom";
	char arr2[] = "free";
	int ret=strcmp(arr1, arr2);
	if (ret > 0)
	{
		printf(">\n");
	}
	else if (ret < 0)
	{
		printf("<n");
	}
	else//==0
	{
		printf("==\n");
	}

	return 0;
}

  
  
  

2.3.3、模拟实现

  一种是直接在外部判断结果:

int my_strcmp(char* s1, char* s2)
{
	assert(s1 && s2);
	while (*s1 && *s2 && *s1 == *s2)
	{
		s1++;
		s2++;
	}
	return *s1 - *s2;
}

  一种是将相等的情况放在while循环内判断。

int my_strcmp(char* s1, char* s2)
{
	assert(s1 && s2);
	while (*s1 == *s2)
	{
		if (*s1 == '\0')
			return 0;
		s1++;
		s2++;
	}
	return *s1 - *s2;
}

  总而言之写法不唯一。
  
  
  
  

3、长度受到限制的字符串函数

3.1、字符串拷贝:strncpy

3.1.1、基本介绍

  相关函数介绍
在这里插入图片描述

  1、strncpy从源字符串中拷贝num个字符到目标空间。
  2、如果源字符串的长度小于num,则拷贝完源字符串之后,在目标空间后边追加0,直到满足num
  3、如果源长度超过 num,则不会在目标末尾隐式附加空字符。
  
  

3.1.2、使用演示

  1)、源字符串的长度超过num

int main()
{
	char arr1[] = "hello";
	char arr2[] = "beautiful!";
	printf("%s\n", strncpy(arr1, arr2, 4));

	return 0;
}

  当源source长于num时,是不把源中的’\0’一并拷贝过来的。
在这里插入图片描述

  
  
  
  2)、源字符串的长度小于num

int main()
{
	char arr1[] = "abcdefghijk";
	char arr2[] = "free";
	printf("%s\n", strncpy(arr1, arr2, 8));

	return 0;
}

在这里插入图片描述

  
  
  
  
  
  
  

3.2、字符串追加:strncat

3.2.1、基本介绍

在这里插入图片描述
  1、strncat字符串追加,追加后会放置一个’\0’。
  
  

3.2.2、使用演示

  1)、当num值小于source时

int main()
{
	char arr1[] = "abcd\0XXXXXXXXX";
	char arr2[] = "freedom";
	printf("%s\n", strncat(arr1, arr2, 4));

	return 0;
}

在这里插入图片描述

  
  
  
  
  2)、当num值大于source时

int main()
{
	char arr1[] = "abcd\0XXXXXXXXX";
	char arr2[] = "freedom";
	printf("%s\n", strncat(arr1, arr2, 9));

	return 0;
}

在这里插入图片描述

  
  
  
  

3.3、字符串比较:strncmp

3.3.1、基本介绍

在这里插入图片描述

  
  
  

3.3.2、使用演示

int main()
{
	char arr1[] = "abcde";
	char arr2[] = "abcdeg";
	printf("%d\n", strncmp(arr1, arr2, 4));

	char arr3[] = "abcde";
	char arr4[] = "abcdeg";
	printf("%d\n", strncmp(arr3, arr4, 6));

	char arr5[] = "abcde";
	char arr6[] = "abcdeg";
	printf("%d\n", strncmp(arr5, arr6, 8));

	return 0;
}

在这里插入图片描述

  
  
  

3.2.3、关于字符串比较的一些细节说明

  代码如下:

int main()
{
	char arr1[] = "abcd";
	char arr2[] = "abcd";
	if (arr1 == arr2)
	{
		printf("==\n");
	}
	else
	{
		printf("!=\n");
	}

	printf(" %d\n", "abcd" < "abcdef");

	return 0;
}

  如下图,事实上,在不适用相应字符串比较函数时,如果直接拿字符串进行比较,这样得出的结果不具有正确性。
  1、比如,这里的arr1arr2,实际比较的是数组地址空间。
  2、再比如,"abcd" < "abcdef",这里实际上比较的仍旧是常量字符串首字符的地址。
在这里插入图片描述
  
  
  
  
  
  
  
  
  
  
  
  
  

4、字符串查找

4.1、字符串中找子串:strstr

4.1.1、基本介绍

  相关函数介绍
在这里插入图片描述
  1、strstr寻找的是首次出现的位置,若找不到,则返回NULL。
  
  

4.1.2、使用演示

int main()
{
	char arr1[] = "abcdefabcdef";
	char arr2[] = "def";
	char* ret1 = strstr(arr1, arr2);
	if (ret1 != NULL)
	{
		printf("%s\n", ret1);
	}

	char arr3[] = "abcdefabcdef";
	char arr4[] = "aaa";
	char* ret2 = strstr(arr3, arr4);
	if (ret2 == NULL)
	{
		printf("%s\n", ret2);
	}

	return 0;
}

在这里插入图片描述
  
  
  

4.1.3、模拟实现

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

	const char* start1 = str1, * start2 = str2;
	const char*cur = str1;
	while (*cur)//向str1中找str2,若st1遍历完后仍旧没找到,则返回NULL 
	{
		start1 = cur;
		start2 = str2;//匹配失败后,要将start2指向还原

		while (*start1 && *start2 && (*start1 == *start2))//若start1指向与start2指向内容相同,标记,并往后匹配
		{//需要注意长度不等时,其中字符串先后结束的情况
			start1++;
			start2++;
		}
		//跳出上述while循环的情况只有两种,一则是匹配失败 *start1 != *start2,一则是其中字符串遇到'\0'
		if (*start2 == '\0')
		{//若结束时start2为NULL,说明成功在start1中找到子串
			return (char*)cur;
		}
		cur++;//如果匹配失败,则将start1中标记指针向后移一位,重新新一轮匹配
	}
	return NULL;
}

  PS:查找子串可使用KMP算法。
  
  
  

4.2、字符串拆分:strtok

4.2.1、基本介绍

在这里插入图片描述
  相关函数介绍

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

  1、第二参数sep是字符串,其定义了用来分隔str的字符集合。

	const char* sep = " #-";

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

	char arr[] = "Life is#too-short for#long-term grudges.";

  3、strtok函数根据给定的sep,在str中找到匹配的标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)

char arr[] = "Life is#too-short for#long-term grudges.";
char arr[] = "Life\0is\0too\0short\0for\0long\0term\0grudges.";

  4、函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
  5、函数的第一个参数为 NULL ,函数将从同一个字符串中被保存的位置开始,查找下一个标记。
  6、如果字符串中不存在更多的标记,则返回 NULL 指针。
  
  

4.2.2、使用演示

int main()
{
	char arr[] = "Life is#too-short for#long-term grudges.";
	char tmp[100] = { 0 };
	strcpy(tmp, arr);

	const char* sep = " #-";
	char* str = NULL;
	for (str = strtok(tmp, sep); str != NULL; str = strtok(NULL, sep))
	{
		printf("%s\n", str);
	}
	printf("\n");
	return 0;
}

在这里插入图片描述
  
  以下为strtok会改变字符串的调试演示:因此在使用该函数时,若不想改变源字符串,则可临时拷贝一份数据。
在这里插入图片描述
  
  
  
  

5、错误信息报告

5.1、strerror

5.1.1、基本介绍

  相关函数介绍
在这里插入图片描述
  1、返回错误码所对应的错误信息。
  
  
  

5.1.2、使用演示(errno、perror)

  1)、VS2019下strerror错误信息举例演示
  strerror生成的错误字符串,取决于系统和库实现。此处只是简单演示一下:

int main()
{
	for (int i = 0; i < 10; ++i)
	{
		printf(" %d: %s\n", i, strerror(i));
	}
	return 0;
}

在这里插入图片描述

  
  
  
  2)、使用演示(二)
  比如下述这段代码,我们使用malloc动态开辟空间,如果其返回值为NULL,说明开辟失败,这时候错误信息会被保存在errno中,我们就可以调用strerror来查看。

#include<limits.h>
#include<errno.h>
int main()
{
	int* p = (int*)malloc(INT_MAX);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	return 0;
}

在这里插入图片描述

  
  
  3)、上述报错,使用perror函数演示
  perror是将错误信息打印出来,而strerror本身只是拿到错误信息(若要打印则需要相对的打印函数 )。

int main()
{
	int* p = (int*)malloc(INT_MAX);
	if (p == NULL)
	{
		//printf("%s\n", strerror(errno));
		perror("malloc");//此处传入的字符串是自定义的
		return 1;
	}
	return 0;
}

在这里插入图片描述
  
  
  
  
  

6、字串操作函数

6.1、总览

  相关函数介绍

在这里插入图片描述

  

6.2、使用演示

  1)、判断是否为数字
在这里插入图片描述

#include<ctype.h>
int main()
{
	printf("%d\n", isdigit('6'));
	printf("%d\n", isdigit('2'));
	printf("%d\n", isdigit('a'));

	return 0;
}

在这里插入图片描述

  
  
  2)、判断是否为字母
在这里插入图片描述

#include<ctype.h>
int main()
{
	printf("%d\n", isalpha ('6'));
	printf("%d\n", isalpha('E'));
	printf("%d\n", isalpha('a'));

	return 0;
}

  使用上述库函数就不用我们自己写函数来判断字符是否为字母了:

	if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z')
	{
		
	}

在这里插入图片描述

  
  
  3)、大小写转换
在这里插入图片描述

#include<stdio.h>
#include<ctype.h>
void str_toupper(char arr[])
{
	int i = 0;
	while (arr[i])
	{
		arr[i] = toupper(arr[i]);
		i++;//ps:此处i++不能直接在while判断语句中使用
	}
}

void str_tolower(char arr[])
{
	int i = 0;
	while (arr[i])
	{
		arr[i] = tolower(arr[i]);
		i++;
	}
}

int main(void)
{
	char arr[20]="";
	scanf("%s", arr);

	str_toupper(arr);
	printf("大写:%s\n", arr);

	str_tolower(arr);
	printf("小写:%s\n", arr);

	return 0;
}

//例如:
//abdf446AEFC
//大写:ABDF446AEFC
//小写:abdf446aefc

  
  
  
  

7、内存操作函数

7.1、内存块拷贝:memcpy

7.1.1、基本介绍

  相关函数介绍
在这里插入图片描述

  1、函数memcpysource的位置开始向后复制num个字节的数据到destination的内存位置。
  2、这个函数在遇到 '\0' 的时候并不会停下来。其总是拷贝num字节。
  3、如果sourcedestination有任何的重叠,复制的结果都是未定义的。
  
  
  
  

7.1.2、使用演示

  1)、memcpy的常规使用举例:sourcedestination无重叠

int main()
{
	int arr1[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int arr2[5] = { 0 };
	memcpy(arr2, arr1, 20);
	return 0;
}

在这里插入图片描述

  
  
  2)、使用memcpy时,sourcedestination有任意重叠的情况演示
  通常情况下,memcpy在作用于重叠空间时结果未定义,比如用我们自己实现的memcpy来演示结果如下:
在这里插入图片描述

  因此才有了后续将学习到的函数:memmove
  但是有的编译器会将该memcpy的实现效果完善到与memmove相似的程度,因此不存在重叠空间拷贝错误的问题。以下是VS2019的演示结果:
在这里插入图片描述

  
  
  
  
  

7.1.3、模拟实现

void* my_memcpy(void* des, void* sou, size_t num)
{
	assert(des && sou);
	void* point = des;
	while (num--)
	{
		*(char*)des = *(char*)sou;
		des = (char*)des + 1;
		sou = (char*)sou + 1;
	}
	return point;
}

  *(char*)des = *(char*)sou;:需要注意,由于void*类型的指针不能直接解引用,此处使用了强制类型转换。选择char*类型,可以做到将指针访问权限控制在一字符,由单字符的挪动,从而达到整体数据的挪动。
  以及,需要注意,此处解引用是在强制类型转换之后。

  des = (char*)des + 1;sou = (char*)sou + 1;:用于达到指针遍历自增的目的。同样的,void*的指针无法自增,此处将其强制类型转换为char*指针。

	++(char*)des;
	++(char*)sou;

  上述这种写法在一些编译器下无法成功运行。
  
  
  
  

7.2、内存块挪动:memmove

7.2.1、基本介绍

在这里插入图片描述
  1、区别于memcpymemmove函数处理的源内存块和目标内存块是可以重叠的。
  2、如果源空间和目标空间出现重叠,就得使用memmove函数处理。
  
  
  

7.2.2、使用演示

int main()
{
	int arr1[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int arr2[5] = { 0 };
	memmove(arr1+2, arr1, 20);
	return 0;
}
int main()
{
	int arr1[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int arr2[5] = { 0 };
	memmove(arr2, arr1, 20);
	return 0;
}

在这里插入图片描述

  
  

7.2.3、模拟实现

  分析思路如下:
  选择原地挪动数据,根据memcpy中的模拟实现可知,存在数据覆盖问题。因此我们分情况讨论。
  1、将des<sou时分为一类,此时挪动数据采取先挪动前面的数据,再挪动后面的数据(从前往后挪动)的方式;
  2、将des>sou时分为一类,此时挪动数据采取先挪动后面的数据,再挪动前面的数据(从后往前挪动)的方式
在这里插入图片描述

void* my_memmove(void* des, void* sou, size_t num)
{
	assert(des && sou);
	void* point = des;
	if (des < sou)//先挪动前再挪动后
	{
		while (num--)
		{
			*(char*)des = *(char*)sou;
			des = (char*)des + 1;
			sou = (char*)sou + 1;
		}
	}
	else//先挪动后再挪动前
	{
		while (num--)
		{
			*((char*)des + num) = *((char*)sou + num);
		}
	}
	return point;
}

  
  
  
  

7.3、内存块比较:memcmp

7.3.1、基本介绍

  相关函数链接
在这里插入图片描述

  
  

7.3.2、使用演示

int main()
{
	int arr1[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int arr2[5] = { 0,1,2,3,0X11223305 };
	printf("%d\n",memcmp(arr2, arr1, 20));
	return 0;
}

在这里插入图片描述
  
  
  
  

7.4、内存块设置:memset

7.4.1、基本介绍

  相关函数链接
在这里插入图片描述
  1、memset是以字节为单位,对指定内存初始化值为value 。
  
  

7.4.2、使用演示

int main()
{
	int arr2[5] = { 0X11111111,0X22222222,0X33333333,0X44444444,0X55555555 };
	memset(arr2, 0, 16);
	return 0;
}

在这里插入图片描述
  
  一定要注意这里的以字节为单位设置值value

int main()
{
	int arr2[5] = { 0X11111111,0X22222222,0X33333333,0X44444444,0X55555555 };
	memset(arr2, 6, 16);
	return 0;
}

在这里插入图片描述

  
  
  
  

  
  
  
  
  
  
  

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

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

相关文章

educoder数据结构 图 无错AC代码版

目录 第1关&#xff1a;实现图的宽度优先遍历 任务描述 相关知识 编程要求 测试说明 输入输出格式说明&#xff1a; 样例输出 Ac_Code 第2关&#xff1a;实现图的深度优先遍历 任务描述 相关知识 测试说明 输入输出格式&#xff1a; 样例输出 AC_Code 第1关&am…

2023年flag

开头总是让人那么茫然无措&#xff0c;在这里记录梳理上一年。以期找到前进的方向&#xff0c;迈开新一年的第一步&#xff0c;然后不断前行。 回顾上一年 首先想到的第一件事&#xff0c;11月换了个工作依然是Java开发10月份准备了软件工程中级考试并考过读了几本技术的书籍…

【设计模式】我终于读懂了享元模式。。。

祝大家开工大吉&#x1f9e7;&#x1f9e7;&#x1f9e7;&#xff01;&#xff01;主页有红包哦 点这里 文章目录祝大家开工大吉&#x1f9e7;&#x1f9e7;&#x1f9e7;&#xff01;&#xff01;主页有红包哦 [点这里](https://blink.csdn.net/details/1469531)&#x1f9e7;…

Python基础学习 -- 进程锁

一、join函数的作用1、等子进程执行完&#xff0c;主进程再结束2、将子进程都存储到一个列表&#xff0c;每个子进程都调用一下join方法if __name__"__main__":print("我是主线程")stime.time()a[2,3,4]b[] #存储创建好的进程for i in a:pProcess(targetfu…

入门算法,这篇文章你得看!(java、算法基础、常用算法)

想用Java快速入门算法&#xff1f;这篇文章你得看&#xff01; 提示&#xff1a;本文章适合想要入门算法&#xff0c;并且想 “快速” 达到一定成果的同学们阅读~ 文章非常非常非常长&#xff08;可能是你见过最长的博客&#xff09;&#xff01;&#xff01;&#xff01; 阅读…

Authing 身份云入选《数字身份治理与管理(IGA)应用实践指南》报告

身份是物理实体映射在网络空间的一串数字代码&#xff0c;是数字世界的通行证。掌控了统一的权威数字身份就等同掌控了实体在数字空间的行为。网络业务的快速发展&#xff0c;使业务与安全深度融合到一起&#xff0c;并使数字身份成为数字化经济建设的重要基石。同一实体可以更…

【数据结构初阶】第一篇——算法性能分析

算法效率 什么是大O 时间复杂度分析 概念 大O渐进表示法 不同数据规模的差异 复杂表达式的化简 O(logn)中的log是以什么为底? 案例分析 算法为什么会超时 递归算法的时间复杂度 空间复杂度分析 概念 案例分析 递归算法的性能分析 代码的内存消耗 算法效率 算法…

如何使用VMware虚拟机(带你快速了解)

前言 &#x1f4dc; “作者 久绊A” 专注记录自己所整理的Java、web、sql等&#xff0c;IT技术干货、学习经验、面试资料、刷题记录&#xff0c;以及遇到的问题和解决方案&#xff0c;记录自己成长的点滴 目录 前言 一、什么是虚拟机 二、VMware的简介 1、大概介绍 2、详…

数据结构 第四章 串

她&#xff1a;点击收听 1 基本知识点 1、串中的元素是字符 2、操作的对象往往不再是单个数据元素,而是一组数据元素(子串) 3、串&#xff1a;由零个或多个字符组成的有限序列 4、子串&#xff1a;串中任意连续个字符组成的子序列 5、包含子串的串又被称为该子串的主串 6、真…

LeetCode[685]冗余连接II

难度&#xff1a;困难题目&#xff1a;在本问题中&#xff0c;有根树指满足以下条件的 有向 图。该树只有一个根节点&#xff0c;所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点&#xff0c;而根节点没有父节点。输入一个有向图&#xf…

Linux 笔记3

5.Linux 的网络信息5.1主机名称5.1.1临时修改&#xff1a;hostname 新名字 &#xff08;需要重新进入才能显示新名字&#xff09;reboot重启虚拟机5.1.2永久&#xff1a;vi /etc/hostname5.2DNS解析dns解析域名域名-》ipdns域名劫持&#xff1a;将域名对应的ip改掉5.2.1修改主机…

networkx学习(四)无标度网络

networkx学习(四)无标度网络 无标度网络: 对于随机网络和规则网络,度分布区间非常狭窄,大多数节点都集中在节点度均值< k >的附近,说明节点具有同质性,因此< k >可以被看作是节点度的一个特征标度。而在节点度服从幂律分布的网络中,大多数节点的度都很小,…

从零创建vue示例

从零创建vue搭建node环境创建vue项目vue项目目录介绍搭建node环境 1.下载node(node官网) 安装node一路点next即可 2.windowR—cmd ----测试一下npm -v 以及node -v 显示版本号 3.执行以下命令&#xff08;-g表示全局安装&#xff09; npm install -g vue npm install -g vue…

优化命令 nload详解

优化命令 nload详解 引言 nload用于实时监控linux下网络流量信息&#xff0c;是命令行工具&#xff0c;用来监控网络的吞吐量。它使用两个图表数据来对进出站流量进行可视化。 一、nload安装 nload工具并不是centos自带的&#xff0c;需要我们手动安装下载 直接yum安装查不到…

Kettle 快捷引入数据库

在编写kettle任务时往往需要连接数据库&#xff0c;kettle一共提供了四种数据库配置方式&#xff0c;JDBC、ODBC、OCI、JNDI&#xff0c;我最初直接使用的最为熟悉的JDBC&#xff0c;但是多写几个转换程序就会发现&#xff0c;每新建一个转换任务文件时都需要重新配置数据信息&…

DDOS渗透与攻防(四)之应用层DoS攻击

系列文章 DDOS渗透与攻防(一)之拒绝服务攻击概念介绍 DDOS渗透与攻防(二)之SYN-Flood攻击 DDOS渗透与攻防(三)之socktress攻击 应用层DoS攻击 攻击协议原理介绍说明-应用层DoS 应用服务漏洞 服务代码存在漏洞&#xff0c;遇异常提交数据时程序崩溃应用处理大量并发请求能力…

Git(见Docx)

Git的概念【1】Git技术&#xff1a;公司必备&#xff0c;一定要会 【2】Git概念&#xff1a; Git是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的项目。【3】什么是版本控制&#xff1f; 版本控制是一种记录一个或若干文件内容变化&#…

Redis 发布订阅模式的深度解析与实现消息队列

1 发布订阅模式(Pub/Sub)的概述我们可以利用Redis的List数据结构实现一个简单的消息队列&#xff0c;通过lpush命令写入消息&#xff0c;通过rpop 命令拉取消息&#xff0c;也可以使用BRPOP实现阻塞式的拉取消息。上面的消息队列有一个缺点&#xff0c;那就是不支持消息多播机制…

百趣代谢组学文献分享:以猪为模型检测哺乳动物之间的代谢物交换

百趣代谢组学文献分享&#xff0c;您对哺乳动物不同器官之间的代谢物交换情况了解吗&#xff1f; 本期百趣代谢组学小趣给大家分享的是美国普林斯顿大学Joshua D. Rabinowitz团队发表在Cell Metabolism上的研究成果。该团队以猪为模型&#xff0c;利用高通量靶标技术定量测定了…

QEMU/KVM带与不带音频驱动参数的实际区别

实际工作中用到QEMU/KVM&#xff0c;按照书中的命令启动虚拟机后&#xff0c;发现Ubuntu镜像启动后找不到声卡设备&#xff0c;经过一番搜索和尝试&#xff0c;最终发现是“-device ac97”这一关键选项所导致的。现将具体的对比结果记录如下&#xff1a; 不带“-device ac97”…