一万字带你详解C语言字符函数、字符串函数、内存函数

article/2025/11/10 7:59:12

文章目录

  • 🍳 前言
  • 一、求字符串长度
    • 💦 strlen
  • 二、长度不受限制的字符串函数
    • 💦 strcpy
    • 💦 strcat
    • 💦 strcmp
  • 三、长度受限制的字符串函数
    • 💦 strncpy
    • 💦 strncat
    • 💦 strncmp
  • 四、字符串查找
    • 💦 strstr
    • 💦 strtok
  • 五、错误信息报告
    • 💦 strerror
    • 💦 perror
  • 六、字符操作函数
    • 1、字符分类函数
      • 💦 isdigit
      • 💦 islower
    • 2、字符转换函数
      • 💦 tolower
  • 七、内存操作函数
    • 💦 memcpy
    • 💦 memmove
    • 💦 memset
    • 💦 memcmp
  • 八、函数的模拟实现
    • 💦 strlen
      • 1、计数器的版本(需要临时变量)
      • 2、递归版本(不使用临时变量)
      • 3、指针版本(指针-指针)
    • 💦 strcpy
    • 💦 strcat
    • 💦 strstr
    • 💦 strcmp
    • 💦 memcpy
    • 💦 memmove

🍳 前言

C语言对于字符和字符串的处理是很频繁的,但是C语言本身没有字符串类型,字符串通常放在常量字符串或者字符数组中。

一、求字符串长度

💦 strlen

🔗 函数原型和头:
在这里插入图片描述
🔗 函数的返回值:

在这里插入图片描述

🔗 功能:
在这里插入图片描述


#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abc";char arr2[] = { 'a', 'b', 'c' };printf("arr1的长度是:%d\n", strlen(arr1));printf("arr2的长度是:%d\n", strlen(arr2));return 0;
} 

输出结果:
在这里插入图片描述
📝 分析结果:
  ▶ “abc” 这种类型的字符串有4个元素 - > ‘a’ ‘b’ ‘c’ ‘\0’
  这里说明了strlen这个函数在求字符串长度时计算的是 ‘\0’ 之前的字符,且以 ‘\0’ 为字符串的结束标志
  ▶ { ‘a’, ‘b’, ‘c’ } 这种类型的字符串只有3个元素 -> ‘a’, ‘b’, ‘c’
  这里的结果是一个随机数,因为字符串里并没有 ‘\0’ 作为结束标志,所以它会继续往下数


❓ 观察以下代码,输出的结果是什么

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

输出结果:
在这里插入图片描述
📝 分析结果:

在这里插入图片描述
strlen函数的返回值类型无符号的


💨 总结:
▶ 字符串以 ‘\0’ 作为结束标志,strlen函数的返回值是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )
▶ 参数指向的字符必须要以 ‘\0’ 结束
▶ 注意函数的返回值为size_t,是无符号的(易错)

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

💦 strcpy

🔗 函数原型和头:
在这里插入图片描述
🔗 函数的返回值:
在这里插入图片描述

🔗 功能:
在这里插入图片描述


#include<stdio.h>
int main()
{char arr[20] = { 0 };strcpy(arr, "hello");printf("%s\n", arr);return 0;
}

⭕ 输出结果:hello
📝 分析结果:strcpy会把源字符串中的内容拷贝到目标空间,并返回目标空间的起始位置


❓ strcpy在拷贝的过程中是以 ‘\0’ 为结束标志吗

📐 验证如下

#include<stdio.h>
#include<string.h>
int main()
{char arr1[20] = "##########";char arr2[] = "ab\0cdef";strcpy(arr1, arr2);return 0;
}

⭕ 结果:
在这里插入图片描述

📝 分析:
strcpy在拷贝字符串时是以’\0’为标志的,且会将’\0’也拷贝


❓ 当原字符串要拷贝的空间比目标字符串的空间大时

