C语言中字符串函数以及内存函数的使用和注意事项

news2024/11/17 3:52:44

目录

0. 前言

1、求字符串长度函数

1.1、strlen

模拟实现

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

2.1 strcpy

 模拟实现

 2.2strcat

模拟实现

 2.3strcmp

模拟实现

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

3.1strncpy

3.2strncat

3.3strncmp

4、字符串查找函数

4.1strstr

模拟实现

3.2strtok 

 实现自动分割字符串

4、错误信息报告函数

4.1、strerror

 4.2、perror

5.内存操作函数

5.1、memcpy

模拟实现

5.2、memmove

模拟实现

 5.3、memset

 5.4、memcmp


0. 前言

C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在 常量字符串中或者字符数组中。 字符串常量适用于那些对它不做修改的字符串函数

1、求字符串长度函数

1.1、strlen

  • 1.strlen用于求字符串长度。
  • 2.包含头文件<string.h>。
  • 3.字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )。
  • 4.参数指向的字符串必须要以 '\0' 结束。

注意:

  • 函数的返回值为size_t是无符号的易错
  • 这里输出的应该是大于,为什么呢,就是这里的strlen函数的返回的是无符号类型的,所以-3会被当作无符号数来看待,

  • 由于strlen返回的是size_t类型,所以在输出时应该使用对应的格式说明符,如%zu

  • 因为strlen返回的是 '\0' 前面的字符个数,如果字符串中间本身就一个'\0',那么返回的值就会返回字符串中的'\0'之前的字符个数。

    例如:"abc\0def" 这个字符串,使用strlen函数会返回3

 易错提醒


请问ret的值是多少?
int ret = strlen("abc") - strlen("abcdef");

答案是3,因为函数的返回值为size_t,是无符号的整型。


strlen函数是C语言中的一个库函数,用于计算字符串的长度(不包括字符串末尾的空字符\0)。它位于<string.h>头文件中。

strlen函数的原型如下:

size_t strlen(const char *str);
  • const char *str:指向要计算长度的字符串的指针。

strlen函数会从指定的字符串地址开始遍历,直到遇到字符串结束标记\0为止,统计期间遇到的字符数量,然后返回计数结果

下面是一个示例代码,展示了如何使用strlen函数来计算字符串的长度:

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

int main() {
    char str[] = "Hello, World!";
    size_t len = strlen(str);

    printf("The length of the string is: %zu\n", len);

    return 0;
}

输出结果为:

The length of the string is: 13

在上面的例子中,strlen函数被用来计算字符串str的长度,并将结果存储在len变量中,然后通过printf函数进行输出。



模拟实现

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

 方法二、

size_t my_strlen(const char* str) {
    const char* end = str;
    while (*end) {
        ++end;
    }
    return end - str;
}

指针-指针的方式实现先把初始地址给end,然后end向后走直到遇到\0,最后返回初始地址和结束地址的差值就是中间的字符个数。

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

2.1 strcpy

`strcpy`函数是C语言中用于字符串复制的函数,其原型为:

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

`strcpy`函数将源字符串 `source` 复制到目标字符串 `destination` 中,包括字符串结束符 `'\0'`。复制的过程会覆盖原来 `destination` 中的内容。

`strcpy`函数的工作原理是从源字符串 `source` 的第一个字符开始,逐个将字符复制到目标字符串 `destination` 中,直到遇到字符串结束符 `'\0'` 为止。复制完成后,目标字符串 `destination` 会成为与源字符串 `source` 相同的副本。

需要注意的是,为了避免内存溢出,目标字符串 `destination` 的空间应该足够大,至少能够容纳源字符串 `source` 和字符串结束符 `'\0'`。

`strcpy`函数的返回值是目标字符串 `destination` 的指针,即函数执行完后,返回指向目标字符串的指针。

下面是一个简单的示例代码,演示了 strcpy 函数的使用:

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

int main() {
    char source[] = "Hello, World!";
    char destination[20];

    strcpy(destination, source);

    printf("Copied string: %s\n", destination);

    return 0;
}

上述代码会输出:Copied string: Hello, World!

