【C语言】进阶——字符串和内存函数

news2024/9/28 19:22:55

 

目录

一:非限制字符串函数

1.strlen

 👊模拟实现

方法1:计算器法

方法2.指针-指针

方法3.函数调用

2.strcpy

 👊模拟实现

 3.strcat

👊模拟实现 

4.strcmp

👊模拟实现 

二:可限制字符串函数

1.strncpy

2.strncat

3.strncmp

4.strstr

 👊模拟实现

5.strtok

6.strerror-perror

三:内存函数 

1.memcpy

👊模拟实现

内存拷贝加强版

 2.memcmp

 3.memset


 

一:非限制字符串函数

1.strlen

size_t strlen ( const char * str ); 

求字符串长度;

头文件:#include<string.h>

注:计算遇到\0之前的长度;

        参数所指向的字符串必须以\0结束;

返回值:返回的是无字符的大小

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

 👊模拟实现

方法1:计算器法
#include <stdio.h>
size_t my_strlen(char* str)
{
    size_t ret;
    while(*str)
    {
        str++;
        ret++;
    }
    return ret;
}
int main() {
   char arr[]={"abcdef"};
    printf("%zd\n",my_strlen(arr));
    return 0;
}
方法2.指针-指针
#include <stdio.h>
size_t my_strlen(char* str)
{
   char* ret = str;
    while(*str)
    {
        str++;
    }
    return str-ret;
}
int main() {
   char arr[]={"abcdef"};
    printf("%zd\n",my_strlen(arr));
    return 0;
}

指针-指针的绝对值是之间相差的元素个数 

方法3.函数调用
#include <stdio.h>
size_t my_strlen(char* str)
{
   if(*str =='\0')
   {
    return 0;
   }
   else
   {
    return 1+my_strlen(str+1);
   }
}
int main() {
   char arr[]={"abcdef"};
    printf("%zd\n",my_strlen(arr));
    return 0;
}

2.strcpy

char* strncpy(char* dest, const char* src, size_t n);

拷贝字符串;

头文件#include<string.h>

将一个字符串的内容拷贝到另一个字符串;

它的参数都是两个指针,

第一个参数为目标空间的起始位置(拷贝的所在位置),

第二个参数是源字符串内容的起始位置,即被拷贝的字符串。

返回值:返回值是目标空间的起始位置;

注:

  • 将源指向的字符串复制到目标指向的空间,包括终结空字符
  • 源字符串必须以 ‘\0’ 结束。
  • 会将源字符串中的 ‘\0’ 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。
#include <string.h>
int main()
{
    char arr1[10] = "";
    char* arr2 = "abcdef";
    strcpy(arr1, arr2);
    printf("%s\n", string);    //abcdef
    return 0;
}

 👊模拟实现

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

char* my_strcpy(char* dest,const char* src)
{
    char* ret = dest;    //因为返回的是目标地址,先记录目标起始地址;
    assert(dest && src);
    while(*dest++ = *src++)    //循环 直到遇到\0
    {
        ;
    }
    return ret;
}

int main()
{
  char temp[] = "xxxxxxxxxxxxxx";
  char arr[] = "who say!!!";
  char* s = my_strcpy(temp,arr);
  printf("%s", s);
  return 0;
}

 3.strcat

char * strcat ( char * destination, const char * source );

字符串追加;

头文件:#include<string>

strcat函数将源字符串追加到目标字符串的后面;

它的两个参数是两个指针

        第一个指针指向的是目标字符串的起始位置

        第二个指针指向的是源字符串的起始位置

返回值:目标空间的起始位置;

注:

  • 源字符串必须以 ‘\0’ 结束;
  • 目标空间必须有足够的大,能容纳下源字符串的内容;
  • 目标空间必须可修改;
  • 从源字符中\0位置开始追加;
int main()
{
	char arr1[20] =  "abcdef" ;
	char arr2[] =  "abdd" ;
	printf("%s", strcat(arr1, arr2));
	return 0;
}

👊模拟实现 

先循环源字符找到'\0',然后解引用追加;

char * my_strcat( char *dest,  char *src)
{    
	char* ret = dest;    //记录起始地址
	while (*dest)     //循环找到/0位置
	{
		dest++;
	}
	while (*dest++ = *src++)    //从dest \0处开始追加
	{
		;
	}
	return ret;        //返回源字符起始地址
}

int main()
{
	char arr1[20] = {"abcdefg"};
	char arr2[] = " ggb";    //GG爆
	printf("%s\n", strcat(arr2, arr2));
}