#include<stdio.h>
#include<string.h>
int main()
{char arr[5] = { 0 };char* p = "hello world";strcpy(arr, p);printf("%s\n", arr);return 0;
}

⭕ 结果:
在这里插入图片描述


❓ 目标空间为常量字符串时

#include<stdio.h>
#include<string.h>
int main()
{char* str = "xxxxxxxxxx";char* p = "hello world";strcpy(str, p);printf("%s\n", str);return 0;
}

⭕ 结果:
在这里插入图片描述


💨 总结:
▶ 源字符串必须以 ‘\0’ 结束
▶ strcpy在拷贝的过程中是以 ‘\0’ 为结束标志,且会将 ‘\0’ 也拷贝
▶ 目标空间必须足够大,以确保能存放源字符串
▶ 目标空间必须可变

💦 strcat

🔗 函数原型和头:
在这里插入图片描述
🔗 函数的返回值:
在这里插入图片描述

🔗 功能:
在这里插入图片描述


#include<stdio.h>
int main()
{char arr1[20] = "hello";char arr2[] = "bit";strcat(arr1, arr2);printf("%s\n", arr1);return 0;
}

⭕ 输出结果:
在这里插入图片描述
📝 分析结果:
从结果可以知道源字符串在追加时会找到目标字符串最后的 ‘\0’ 并将它给覆盖,然后开始追加


❓ 在源字符串追加给目标字符串时,源字符串的 ‘\0’ 会不会也追加

📐 验证如下

#include<stdio.h>
int main()
{char arr1[20] = "hello\0xxxxxxxxxx";char arr2[] = "bit";strcat(arr1, arr2);printf("%s\n", arr1);return 0;
}

结果:
在这里插入图片描述


❓ strcat能不能自己给自己追加

#include<stdio.h>
int main()
{char arr[20] = "abcd";strcat(arr, arr);//?printf("%s\n", arr);return 0;
}

结果:

在这里插入图片描述
📝 分析:
在这里插入图片描述


💨 总结:
▶ 源字符串必须以 ‘\0’ 结束
▶ 目标空间必须足够大,以足以容纳字符串
▶ 目标字符串必须可修改
▶ 不能自己追加自己

💦 strcmp

🔗 函数原型和头:
在这里插入图片描述
🔗 函数的返回值:
在这里插入图片描述

🔗 功能:
在这里插入图片描述


❌ 字符串的比较

#include<stdio.h>
#include<string.h>
int main()
{char* p1 = "def";char* p2 = "abc";if(p1 > p2)printf(">\n");else printf("<\n");return 0;
}

📝 分析:
在这里插入图片描述


✔ 字符串的比较

#include<stdio.h>
#include<string.h>
int main()
{char* p1 = "def";char* p2 = "abcdef";printf("%d\n", strcmp(p1, p2));return 0;
}

📝 分析:
在这里插入图片描述

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

💦 strncpy

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述
🔗 功能:
在这里插入图片描述


#include<stdio.h>
#include<string.h>
int main()
{char arr1[20] = "abcdef";char arr2[] = "qwer";strncpy(arr1, arr2, 2);printf("%s\n", arr1);return 0;
}

结果:
在这里插入图片描述


❓ 当指定的个数num大于源字符串时

#include<stdio.h>
#include<string.h>
int main()
{char arr1[20] = "abcdef";char arr2[] = "qwe";strncpy(arr1, arr2, 6);printf("%s\n", arr1);return 0;
}

结果:
在这里插入图片描述


💨 总结:
▶ 拷贝num个字符从源字符串到目标空间
▶ 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后面追加0,直到num个

💦 strncat

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述
🔗 功能:
在这里插入图片描述


#include<stdio.h.>
#include<string.h>
int main()
{char arr1[20] = "hello ";char arr2[] = "world";strncat(arr1, arr2, 3);printf("%s\n", arr1);return 0;
}

结果:
在这里插入图片描述