注意事项:

  • strcpy用于拷贝字符串,将字符串2拷贝到字符串1当中。
  • 包含头文件<string.h>。
  • 源字符串必须以 '\0' 结束。
  • 会将源字符串中的 '\0' 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。

验证确实是拷贝了\0
源字符串必须以\0结尾
目标空间必须可变化
这里就没法完成复制了,因为arr1是一个常量字符串没有办法被修改

 模拟实现


char* my_strcpy(char* dest,const char* src)
{
	char* ret = dest;
	while (*dest = *src)
	{
		dest++;
		src++;
	}
	return ret;
}

 2.2strcat

cat 是 concatenate(连接)的缩写。

strcat函数是C语言中用于字符串拼接的函数,其原型为:

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

效果strcat函数将源字符串 source 追加到目标字符串 destination 的末尾,拼接的结果会改变目标字符串 destination

工作原理:strcat函数的工作原理是首先定位目标字符串 destination 的结束符 '\0' 的位置,然后从源字符串 source 的第一个字符开始,逐个将字符复制到目标字符串的结束符之前,直到遇到源字符串的结束符 '\0' 为止。完成拼接后,目标字符串 destination 会包含原有内容和源字符串 source 的内容。

需要注意的是,为了避免内存溢出,目标字符串 destination 的空间应该足够大,以容纳源字符串 source 的内容及拼接后的结果。

 源字符串必须以 '\0' 结束(保证找得到目标空间的末尾),在拷贝时会把源字符串的 '\0 '也拷贝过去

不能字符串自己追加自己,因为当自己追加自己的时候,追加的过程中会将目标字符串的 '\0' 覆盖掉,(因为自己追加自己两个指针指向的是同一块空间)而有因为此时目标字符串就是源字符串,就会导致源字符没有 '\0' ,将会一直拼接下去导致死循环。

        虽然有些环境中该函数可以完成自己拼接自己,但是C语言的标准中并未规定strcat可以自己拼接自己,所以这个函数最好不要使用在自己拼接自己的情况下。如果真有自己追加自己的场景,建议使用strncat函数

返回值:strcat函数的返回值是目标字符串 destination 的指针,即函数执行完后,返回指向目标字符串的指针。

模拟实现

1.先找到目标空间的\0

  

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

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

int main()
{
	char arr1[20] = "hello ";
	char arr2[] = "world";
	my_strcat(arr1, arr2);

	printf("%s\n", arr1);
	return 0;
}

 问题:字符能不能自己给自己追加?

不能

  

源头和目标都是一块空间了,当src把h替代了\0,之后src就没有办法遇到\0了,因为追加第一个的时候就修改掉了\0

 2.3strcmp

strcmp 函数用于比较两个字符串,它返回一个整数值,表示两个字符串的大小关系。

函数原型如下:

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

参数 str1 和 str2 是需要比较的两个字符串。

  • 如果 str1 和 str2 相等,strcmp 返回值为 0。
  • 如果 str1 大于 str2strcmp 返回值大于 0。
  • 如果 str1 小于 str2strcmp 返回值小于 0。

下面是一个示例代码,演示了 strcmp 函数的用法:

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

int main() {
    char str1[] = "apple";
    char str2[] = "banana";

    int result = strcmp(str1, str2);

    if(result == 0) {
        printf("str1 和 str2 相等\n");
    } else if(result > 0) {
        printf("str1 大于 str2\n");
    } else {
        printf("str1 小于 str2\n");
    }

    return 0;
}

输出结果为:

str1 小于 str2

上述示例中,strcmp 函数比较了两个字符串 "apple" 和 "banana",并根据比较结果输出相应的信息。由于字母 "a" 的 ASCII 值小于字母 "b",因此 str1 小于 str2

易错:strcmp不是比较字符串长度的,而是比较对应位置上字符的大小(ASCII)

模拟实现

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

	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;

		str1++;
		str2++;
	}

	return (*str1 - *str2);
}
//VS
//> 1
//= 0
//< -1
//

int main()
{
	int ret = my_strcmp("bbq", "bcq");
	if (ret>0)
		printf(">\n");

	printf("%d\n", ret);
	return 0;
}

 先排除相等的部分,剩下不相等的部分才进行比较,第一个while循环中*str1==‘\0’判读是不是读完了

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