4.strcmp

int strcmp ( const char * str1, const char * str2 );

用于比较两个字符串内容的函数;

头文件:#include<string.h>

它的两个参数都是指针,两个指针分别指向待比较的起始位置,

返回值:

当str1大于str2的时候返回一个大于0的数;

小于则返回一个小于0的数;

等于则返回0;

注:

字符串比较的是两个字符串对应的ASCII值而不是字符串的长度。

int main()
{
	char arr[] = "abcd";
	char arr2[] = "abcdef";
	printf("%d", strcmp(arr, arr2));    //返回小于0的数
	return 0;
}

👊模拟实现 

 从两个字符串的起始位置开始比较,

如果相同且不为‘\0’,则继续比较下一对字符;

如果相同且等于’\0’直接返回0;

如果目标空间的字符ASCII值大于源字符串中字符的ASCII值则返回1,否则返回-1。

#include<assert.h>
int my_strcmp(char *str1,char *str2)
{
	assert(str1 && str2);
	while (*str1==*str2)
	{
		if (*str1== '\0')
		{
			return 0;
		}
		str1++;
		str2++;
	}
	if (*str1> *str2)
	{
		return 1;
	}
	else {
		return -1;
	}
}
    
int main()
{
	char arr1[20] = "abcde";
	char arr2[] = "abcdef";
	printf("%d", my_strcmp(arr1, arr2));
	return 0;
}

二:可限制字符串函数

可限制字符串函数,就是可以限制字符串大小的函数;

1.strncpy

char * strncpy ( char * destination, const char * source, size_t num );

strcpy相比:多了个无字符型num;

限制几个拷贝字符个数,也会补\0; 

头文件:#include<string.h>;

返回值:返回目标字符串首地址;

注:

  1. 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
  2. 拷贝num个字符从源字符串到目标空间。num为多少就拷贝多少。
#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "xxxxxxxxxxxxxx";
	char arr2[] = "who say!!!";
	printf("%s", strncpy(arr1, arr2, 10));
	return 0;
}

将arr2中的10个字符拷贝到arr1中;        如果arr2中不足10个字符,直接补‘\0’,

2.strncat

char * strncat ( char * destination, const char * source, size_t num );

与strcat相比:多了个size_t型参数;指定操作字符的个数

在目标字符\0处追加 源字符的num个字符;

头文件:#include<string.h>;

返回值:返回目标字符串首地址;

注:

  1. 如果追加时操作数大于源字符串中字符个数,只会将源字符串的字符个数全部追加完就结束
  2. 追加时是从目标空间的‘\0’开始追加,追加完后再补一个‘\0’,使之成为字符串
  3. 可以自己给自己追加
int main()
{
	char arr1[10] = "abcd\0xxxxx";
	char arr2[] = "efghg";
	printf("%s", strncat(arr1, arr2, 2));
	return 0;
}

3.strncmp

int strncmp ( const char * str1, const char * str2, size_t num );

与上同理:num表示源字符限制元素个数比较

头文件:#include<string.h>;

返回值:比较规则和strcmp一致;

int main()
{
	char arr1[10] = "abc";
	char arr2[] = "defg";
	printf("%d", strncmp(arr1, arr2, 2));
	return 0;
}

4.strstr

const char * strstr ( const char * str1, const char * str2 );

strstr函数可以在一个字符串1中查找另一个字符串2;

如果字符串2能在字符串1中找到,返回字符串2在字符串1中出现的起始位置;

否则就返回空指针;

头文件:#include<string.h>;

注:

如果字符串2为空字符串,则返回的是字符串1的起始位置。

int main()
{
	char arr1 [] = "abcabc";
	char arr2[] = "abc";
	char* ret = strstr(arr1, arr2);
	if (ret != NULL)
	{
		printf("找到了");
	}
	else 
	{
		printf("没有找到");
	}
	return 0;
}

 👊模拟实现

//模拟ststr()
char* my_strstr(const char* str1, const char* str2)
{
	char* cp = str1;   //记录相匹配的位置
	char* s1 = str1;  //遍历字符串str1
	char* s2 = str2;  //遍历字符串str2
	while (s2)
	{
		while (*s1 == *s2)
		{
			s1++;
			s2++;
		}
		//s2遍历完了,返回记录相匹配的位置
		if (*s2 =='\0')
			return cp;
		//匹配位置不对,匹配位置+1
		cp++;
		s1 = cp;	//将s1拉回位置
		s2= str2;	//将s2拉回首地址,重新遍历
		//cp遍历完str1,没有相匹配位置 ,返回空指针
		if (*cp == '\0')
			return NULL;
	}
	//str2为空,返回str1
	return str1;
	
}

 