❓ strncat能否自己给自己追加

#include<stdio.h.>
#include<string.h>
int main()
{char arr1[20] = "hello";strncat(arr1, arr1, 6);printf("%s\n", arr1);return 0;
}

结果和分析:
在这里插入图片描述

💦 strncmp

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述
🔗 功能:
在这里插入图片描述


#include<stdio.h>
#include<string.h>
int main()
{char* p1 = "abcdef";char* p2 = "abcpdf";int ret = strncmp(p1, p2, 4);printf("%d\n", ret);return 0;
}

结果:
在这里插入图片描述

四、字符串查找

💦 strstr

🔗 函数原型和头:
在这里插入图片描述
🔗 函数的返回值:
在这里插入图片描述
🔗 功能:
在这里插入图片描述


#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abcdefabcdef";char arr2[] = "bcd";char* ret = strstr(arr1, arr2);if(ret == NULL)printf("没找到\n");elseprintf("找到了:%s\n", ret);return 0;
}

结果:
在这里插入图片描述
📝 分析:
strstr在目标字符串中查找子串,如果找到了就返回目标字符串中首次出现的源字符串的地址,否则返回一个空指针

💦 strtok

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图 片描述

🔗 功能:
在这里插入图片描述

🔗 函数详解:
▶ strDelimit的参数是一个字符串,定义了用作分隔符的字符集合
▶ strToken指定一个字符串,它包含了0个或者多个由strDelimit串中一个或者多个分隔符分割的标记
▶ strtok函数找到strToken中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改)
▶ strtok函数的第一个参数不为NULL,函数将找到strToken中的第一个标记,strtok函数将保存它在字符串中的位置
▶ strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记
▶ 如果字符串中不存在更多的标记,则返回NULL指针
▶ strtok函数被调用一次,只会切割一次,所以想要切割多次,就需要多次调用


#include<stdio.h>
#include<string.h>
int main()
{char arr[] = "wanghong@deboke.csdn";char* p = "@.";char temp[30] = { 0 };//拷贝一份arr于temp,让temp去切割strcpy(temp, arr);char* ret = NULL;ret = strtok(temp, p);printf("%s\n", ret);ret = strtok(NULL, p);printf("%s\n", ret);ret = strtok(NULL, p);printf("%s\n", ret);return 0;
}

结果与分析:
在这里插入图片描述


但是我们通常会将多次分割的这个操作使用循环来实现,非常的妙啊 !!!

#include<stdio.h>
#include<string.h>
int main()
{char arr[] = "wanghong@deboke.csdn";char* p = "@.";char temp[30] = { 0 };strcpy(temp, arr);char* ret = NULL;for(ret = strtok(temp, p); ret != NULL; ret = strtok(NULL, p)){printf("%s\n", ret);}return 0;
}

五、错误信息报告

💦 strerror

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:在这里插入图片描述

🔗 功能:
在这里插入图片描述

🔗 函数详解:
在调用库函数失败时,都会设置错误码
C语言中有一个全局的错误码 -> int errno ,只要调用库函数发生了错误,就会把错误码放到errno里去
这里strerror就会把错误码翻译成对应的错误信息,然后再把错误信息以字符串首地址返回回来
通常strerror都会和errno一起使用
使用errno需要头文件errno.h


#include<stdio.h>
#include<string.h>
int main()
{printf("%s\n", strerror(0));printf("%s\n", strerror(1));printf("%s\n", strerror(2));printf("%s\n", strerror(3));printf("%s\n", strerror(4));printf("%s\n", strerror(5));return 0;
}

结果与分析:

在这里插入图片描述


❓ 具体是怎么用的
注:以下所使用的对文件操作的一些函数会在后面进行了解

#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{//以读的形式打开test.txt文件,这个文件如果不存在就会打开失败,然后返回一个空指针FILE* pf = fopen("test.txt", "r");//失败就输出失败的原因,也就题错误信息if(pf == NULL){printf("%s\n", strerror(errno));								return 1;}//...fclose(pf);//关闭文件pf == NULL;return 0;
}