3.1strncpy

 函数介绍:

strncpy 函数是一个用于复制字符串的函数。它的原型如下:

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

strncpy 函数将从源字符串 src 复制最多 n 个字符到目标字符串 dest 中。

如果 src 的长度小于 n,则复制完 src 中的所有字符后,将剩余部分用空字符 \0 填充直到n

如果 src 的长度大于等于 n,则仅复制前 n 个字符,不会添加额外的空字符。

需要注意的是,strncpy 函数没有在目标字符串 dest 的末尾自动添加终止符 \0 的能力。因此,在使用 strncpy 复制字符串后,需要手动在 dest 的最后一个元素添加 \0,以确保目标字符串正确终止。

 相比较于strcpy函数多了一个参数 n。

下面是一个示例代码,展示了如何使用 strncpy 函数:

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

int main() {
    char src[] = "Hello, World!";
    char dest[20];

    strncpy(dest, src, sizeof(dest));
    dest[sizeof(dest) - 1] = '\0';

    printf("Source: %s\n", src);
    printf("Destination: %s\n", dest);

    return 0;
}

输出结果:

Source: Hello, World!
Destination: Hello, World!

在这个示例中,我们将源字符串 "Hello, World!" 复制到了目标字符串 dest 中,并保证了目标字符串的正确终止。由于 dest 的大小为 20,所以在复制后,目标字符串中的剩余部分将被空字符 \0 填充。

3.2strncat

  • 区别也仅与strcat差一个参数,记录要操作的个数。
  • 使用strncat追加,当结束追加时,就算没到\0,也会在末尾追加一个\0。
  • 如果源字符串的长度小于num,则追加完源字符串之后,会自动停止追加。注意此处与strncpy的区别。
  • 包含头文件<string.h>。

下面是示例代码:

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

int main() {
    char s1[20] = "Hello ";
    char s2[] = "world!";
    
    strcat(s1, s2);
    printf("Result of strcat: %s\n", s1);
    
    char s3[20] = "Hello ";
    char s4[] = "world!";

    strncat(s3, s4, 5);
    printf("Result of strncat: %s\n", s3);
    
    return 0;
}

输出结果为:

Result of strcat: Hello world!
Result of strncat: Hello world

可以看到,在使用strncat函数时,只拼接了源字符串的前5个字符。

3.3strncmp

  • 区别也仅与strcmp差一个参数,记录要操作的个数。
  • 包含头文件<string.h>。

下面是一个使用strncmp函数比较两个字符串的示例代码:

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

int main()
{
    char str1[] = "Hello";
    char str2[] = "World";
    int result = strncmp(str1, str2, 3); // 只比较前3个字符

    if (result == 0)
    {
        printf("str1和str2的前3个字符完全相同\n");
    }
    else if (result < 0)
    {
        printf("str1小于str2\n");
    }
    else
    {
        printf("str1大于str2\n");
    }

    return 0;
}

以上代码输出结果为"str1小于str2",因为在比较前3个字符时,'H'的ASCII值为72,'W'的ASCII值为87,72小于87。

4、字符串查找函数

4.1strstr

strstr函数是一个C语言字符串处理函数,用于在一个字符串中查找另一个字符串的第一次出现位置。

函数原型如下:

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

函数接受两个参数:str1和str2。str1是源字符串,在该字符串中进行查找操作。str2是要查找的目标字符串。

函数的返回值是一个指向第一次在str1中出现str2的位置的指针。如果未找到str2,函数返回NULL。

strstr函数的工作原理是基于字符串匹配算法,通常使用的是KMP算法或Boyer-Moore算法等。它会从str1的第一个字符开始比较,如果找到了一个匹配的字符,就会继续比较下一个字符。如果完全匹配,就返回匹配位置的指针。如果未能找到匹配,就继续在剩余的str2中继续查找。

int main()
{
	char arr1[] = "abcdefghidef";   //def出现了两次
	char arr2[] = "def";
	char* ret = strstr(arr1, arr2);
	if (ret == NULL)
		printf("找不到\n");
	else
		printf("%s\n", ret);
	return 0;
}

 结果是defghidef