5.strtok

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

字符分割函数 

sretok函数通过给定的分隔符的字符集合中的字符去把字符串分割成若干个子字符串

注意如果分隔符的字符集合中的字符不是待分割字符串的字符,是无法分割。

所以分割符的字符集合必须是字符串中有的字符。

它的第一参数是需要被分割的字符串的首地址,第二个参数也是字符串的首地址(不分先后)

该字符串是作分割符的字符集合。返回值是查找到分隔符之前字符串的首地址。

头文件:#include<string.h>

注:

  1. strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。
  2. strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串 中的位置。
  3. strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
  4. 如果字符串中不存在更多的标记,则返回 NULL 指针。
  5. strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。
//strtok
//char* strtok(char* arr,const char* del)
int main()
{
	char arr[] = { "whosay@lana.haha" };
	char* p = "@.";
	char* ret = strtok(arr, p);
	printf("%s\n", ret);
	ret = strtok(NULL, p);
	printf("%s\n", ret);
	return 0;
}

 这样就将代码写死了,因为有时候我们无法确定输入的字符串;

而strtok函数,可以记住上次置NULL的地址。可以利用循环优化;

int main()
{
	char arr[] = { "whosay@.lana.haha" };
	char buf[100] = { 0 };
	char* p = "@.";
	char* s;
	for (s = strtok(buf, p); s != NULL; s = strtok(NULL, p))
	{
		printf("%s\n", s);
	}
	return 0;
}

6.strerror-perror

报错函数 

返回错误码,所对应的错误信息。

perror == printf + strerror

int main()
{
	FILE* pf = fopen("add.txt", "r");
	if (pf == NULL)
	{
		perror("打开文件失败"); 
		return 1;
	}
	else
	{
		printf("打开文件成功\n");
		//...
	}
	return 0;
}

这两个函数都是将错误码转换成人们能看的懂的信息,

区别就是strerror是将错误码转换成错误信息后不打印;

而perror会打印

而且perror中的字符内容是自己指定;

三:内存函数 

1.memcpy

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

mencpy函数是拷贝两块无关的内存区域数据的函数,它会从源数据中的起始位置拷贝num个字节的数据到目标空间里去;

返回值:返回目标空间的首地址

注:

  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
  • 这个函数在遇到 ‘\0’ 的时候并不会停下来。
  • 如果source和destination有任何的重叠,复制的结果都是未定义的。
#include<stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
    
	int* ret = (int*)memcpy(arr2, arr1, 20);    //返回值的类型为void*,所以强转。

	for (int i = 0; i < 5; i++) {
		printf("%d ", *(ret+i));
	}

	return 0;
}

一个整型4个字节,要拷贝5个数,所以传参的第三个参数为20.

👊模拟实现

#include<stdio.h>
#include<string.h>
#include<assert.h>
void* my_memcpy(void* dest, void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src= (char*)src+ 1;
	}
	return (char*)ret;
}

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

	for (int i = 0; i < 5; i++) {
		printf("%d ", *(ret + i));
	}

	return 0;
}

2.memmove

内存拷贝加强版

menmvoe函数和menecpy函数的参数和返回值是一模一样的;

memmvoe函数和memcpy函数最大的区别:

  1.         memmove函数操作的源内存块和目标空间的内存块是可以重叠的
  2.         而memcpy函数的源内存块和目标空间的内存块是不能重叠
#include<stdio.h>
#include<string.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int* ret = (int*)memmove(arr+3 , arr, 20);
		for (int i = 0; i <=6; i++) {
		printf("%d ", *(ret + i));
	}

	return 0;
}

 👊模拟实现

将拷贝情况分为三种

  1. dest指针位于sour内存块的左边,从前向后拷贝。
  2. dest指针在sour内存块内,则从后向前拷贝。
  3. dest指针与sour内存块位于同一区域,则可以从后往前拷贝也可以从前往后拷。

为了方便分为两种情况,直接划分从前往后拷和从后往前拷。

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

int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1 + 2, arr1, 16);
	int i = 0;
	int sz = sizeof(arr1) / sizeof(arr1[0]);
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

 2.memcmp

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

mencmp函数是比较两个内存块大小的函数

它会比较ptr1和ptr2开始的num个字节,

  1. 当ptr1>ptr2时,返回一个大于0的数;
  2. 当ptr1<ptr2时,返回一个小于0的数;
  3. 相等时则返回0;