结果:
在这里插入图片描述


💦 perror

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:在这里插入图片描述

🔗 功能:
在这里插入图片描述


#include<stdio.h>
#include<stdio.h>
int main()
{FILE* pf = fopen("test.txt", "r");if(pf == NULL){perror("fopen");		return 1;}//...fclose(pf);pf == NULL;return 0;
}

结果与分析:
在这里插入图片描述

六、字符操作函数

1、字符分类函数

在这里插入图片描述
🧿 注:简单介绍几个,其余的函数可以照猫画虎


💦 isdigit

🔗 函数原型和头:
在这里插入图片描述
🔗 函数的返回值:
在这里插入图片描述
🔗 功能:
在这里插入图片描述

#include<stdio.h>
#include<ctype.h>
int main()
{char ch = '@';//如果是数字字符返回非0的值,否则返回0int ret = isdigit(ch);printf("%d\n", ret);return 0;
}

💦 islower

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述

🔗 功能:
在这里插入图片描述

#include<stdio.h>
#include<ctype.h>
int main()
{char ch = 'A';//如果是小写字母返回非0的值,否则返回0int ret = islower(ch);printf("%d\n", ret);return 0;
}

2、字符转换函数

函数功能
tolower大写转小写
toupper小写转大写

💦 tolower

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述

🔗 功能:
在这里插入图片描述

#include<stdio.h>
#include<ctype.h>
int main()
{char arr[20] = { 0 };scanf("%s", arr);int i = 0;while(arr[i] != '\0'){printf("%c ", tolower(arr[i]));i++;}return 0;
}

结果:
在这里插入图片描述

七、内存操作函数

💦 memcpy

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述

🔗 功能:
在这里插入图片描述


#include<stdio.h>
#include<string.h>
int main()
{int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[20] = { 0 };memcpy(arr2, arr1, 20);//注意第3个参数的单位是字节return 0;
}

结果:
在这里插入图片描述

❓ 我们注意到memcpy的前2个参数是void*类型的,那是不是说它可以拷贝不同的数据

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abcdef";char arr2[20] = { 0 };memcpy(arr2, arr1, 6);printf("%s\n", arr2);return 0;
}

结果:
在这里插入图片描述


❓ 对于strcpy函数在拷贝字符串时,如果遇到’\0’它会停止拷贝,那么思考memcpy在拷贝字符串时遇到’\0’是否也会停止

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abc\0def";char arr2[20] = { 0 };memcpy(arr2, arr1, 6);printf("%s\n", arr2);return 0;
}

结果:
在这里插入图片描述


这里有一个场景 ❓
在这里插入图片描述

#include<stdio.h>
#include<string.h>
int main()
{int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };memcpy(arr1 + 2, arr1, 20);return 0;
}

结果与分析:
在这里插入图片描述

💦 memmove

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述
🔗 功能:
在这里插入图片描述


#include<stdio.h>
#include<string.h>
int main()
{int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };memmove(arr1 + 2, arr1, 20);return 0;
}

结果与分析:
在这里插入图片描述

💦 memset

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述

🔗 功能:
在这里插入图片描述


#include<stdio.h>
#include<string.h>
int main()
{int arr[10] = { 0 };memset(arr, 1, 20);return 0;
}

结果与分析:
在这里插入图片描述


❓ 使用memset设置一个10个元素的数组的元素为1

#include<stdio.h>
#include<string.h>
int main()
{int arr[10] = { 0 };int i = 0;for (i = 0; i < 10; i++){memset(&arr[i], 1, 1);}return 0;
}

结果:
在这里插入图片描述

💦 memcmp

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述

🔗 功能:
在这里插入图片描述