模拟实现

char* my_strstr(char *str1, char* str2)
{
	char* cp = str1;
	char* s1 = cp;
	char* s2 = str2;

	if (*str2 == '\0')
		return str1;

	while (*cp)
	{
		//开始匹配
		s1 = cp;
		s2 = str2;
		while (*s1 && *s2 && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return cp;

		cp++;
	}

	return NULL;
}


int main()
{
	char arr1[] = "abbbcdef";
	char arr2[] = "bbc";

	char* ret = my_strstr(arr1, arr2);
	if (ret != NULL)
		printf("%s\n", ret);
	else
		printf("找不到\n");

	return 0;
}

Cp最开始指向起始位置,cp赋给s1

*cp不等于\0,因为等于\0说明检查完了,就返回NULL

S1和s2来遍历字符串

没有匹配完全的话,跳出第二个while

循环,然后cp++,改变开始匹配的位置,然后把cp赋给s1,重新赋值给s1是因为要找到开始匹配的正确位置,每次cp都像后面移动一位

应该返回cp因为只有*s1==*s2才会进入循环,这个时候cp里面是第一次出现位置的指针。

只有*s2为\0才是才是在str1中匹配到了str2         

最前面是特殊情况是str2是空字符串       

                            

3.2strtok 

strtok函数是C语言中的一个字符串分割函数,其原型为:

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

该函数的作用是将字符串str按照指定的分割符delim进行分割,并返回分割后的第一个子字符串。同时,该函数会静态地记录当前的分割位置,便于下一次调用时继续分割剩余的字符串。

函数的使用步骤如下:

  1. 在第一次调用时,需要将待分割的字符串str传入,之后的调用中将传入NULL。strtok函数会将其修改,将分割出的子字符串以'\0'结尾并返回该子字符串的指针。
  2. 在第一次调用时,需要将分割符delim传入。之后的调用中,delim可以传入NULL。如果传入NULL,则将沿用上一次调用时的分割符。
  3. 使用返回的子字符串指针进行处理操作,之后再次调用strtok函数可以获取下一个分割出的子字符串指针。直到返回NULL,表示没有更多的子字符串可供分割。
  4. delimiters参数是个字符串,定义了用作分隔符的字符集合。
    第一个参数指定一个字符串,它包含了0个或者多个由delimiters字符串中一个或者多个分隔符分割的标记。
    strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
    strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
    strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
    如果字符串中不存在更多的标记,则返回 NULL 指针

下面是一个简单的示例代码,展示了如何使用strtok函数分割一个字符串:

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

int main() {
    char str[] = "Hello,World!How,are,you?";
    char *token;

    // 第一次调用strtok函数
    token = strtok(str, ",!");
    printf("%s\n", token);  // 输出"Hello"

    // 之后的调用
    while ((token = strtok(NULL, ",!")) != NULL) {
        printf("%s\n", token);
    }

    return 0;
}

该代码首先将字符串"Hello,World!How,are,you?"传给strtok函数进行第一次调用。由于传入的分割符为",!",所以它会将字符串分割成"Hello"、"World"、"How"、"are"和"you?"五个子字符串。然后,通过循环调用strtok函数,我们可以依次输出这五个子字符串。

第一次调用把,改为\0然后返回H的地址,第二次函数已经记住了第一次标记的位置,然后向后访问直到找到下一个标记然后改为\0,再传回来W的地址。

 实现自动分割字符串

	char* ret = NULL;

	for (ret = strtok(copy, sep); ret != NULL; ret=strtok(NULL, sep))
	{
		printf("%s\n", ret);
	}

初始部分就是第一次调用strtok函数,判断部分是判断

调整部分是传空指针直到切割完了。

循环中的判断部分 ret != NULL 是用来判断 strtok 函数返回的切割结果是否为 NULL,如果是 NULL,则意味着没有更多的切割结果了,循环就会结束。换句话说,当 strtok 函数切割完所有的子字符串之后,它会返回 NULL,这时循环就会终止。

4、错误信息报告函数

4.1、strerror

strerror 函数是一个 C 标准库中的函数,它可以根据给定的错误码返回对应的错误消息字符串。

strerror 函数的原型如下:

#include <string.h>

char *strerror(int errnum);

其中,errnum 是一个整数类型的参数,表示错误码。函数会根据 errnum 找到对应的错误消息字符串,并返回该字符串的指针。

使用 strerror 函数,我们可以根据系统函数返回的错误码获取相应的错误信息,从而更好地了解问题所在,方便进行调试和错误处理。

  • strerror函数是将错误码翻译成错误信息,返回错误信息的字符串起始地址。
  • 包含头文件<string.h>。
  • C语言中使用库函数的时候,如果发生错误,就会将错误码放在errno的变量中,errno是一个 全局变量,可以直接使用。

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

 每一个错误码都对应一个错误信息。

 

【使用方式】

以打开文件为例子,fopen以读的形式打开文件,当文件存在时打开成功,文件不存在时打开失败,并返回空指针。可以利用这个来设置一个打开失败时的错误信息告知。

 【使用方法】

int main()
{
	FILE* pf = fopen("add.txt", "r");  //当前文件路径中并没有add.txt文件,打开失败
	if (pf == NULL)
	{
		printf("打开文件失败,原因是:%s\n", strerror(errno));
		return 1;
	}
	else
	{
		printf("打开文件成功\n");
	}
	return 0;
}

fopen 是 C 语言标准库中的一个函数,用于打开一个文件。函数声明如下:

FILE *fopen(const char *filename, const char *mode);

fopen 函数接受两个参数,第一个参数 filename 是一个字符串,表示文件的路径或名称;第二个参数 mode 是一个字符串,表示文件的打开模式,可以是以下几种之一:

  • "r":以只读方式打开文件。
  • "w":以写入方式创建文件,如果文件已存在则清空文件内容。
  • "a":以写入方式打开文件,如果文件不存在则创建文件,新增的内容会被追加到文件末尾。
  • "rb":以二进制只读方式打开文件。
  • "wb":以二进制写入方式创建文件,如果文件已存在则清空文件内容。
  • "ab":以二进制写入方式打开文件,如果文件不存在则创建文件,新增的内容会被追加到文件末尾。

fopen 函数返回一个 FILE* 类型的指针,如果打开文件成功,则返回指向该文件的指针;如果打开文件失败,则返回 NULL

 4.2、perror

perror 是 C 语言标准库中的一个函数,用于打印与当前错误代码相对应的错误信息到标准错误文件 stderr 中。函数声明如下:

void perror(const char *s);

perror 函数接受一个参数 s,表示要输出的错误信息前缀。它会根据全局变量 errno 的值,在标准错误文件上输出一个相应的错误信息,形如 s: 错误信息

perror也是用于翻译错误信息 ,但与strerror不同的是,perror会直接打印错误码所对应的错误信息。而perror中传递的字符串参数就是自定义显示信息的部分,打印的结果就是 自定义显示信息:错误信息


包含头文件<stdlib.h>


可以简单理解为:perror = printf + strerror 即翻译又打印

5.内存操作函数

为了满足各种数据都可以使用的情况而不只是字符串,我们要从内存入手。下面我们来学习一下内存操作函数。

5.1、memcpy

memcpy() 函数是 C 语言中的一个标准库函数,它用于将内存块的内容从源地址复制到目标地址。它的函数原型如下:

void *memcpy(void *dest, const void *src, size_t n);
  • dest:目标地址,即复制后的数据将存储的位置。
  • src:源地址,即要被复制的数据的起始位置。
  • n:要复制的字节数。
  • 拷贝结束之后返回目标空间的起始地址

memcpy() 函数会将 src 地址开始的 n 个字节的内容复制到 dest 地址开始的内存区域。需要注意的是,复制过程中不会考虑字符的含义,只是简单地复制二进制数据

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


包含头文件<string.h>


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


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


因为C语言标准中并未规定memcpy能适用于重叠内存的拷贝,因此不重叠内存的拷贝才使用memcpy,而重叠内存的拷贝使用接下来讲解的memmove函数。

下面是一个使用 memcpy() 函数的示例:

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

int main() {
    const char src[] = "Hello, World!";
    char dest[100];

    memcpy(dest, src, strlen(src) + 1);
    printf("Copied string: %s\n", dest);

    return 0;
}

在这个示例中,我们首先定义了一个源字符串 src,然后创建了一个足够大的目标字符串 dest 来存储复制后的内容。然后,memcpy() 函数将 src 字符串的内容复制到 dest 字符串,并通过 printf 函数打印出复制结果。

模拟实现

函数拷贝结束后,返回目标空间的起始地址
void* my_memcpy(void* dest, const void* src, size_t num)
{
	void* ret = dest;
	assert(src && dest);

	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 };
	//01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00 06 00 00 00

	int arr2[20] = { 0 };
	my_memcpy(arr2, arr1, 21);
	int i = 0;
	for (i = 0; i < 20; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

写成(char*)dest++是不能实现指针先后走一个bit的,因为这里强制转换是临时的,++的时候已经没用了。

如果拷贝有重叠的空间会怎么样呢

比如传参为arr+2和arr1

void* my_memcpy(void* dest, const void* src, size_t num)
{
	void* ret = dest;
	assert(src && dest);

	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 };
	//             1 2 1 2 3 4 5 8 9 10
	my_memcpy(arr1+2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

结果不是我们想要的那样1 21 2 3 4 5 8 9 10

因为空间上有重叠的地方

把34拷贝为12之后想要把56拷贝为34就没有办法实现了因为34已经被改成12了。

memcpy函数是用来处理不重叠的内存的拷贝的,处理有重叠的内存的拷贝的使用memmove函数

5.2、memmove

原来出现的错误的原因是因为拷贝给后面的空间的时候前

想办法不被覆盖。到着来拷贝可以解决问题先把5拷贝到7的位置,4拷贝到6的位置,

但是不能一劳永逸。比如如果我把src和des调换就出错了

dest在src前面就要倒着拷贝,dest在src后面就要

一次性解决:

如果dest落在src前面那就是从前向后拷贝就是说先把src的第一个位置拷贝到dest依次往后

如果dest落在src到末尾之间的位置,那就是从后向前面拷贝

指针可以比较大小

模拟实现

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--)//20
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	return ret;
}

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

后向前拷贝要先找到src+num的位置,这个是第一个要拷贝的。

 5.3、memset

memset()函数用于将一块内存区域的每个字节都设置为特定的值。其函数原型如下:

void *memset(void *ptr, int value, size_t num);
  • ptr:指向要设置值的内存区域的指针。
  • value:要设置的值,它的类型是int,但会被转换成unsigned char
  • num:要被设置的字节数。
  • 将ptr所指向空间的前num个字节设置为指定值value。
  • 包含头文件<string.h>

memset()函数在初始化内存块、清零内存块、以及将内存块中的特定字节设置为某个值时非常有用。例如:

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

int main() {
    char str[50];
    
    strcpy(str, "Hello");
    printf("Before memset: %s\n", str);
    
    memset(str, '$', 3);
    printf("After memset: %s\n", str);
    
    return 0;
}

输出结果为:

Before memset: Hello
After memset: $$$lo

在上述示例中,memset()函数将字符串str的前3个字节设置为'$',修改了字符串的一部分内容。

 5.4、memcmp

memcmp()函数用于比较两个内存区域的内容是否相同。其函数原型如下:

int memcmp(const void *ptr1, const void *ptr2, size_t num);
  • ptr1:指向要比较的第一个内存区域的指针。
  • ptr2:指向要比较的第二个内存区域的指针。
  • num:要比较的字节数。

memcmp()函数将比较两个内存区域中的字节内容,并返回一个整数结果,表示比较结果的大小关系:

  • 如果ptr1ptr2指向的内存区域完全相同,则返回值为0。
  • 如果ptr1指向的内存区域小于ptr2指向的内存区域,则返回值小于0。
  • 如果ptr1指向的内存区域大于ptr2指向的内存区域,则返回值大于0。

下面是一个使用memcmp()函数的示例:

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

int main() {
    char str1[] = "Hello";
    char str2[] = "World";
    
    int result = memcmp(str1, str2, 5);
    
    if (result == 0) {
        printf("str1 and str2 are equal.\n");
    } else if (result < 0) {
        printf("str1 is less than str2.\n");
    } else {
        printf("str1 is greater than str2.\n");
    }
    
    return 0;
}

输出结果为:

str1 is less than str2.

在上述示例中,memcmp()函数比较了两个字符串str1str2的前5个字节内容,发现str1小于str2,因此打印出相应的结果。 

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

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

相关文章

使用51单片机控制T0和T1分别间隔1秒2秒亮灭逻辑

#include <reg51.h>sbit LED1 P1^0; // 设置LED1灯的接口 sbit LED2 P1^1; // 设置LED2灯的接口unsigned int cnt1 0; // 设置LED1灯的定时器溢出次数 unsigned int cnt2 0; // 设置LED2灯的定时器溢出次数// 定时器T0 void Init_Timer0() {TMOD | 0x01;; // 定时器…

HarmonyOS ArkUI实战开发-页面跳转(Router、Ability)

页面跳转可以分为页面内跳转和页面间跳转&#xff0c;页面内跳转是指所跳转的页面在同一个 Ability 内部&#xff0c;它们之间的跳转可以使用 Router 或者 Navigator 的方式&#xff1b;页面间跳转是指所跳转的页面属与不同的 Ability &#xff0c;这种跳转需要借助 featureAbi…

Java中的对象

什么是类和对象 在Java中类是物以类聚&#xff0c;分类的思维模式&#xff0c;思考问题首先会解决问题需要哪些分类&#xff0c;然后对这些类进行单独思考&#xff0c;最后才是对某分类下的细节进行单独思考 面向对象适合处理复杂问题适合处理需要多人协作的问题 在Java中面向…

二维前缀和与差分

前言 延续前面所讲的一维前缀和以及差分&#xff0c;现在来写写二维前缀和与差分 主要这个画图就比前面的一维前缀和与差分复杂一点&#xff0c;不过大体思路是一样的 一维和二维的主要思路在于一维是只针对对一行一列&#xff0c;而二维是针对与一个矩阵的 好吧&#xff0…

可视化大屏在政务领域应用非常普遍,带你看看

可视化大屏在政务领域的应用非常普遍&#xff0c;政务领域需要处理大量的数据和信息&#xff0c;通过可视化大屏可以将这些数据以直观、易懂的方式展示出来&#xff0c;帮助政府决策者和工作人员更好地了解和分析数据&#xff0c;从而做出更准确、科学的决策。 在政务领域&…

java学习之路-抽象类和接口

目录 前言 1.抽象类 1.2抽象类语法 1.3抽象类特性 1.4抽象类的作用 2.接口 2.1接口概念 2.2接口的定义 2.3接口的使用 接口使用栗子 2.4接口特性 2.5 实现多个接口 请看栗子 2.6接口间的继承 2.7接口使用实例 2.8Clonable 接口和深浅拷贝 2.9 抽象类和接口的区别…

Qt图片等资源管理

Qt的图片等资源管理通常有两种方式 1&#xff0c;直接将图标和一些配置文件打包在可执行程序中 添加qrc文件&#xff0c;可使用qtcreator直接添加 右键选中工程 点击选择即可。 然后添加文件。我这个例子是添加了Image文件夹下的图片资源 使用的时候&#xff0c;可以在代码…

MySQL学习笔记1(MySQL基础)

1.MySQL基础 1.数据库相关概念 ​ *数据库&#xff1a;存储数据的仓库&#xff0c;数据是有组织的进行存储 DtaBase(DB) ​ *数据管理系统&#xff1a;操纵和管理数据库的大型软件 DataBase Management System (DBMS) ​ *SQL&#xff1a;操作关系型数据库的编程语言&#…

【GNSS】GNSS开源相关代码汇总

仅作为笔者的学习笔记使用 参考&#xff1a;GNSS算法相关开源代码&#xff08;含多传感器融合相关项目&#xff09; - 知乎 (zhihu.com)

C语言本身不难,难得是应用场景很多

你学了C语言多半是要做项目的&#xff0c;这个过程中C语言是远远不够的&#xff0c;你把这部分难度加到C语言上&#xff0c;自然就难了在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「C语言的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区…

浅谈防火墙,IPS,APT威胁检测的互补性

在学习网络安全产品时发现很多产品的目的与功能大同小异都是防范非法流量或威胁&#xff0c;但是既然有产品的差异就有作用的目的的差异&#xff0c;下面浅谈一下三个网络安全产品的差异化与互补点 防火墙 传统防火墙主要是工作在二到四层&#xff0c;不会对报文的载荷进行检…

大家期待的「红米汽车」,可能真的要来了

不知道从啥时候开始&#xff0c;小米逐渐摘掉了那顶让人越看越顺眼的「亲民帽子」。 从最开始 1999 到后来 3999 甚至是 4999、5999&#xff0c;雷军口中的高端梦正在一步步实现。 前段时间小米首款汽车 SU7 上市&#xff0c;21.59-29.99 万元定价再次引来大批网友直呼&#…

串联滞后校正及matlab实现

syms b_1 Z[]; P[0,-10,-5]; K1500; G_0zpk(Z,P,K); %G_0为校正前系统开环传递函数 [num,den]tfdata(G_0); %求解b&#xff0c;T [Gm,Pm,wg_0,wc_0]margin(G_0); %Pm为校正前的幅值裕度, gamma60; %确定校正后的相角裕度 Phi_c-6; %校正后的截止频率下Gc(s)的相角&#xff0c;一…

C++ 散列表(hash table)

目录 一&#xff0c;哈希表 1&#xff0c;哈希表概述 2&#xff0c;哈希函数 3&#xff0c;碰撞冲突 二&#xff0c;代码实现 1&#xff0c;哈希函数与素数函数 2&#xff0c;哈希节点与哈希表数据结构 3&#xff0c;构造、析构以及一些简单的功能 4&#xff0c;清空&…

如何使用渐变块创建自定义聊天机器人

如何使用渐变块创建自定义聊天机器人 文章目录 如何使用渐变块创建自定义聊天机器人一、介绍二、参考示例1、一个简单的聊天机器人演示2、将流式传输添加到您的聊天机器人3、喜欢/不喜欢聊天消息4、添加 Markdown、图像、音频或视频 一、介绍 **重要提示&#xff1a;**如果您刚…

《你想活出怎样的人生》上映,AOC带你打开宫崎骏的动画世界大门!

摘要&#xff1a;宫崎骏式美学&#xff0c;每一帧都是治愈&#xff01; 近日&#xff0c;宫崎骏新作《你想活出怎样的人生》正式公映。苍鹭与少年的冒险、奇幻瑰丽的场景、爱与成长的主题&#xff0c;让观众们收获到满满的爱与感动。宫崎骏总能以细腻的画面、温柔的音乐&#…

LeetCode - 面试题 08.06. 汉诺塔问题

目录 题目链接 解题思路 解题代码 题目链接 LeetCode - 面试题 08.06. 汉诺塔问题 解题思路 假设 n 1,只有一个盘子&#xff0c;很简单&#xff0c;直接把它从 A 中拿出来&#xff0c;移到 C 上&#xff1b; 如果 n 2 呢&#xff1f;这时候我们就要借助 B 了&#xff0c;因…

select实现echo服务器的并发

select实现echo服务器的并发 代码实现 #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> #include <arpa/inet.h> #include <sys/select.h> #include <sys/time.h…

14. Spring AOP(二)实现原理

源码位置&#xff1a;spring_aop 上一篇文章中我们主要学习了AOP的思想和Spring AOP使用&#xff0c;本文讲的是Spring是如何实现AOP的&#xff0c;Spring AOP是基于动态代理来实现AOP的&#xff0c;在将动态代理之前先来了解一下什么是代理模式。 1. 代理模式 在现实中就有许…

【算法刷题 | 回溯思想 07】4.18(全排列、全排列 ||)

文章目录 11.全排列11.1题目11.2解法&#xff1a;回溯11.2.1回溯思路&#xff08;1&#xff09;函数返回值以及参数&#xff08;2&#xff09;函数返回值&#xff08;3&#xff09;遍历过程 11.2.2代码实现 12.全排列 ||12.1题目12.2解法&#xff1a;回溯12.2.1回溯思路12.2.3代…