#include<stdio.h>
#include<string.h>
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,0x1122334455 };
	int ret = memcmp(arr1, arr2, 16);
	printf("%d\n", ret);
	return 0;
}

 3.memset

void *menmset(void* dest ,int num,size_t count)

内存设置

memset函数可以将内存块中的的某一部分修改为指定的字符。

三个参数,

第一个参数是目标的起始位置,

第二个参数是指定的修改内存区域的字符,

第三个参数是从起始位置开始设置的内存的字节个数。

memset是以字节为单位来初始化内存单元的。

int main()
{
	char arr[] = "hello world" ;
	memset(arr, 6, 5);
	
	return 0;
}

身为初学者,自知有很多不足,希望得到大佬们指点和改善!!!

 

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

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

相关文章

Echarts 旭日图的详细配置过程

文章目录 旭日图 简介配置过程简易示例 旭日图 简介 Echarts旭日图是一种数据可视化图表类型&#xff0c;用于展示层次关系数据的分布情况。旭日图通过不同的环形区域和扇形区域来表示数据的层次和大小关系&#xff0c;从而形成一个太阳的形状&#xff0c;因此得名旭日图。 E…

WebGL 计算平行光、环境光下的漫反射光颜色

目录 光照原理 光源类型 平行光 点光源 环境光 反射类型 漫反射 漫反射光颜色 计算公式 环境反射 环境反射光颜色 表面的反射光颜色&#xff08;漫反射和环境反射同时存在时&#xff09;计算公式 平行光下的漫反射 根据光线和法线方向计算入射角θ&#xff08;以便…

Intellij IDEA 提效小技巧

快速找到Controller方法 如果你的项目里有非常多的controller&#xff0c;里面有非常多的http或者resful方法。如何快速找到这些方法呢&#xff1f;这个时候&#xff0c;ctrlaltshiftn就可以派上用场了。 比如说&#xff0c;你依稀记得入账单相关的接口&#xff0c;都有个bil…

让Pegasus天马座开发板用上OLED屏

继上篇《让Pegasus天马座开发板吃上STM8S标准库》移植完标准库之后&#xff0c;于是我又想为天马座开发板添加一块屏幕。终于在我的零件箱底下找到了沉入箱底多年的0.96OLED屏幕。 屏幕介绍 这个是128x64像素的屏幕模块&#xff0c;其使用的SSD1306的驱动IC。而目前该模组&…

软件测试为什么外包更好?权威软件测试外包公司应该具备的资质

软件测试外包公司是一家专门从事软件测试服务的企业&#xff0c;其主要任务是帮助公司或个人进行软件产品的测试工作。相比较于自行开设测试部门或雇佣全职测试人员&#xff0c;外包软件测试具有成本更低、灵活性更高的优势。同时&#xff0c;外包公司通常拥有丰富的测试经验和…

Java高级-Junit单元测试框架

单元测试框架 1.介绍2.案例、断言机制3.常见注解 1.介绍 单元测试 就是在针对最小的功能单元方法&#xff0c;编写测试代码对其正确性测试 Junit单元测试框架 可以对方法进行测试&#xff0c;是第三方公式开源出来的 优点 可以灵活的编写测试代码&#xff0c;可以针对某个…

Stable Diffusion 参数介绍及用法

大模型 CheckPoint 介绍 作用&#xff1a;定调了作图风格&#xff0c;可以理解为指挥者 安装路径&#xff1a;models/Stable-diffusion 推荐&#xff1a; AnythingV5Ink_v32Ink.safetensors cuteyukimixAdorable_midchapter2.safetensors manmaruMix_v10.safetensors counterf…

leetcode刷题笔记——位运算

C/C语言中逻辑右移和算数右移共享同一个运算符>> 如果运算数类型是unsigned则采用逻辑右移&#xff0c;而signed则采用算数右移。对于signed类型的数据&#xff0c;如果需要使用算数右移&#xff0c;或者unsigned类型的数据需要使用逻辑右移&#xff0c;都需要进行类型转…

JAVASE---认识异常

在Java中&#xff0c;将程序执行过程中发生的不正常行为称为异常。 1.算数异常 2.数组越界异常 3.空指针异常 java中不同类型的异常&#xff0c;都有与其对应的类来进行描述。 异常的体系结构 1. Throwable&#xff1a;是异常体系的顶层类&#xff0c;其派生出两个重要的子…

阿里云服务器租用费用价格表(2023新版报价)