#include<stdio.h>
#include<string.h>
int main()
{float arr1[] = { 1.0, 2.0, 3.0, 4.0 };float arr2[] = { 1.0, 3.0 };int ret = memcmp(arr1, arr2, 8);//注意第3个参数的单位是字节printf("%d\n", ret);return 0;
}

八、函数的模拟实现

💦 strlen

1、计数器的版本(需要临时变量)

#include<stdio.h>
int my_strlen(char* str)
{int count = 0;while(*str != '\0'){count++;str++;}return count;
}
int main()
{char arr[] = "abc";int len = my_strlen(arr);printf("%d\n", len);return 0;
}

❗ 缺陷和不足:
❌ 在my_strlen函数里对指针直接进行解引用这是不安全的,因为指针一定得是有效的
❌ my_strlen函数的功能是求字符串的长度,而这个my_strlen函数有权限去改变字符串里的内容,因此这是不安全的
❌ my_strlen这个函数的返回值是int,如果返回的是一个负数,那可能就当场懵逼
💯优化
✔ 使用断言assert,需要引头文件<assert.h>
✔ 使用const对str进行限制
✔ 将函数的返回值int更改为size_t
✔ 对代码整体进行更简洁的优化

#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char* str)
{assert(str != NULL);int count = 0;while (*str++)count++;return count;
}
int main()
{char arr[] = "abc";printf("%d\n", my_strlen(arr));return 0;
}

2、递归版本(不使用临时变量)

#include<stdio.h>
#include<assert.h>
int my_strlen(const char* str)
{assert(str);if(*str)return 1 + my_strlen(str+1);else return 0;
}
int main()
{char arr[] = "abc";printf("%d\n", my_strlen(arr));return 0;
}

3、指针版本(指针-指针)

字符串长度 = '\0’的地址 - 首元素的地址

#include<stdio.h>
#include<assert.h>
int my_strlen(char* first, char* end)
{assert(first);assert(end);return end - first;
}
int main()
{char arr[] = "abc";int left = 0;int right = sizeof(arr)/sizeof(arr[0]) - 1;printf("%d\n", my_strlen(&arr[left], &arr[right]));return 0;
}

💦 strcpy

#include<stdio.h>
#include<assert.h>
void my_strcpy(char* dest, char* src)
{while(*src != 0){*dest = *src;dest++;src++;}src = 0;//将str1的最后一个元素拷贝为0
}
int main()
{char arr1[20] = { 0 };char arr2[] = "hello bit";my_strcpy(arr1, arr2);printf("%s\n", arr1);return 0;
}

❗ 缺陷和不足:
❌ 在my_strcpy函数里对指针直接进行解引用这是不安全的,因为指针一定得是有效的
❌对于my_strcpy这个函数的2个参数,其一是目标字符串,其二是源字符串,目标字符串必须保证可被修改,而源字符串不能被修改
❌ 这里是先把除 ‘\0’ 其它字符先拷贝,再拷贝 ‘\0’
❌ 这个函数的返回值是void,而在库里是char*,返回的是目标字符串的首地址 :
在这里插入图片描述

💯优化
✔ 使用断言assert,需要引头文件<assert.h>
✔ 使用const对str2进行限制
✔ 将 ‘\0’ 和其它字符一起进行拷贝
✔ 将函数的返回值设置为char*

#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{assert(dest);assert(src);char* temp = dest;//备份一份首地址while (*dest++ = *src++){;}return temp;//返回备份的首地址 
}
int main()
{char arr1[20] = { 0 };char arr2[] = "hello bit";printf("%s\n", my_strcpy(arr1, arr2));return 0;
}

💦 strcat

#include<stdio.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{assert(dest && src);char* temp = dest;//备份一份首地址 //找到目标字符串中的'\0'while(*dest){dest++;}while(*dest++ = *src++){;}return temp;//返回首地址 
}
int main()
{char arr1[20] = "hello";char arr2[] = "bit";printf("%s\n", my_strcat(arr1, arr2));return 0;
}

💦 strstr

#include<stdio.h>
#include<assert.h>
char* my_strstr(const char* father, const char* son)
{assert(father && son);const char* f1 = NULL;						const char* s2 = NULL;const char* ret = father; while(*ret){f1 = ret;s2 = son;if(*son == '\0'){return (char*)father;}while(*f1 && *s2 && (*f1 == *s2)){f1++;s2++;	}if(*s2 == '\0'){return (char*)ret;}ret++;}return NULL;
}
int main()
{char arr1[] = "abbbcdef";char arr2[] = "bbc";char* ret = my_strstr(arr1, arr2);if(ret == NULL){printf("没找到\n");}		else{printf("找到了:%s\n", ret);}return 0;
}

分析:
在这里插入图片描述
▶ 考虑到如果查找失败的情况,不一定是找不到子串,所以要从新再匹配直到*father == '\0’就找不到子串
▶ 需要定义2个指针f1(指向father首地址)和s2(指向son首地址)来帮我们往下去匹配
▶ 如果匹配失败指针要回到该回到的地方
对于s2:s2 = son
对于f1:这里就还需要另1个指针ret(指向fther首地址)来记录,每一次匹配失败,都让ret++,然后f1 = ret


🍳拓展:KMP算法 - 字符串查找算法 - 大家可以了解一下

💦 strcmp

#include<stdio.h>
#include<assert.h>
int my_strcmp1(const char* str1, const char* str2)
{assert(str1 && str2);while(1){if(*str1 > *str2)return 1;else if(*str1 < *str2)return -1;else{if(*str1 == *str2 && *str1 == '\0')return 0;else{str1++;str2++;	}}	}
}
int my_strcmp2(const char* str1, const char* str2)
{assert(str1 && str2);while(*str1 == *str2){if(*str1 == '\0')return 0;str1++;str2++;}	if(*str1 > *str2)return 1;elsereturn -1;
}int main()
{char* p1 = "def";char* p2 = "abcdef";//int ret = my_strcmp1(p1, p2);//版本1int ret = my_strcmp2(p1, p2);//版本2if(ret > 0)printf("p1 > p2\n");else if (ret < 0)printf("p1 < p2\n");else printf("p1 = p2\n");return 0;
}

优化冗余:

#include<stdio.h>
#include<assert.h>
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;
}
int main()
{char* p1 = "def";char* p2 = "abcdef";int ret = my_strcmp(p1, p2);if(ret > 0)printf("p1 > p2\n");else if (ret < 0)printf("p1 < p2\n");else printf("p1 = p2\n");return 0;
}

💦 memcpy

#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{assert(dest && src);void* ret = dest;while(num--){/**(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;*/*((char*)dest)++ = *((char*)src)++;}return ret;
}
int main()
{int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[20] = { 0 };my_memcpy(arr2, arr1, 20);
}

❗ 注意:
在这里插入图片描述


my_memcpy能否拷贝重叠的内存空间

#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{assert(dest && src);void* ret = dest;while(num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}return ret;
}
int main()
{int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };my_memcpy(arr1 + 2, arr1, 20);
}

结果:
在这里插入图片描述

💦 memmove

❓ 相信小伙伴们都很好奇memmove是怎么处理内存重叠的情况的
🔎 分析一波
在这里插入图片描述

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

http://chatgpt.dhexx.cn/article/7lgENHSe.shtml

相关文章

一线工程师告诉你嵌入式真实现状与发展前景

个人说明&#xff1a;本人并不是年薪百万的技术大牛&#xff0c;但总算是一名合格的嵌入式工程师&#xff0c;现在某企业担任嵌入式软件工程师开发一职&#xff0c;以下观点可能会带有片面或者分析不全&#xff0c;但却是一名一线企业嵌入式软件开发者真实感受和所得&#xff0…

他又赚了一万美金