租用阿里云服务器怎么收费&#xff1f;阿里云服务器配置不同一年价格也不同&#xff0c;阿里云2核2G3M带宽108元一年、2核4G4M带宽297.98元12个月&#xff0c;云服务器u1公网带宽可选1M到5M&#xff0c;系统盘为ESSD云盘40GB起&#xff0c;CPU内存配置可选2核2G、2核4G、4核8G、…

Python爬虫在电商数据获取与分析中的应用

前言 随着电商平台的兴起&#xff0c;越来越多的人开始在网上购物。而对于电商平台来说&#xff0c;商品信息、价格、评论等数据是非常重要的。因此&#xff0c;抓取电商平台的商品信息、价格、评论等数据成为了一项非常有价值的工作。本文将介绍如何使用Python编写爬虫程序&a…

BaseRecyclerView - 一个强大的RecyclerAdapter框架

官网 BRVAH 项目介绍 高效的使用RecyclerView应对项目中的常见需求的Adapter&#xff0c;RecycleView从未如此简单&#xff01; BRVAH官方使用指南 BRVAH官方使用指南&#xff08;持续更新&#xff09; - 简书

智慧交通:连接城市未来的纽带

在当今快节奏的现代生活中&#xff0c;交通问题一直是城市面临的重要挑战之一。拥堵、事故和空气污染等问题不仅影响着居民的日常生活&#xff0c;也对经济和环境产生了负面影响。为了解决这些问题&#xff0c;智慧交通作为一项重要的技术和社会创新出现在我们的视野中。 智慧交…

docker镜像相关

docker镜像相关 docker镜像相关理解解释unionFS&#xff08;联合文件系统&#xff09;镜像加载原理docker镜像要采用这种分层结构 重点理解docker镜像commit 操作实例案例演示总结 docker镜像相关理解 解释 镜像是一种轻量级&#xff0c;可执行的独立软件包&#xff0c;它包含…

软考高级之系统架构师之企业应用集成EAI

概述 在企业信息化建设的过程中&#xff0c;由于缺乏统一规划和总体布局&#xff0c;往往形成多个信息孤岛。信息孤岛使数据的一致性无法得到保证&#xff0c;信息无法共享和反馈&#xff0c;需要重复多次的采集和输入。信息孤岛是企业信息化一个重要的负面因素&#xff0c;其…

SpringBoot结合Vue.js+axios框架实现增删改查功能+网页端实时显示数据库数据(包括删除多条数据)

本文适用对象&#xff1a;已有基础的同学&#xff0c;知道基础的SpringBoot配置和Vue操作。 在此基础上本文实现基于SpringBoot和Vue.js基础上的增删改查和数据回显、刷新等。 一、实时显示数据库数据 实现步骤&#xff1a; 第1步&#xff1a;编写动态请求响应类&#xff1…

由河北吉力宝战略发展规划看中国品牌商业发展新方向

当今时代&#xff0c;一个经济体的发展和崛起背后&#xff0c;往往是一批民族品牌在提供强力的支撑。中国作为全世界最大的发展中国家&#xff0c;在经济建设中取得了举世瞩目的发展成就&#xff0c;各个行业涌现出一批优秀的国民品牌。 随着高质量发展成为各行各业的广泛共识…

docker alpine:3.16 root权限安装Anaconda3-2020.07-Linux-x86_64和jdk

首先查看系统版本: rootfv-az454-287:/tmp# uname -a Linux fv-az454-287 5.15.0-1046-azure #53~20.04.1-Ubuntu SMP Mon Aug 28 14:17:23 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux rootfv-az454-287:/tmp# grep NA /etc/os-release NAME"Ubuntu" PRETTY_NAME&q…

笔记本电脑没有麦克风,声音无法找到输入设备

新买的电脑没有扬声器&#xff0c;电脑声音没有输入设备&#xff0c;在开腾讯会议的时候才发现竟然有这个问题。 网上找原因&#xff0c;哎&#xff0c;找了一大堆每一个靠谱的 这让我想起来上次电脑没有热键的问题&#xff0c;所有问题的终极解决方案&#xff0c;都在源头那里…

加密算法总结

数字签名、信息加密 是前后端开发都经常需要使用到的技术&#xff0c;应用场景包括了用户登入、交易、信息通讯、oauth 等等&#xff0c;不同的应用场景也会需要使用到不同的签名加密算法&#xff0c;或者需要搭配不一样的 签名加密算法 来达到业务目标。这里简单的给大家介绍几…