有一些学员真的挺能干的&#xff0c;收了一万刀&#xff0c;感到欣慰&#xff0c;毕竟在国外lead这条路&#xff0c;有很多人被骗&#xff0c;也有很多人赚钱。 但是大部分人跟着某一些所谓的大佬&#xff0c;最后自己却不动手操作。 ​ 从一开始怕跟我学习&#xff0c;到最后选…

有人竟然用Python在股市赚到了第一桶金!!!

Python的热度一直高居不下&#xff01;除了数据分析&#xff0c;还有运维、自动化测试、后端开发、机器学习...Python的用武之地真的太多了&#xff01; 而量化更是Python的一种高级应用&#xff01; 什么是量化&#xff1f; 我们利用计算机技术&#xff0c;通过建模分析、优化…

chatgpt赋能python:如何在Python中二次运行同一个命令语句

如何在Python中二次运行同一个命令语句 如果您是一个熟练的Python开发者&#xff0c;一定会遇到必须二次运行同一个命令语句的情况。在本文中&#xff0c;我们将探讨Python中的几种方法来实现这一目标。 方法1&#xff1a;使用Python Shell Python Shell是Python解释器的一个…

数学第一单元计算机思维导图,四年级下册第一单元思维导图数学

250212002102410 这个大数的认识吧……是四年级上册第一单元,主要讲了亿以内、亿以上、计算器、算盘等.......还可以用思维导图来画,更清楚,有什么问题随时来问我 人民教育出版社四年级上册语文第一单元思维导图怎么画 按照阅读和写作两大块来制作。 比如在阅读方面 思维导图…

chatgpt赋能python:Python中如何一行输入多个数据

Python中如何一行输入多个数据 在Python中&#xff0c;我们有时需要一次性输入多个数据&#xff0c;作为程序的输入。常见的情况包括输入一组数字、输入多个字符串等。在本文中&#xff0c;我们将介绍多种方法来实现一行输入多个数据。 方法一&#xff1a;使用input函数 Pyt…

chatgpt赋能python:Python教程:如何创建一个list

Python教程&#xff1a;如何创建一个list Python是一种高级编程语言&#xff0c;用于开发桌面应用程序&#xff0c;网络应用程序以及游戏。List是Python中的一种基本数据类型&#xff0c;它是由一组有序的元素组成的。本篇文章将介绍如何使用Python创建一个list。 什么是list…

chatgpt赋能python:Python字符串:如何定义一个空字符串

Python字符串&#xff1a;如何定义一个空字符串 在Python中&#xff0c;字符串是一种常见的数据类型&#xff0c;通常用于存储文本信息。定义一个空字符串在Python中非常简单&#xff0c;本文将介绍如何定义一个空字符串以及在Python中使用字符串的一些常见操作。 定义一个空…

chatgpt赋能python:Python如何Input一个字典:详细教程

Python 如何 Input 一个字典&#xff1a;详细教程 在 Python 中&#xff0c;字典是一种非常有用的数据结构。它可以存储键值对&#xff0c;让我们能够通过键访问值。Python 中没有固定的语法来创建字典&#xff0c;因此本文将教你如何输入一个字典。 什么是字典&#xff1f; …

chatgpt赋能python:Python如何创建一个文件?

Python如何创建一个文件&#xff1f; 在Python中&#xff0c;我们可以使用open()函数来创建一个新文件。该函数接收两个参数&#xff1a;文件名和文件模式。文件模式可以是只读(‘r’)&#xff0c;只写(‘w’)或者追加(‘a’)。在创建新文件时&#xff0c;我们通常使用’w’模…

chatgpt赋能python:如何使用Python分割大文件-一个简单的教程

如何使用Python分割大文件 - 一个简单的教程 如果你需要处理大文件&#xff0c;可能会遇到内存不足或处理时间过长的问题。幸运的是&#xff0c;Python语言提供了一些工具来解决这个问题&#xff0c;其中一个就是文件分割。在本教程中&#xff0c;我们将学习如何使用Python分割…

chatgpt赋能python:Python循环语句详解:如何循环执行一个语句

Python循环语句详解&#xff1a;如何循环执行一个语句 Python是一种简单易学、优雅高效的编程语言&#xff0c;在很多领域都有广泛应用。其中&#xff0c;循环语句特别重要&#xff0c;可以让我们轻松实现重复执行某个语句的功能。本文将介绍Python的循环语句及其应用场景&…

chatgpt赋能python:Python如何创建一个文件

Python如何创建一个文件 在Python编程中&#xff0c;创建新文件是一项常见的任务。本文将介绍如何在Python中创建新文件&#xff0c;并提供示例代码和使用建议。 介绍 在Python中创建新文件可使用内置的open()函数。该函数可打开现有文件以供读取和写入&#xff0c;或创建新…

chatgpt赋能python:Python实现文件复制到另一个文件夹下的方法

Python实现文件复制到另一个文件夹下的方法 如果你经常需要复制文件并将它们保存到不同的文件夹下&#xff0c;那么使用Python脚本来执行此任务是一个非常好的选择。Python提供了强大的文件操作功能&#xff0c;使得编写脚本来完成文件操作变得相对简单。在本篇文章中&#xf…

chatgpt赋能python:Python中的乘号:一个重要的数学运算符

Python中的乘号&#xff1a;一个重要的数学运算符 在 Python 编程语言中&#xff0c;乘号通常是使用“*”表示的数学运算符。这个运算符非常常用&#xff0c;它可以在各种情况下使用。本文将探讨 Python 中乘号的基本用法&#xff0c;以及更高级的用法。 基本用法 在 Python…

chatgpt赋能python:Python中如何输入一个列表?

Python中如何输入一个列表&#xff1f; 如果你正在使用Python编程语言&#xff0c;那么输入一个列表是很常见的任务。列表是Python中最常用的数据类型之一&#xff0c;它允许我们在一个变量中存储多个值。在这篇文章中&#xff0c;我们将介绍如何使用Python语言输入一个列表&a…

chatgpt赋能python:Python中的e——一个优秀的数学常数

Python中的e——一个优秀的数学常数 在 Python 中&#xff0c;我们可以使用 math 模块来获得数学函数和常数。而其中一个重要的常数就是自然对数的底数 e。 什么是e&#xff1f; e是一个重要的数学常数&#xff0c;它的值约等于2.71828。它是里奥纳多皮萨诺在研究复利时发现…

美国计算机专业排名前50公立大学,US News排名前50的大学里,计算机好的有哪些?(本科)...

堪萨斯州立大学成立1863年&#xff0c;是该州第一所公立大学。2009年Usnews 美国大学综合排名第130。 .... 全美公立大学排名前50位&#xff0c;工程类专业排名前10位。全美第8安全的校园。 .... 克利夫兰州立大学是俄亥俄州最具多样化的公立大学&#xff0c;提供优质的本科课程…

专业排名全美top6|建筑学硕士学历CSC获批顺利赴美

E老师人文社科背景&#xff0c;二本院校任教&#xff0c;硕士毕业&#xff0c;没有英文文章&#xff0c;且申请周期只有一个月。据此我们提出&#xff0c;以赶上CSC申报为前提&#xff0c;尽量申请美国综合或者专业排名靠前的学校。最终我们助E老师获得美国专业排名TOP6的弗吉尼…

2023USNews全美大学排名出炉!哥大暴跌16名,藤校教育遭质疑

视学算法报道 编辑&#xff1a;武穆 桃子 【导读】USNews全美大学2023排名近日公布&#xff0c;其中哥伦比亚大学从第2名跌到第18名。由此&#xff0c;在美国的高校圈里引发了诸多争议。 USNews全美大学2023排名公布了&#xff01; 9月12日&#xff0c;世界最权威排名机构之一…