目录
功能:在 str1 中查找 str2,若找到返回子串起始地址,否则返回 NULL。
前言
在编程时,我们经常要处理字符和字符串,为了⽅便操作字符和字符串,C语⾔标准库中提供了 ⼀系列库函数,接下来我们就学习⼀下这些函数。
本文章将讲解内容如下:
1. 字符分类函数
2. 字符转换函数
3. strlen的使用和模拟实现
4. strcpy的使用和模拟实现
5. strcat的使用和模拟实现
6. strcmp的使用和模拟实现
7. strncpy函数的使用
8. strncat函数的使用
9. strncmp函数的使用
10. strstr的使用和模拟实现
11. strtok函数的使用
12. strerror函数的使用
本篇文章也会讲解C语⾔内存函数知识的相关内容:
本篇文章涉及的C语⾔内存函数有:
1.memcpy函数
2.memmove函数
3.memset函数
4.memcmp函数
一、字符分类函数
C语⾔中有⼀系列的函数是专⻔做字符分类的,也就是⼀个字符是属于什么类型的字符的。 这些函数的使⽤都需要包含⼀个头⽂件是 ctype.h。
#include<ctype.h>
1.常用的字符分类函数及其功能:
以下是常用的字符分类函数及其功能:
-
判断字母和数字
int isalnum(int c):检查字符是否为字母(a-z, A-Z)或数字(0-9)int isalpha(int c):检查字符是否为字母(a-z, A-Z)int isdigit(int c):检查字符是否为数字(0-9)
-
判断大小写字母
int islower(int c):检查字符是否为小写字母(a-z)int isupper(int c):检查字符是否为大写字母(A-Z)
-
判断空白字符
int isspace(int c):检查字符是否为空白字符(空格、制表符\t、换行符\n等)
-
判断其他属性
int iscntrl(int c):检查字符是否为控制字符(如\n、\t等)int isgraph(int c):检查字符是否为可打印且非空格字符int isprint(int c):检查字符是否为可打印字符(包括空格)int ispunct(int c):检查字符是否为标点符号int isxdigit(int c):检查字符是否为十六进制数字(0-9, a-f, A-F)
2.这些函数的返回值规则:
如果条件满足,即函数对应的要求成立,返回非0值,不符合,返回0.
3.使用示例:
这些函数的使⽤⽅法⾮常类似:
int islower ( int c );
通过此函数,判断参数c是否为小写字母,如果条件满足,即函数对应的要求成立,返回非0值,不符合,返回0.
习题例子:
写⼀个代码,将字符串中的⼩写字⺟转⼤写,其他字符不变。
#include<stdio.h>
#include<ctype.h>
int main()
{
char p[]="ABCdef";
int i=0;
char c;
while(p[i]!='\0')
{
c=p[i];
if(islower(p[i]))
{
c-=32;
p[i]=c;
}
i++;
}
printf("%s",p);
}

如果记不住这些函数也没太大影响,如上方的islower,我们可以用 if(p[i]>='a'&&p[i]<='z') 来代替,同理,其他函数均可这样做。
二、字符转换函数
1.C语⾔提供了2个字符转换函数:
1. int tolower (int c)
2. int toupper(int c)
int tolower (int c)函数的作用为将大写字母转化成小写字母;
同理 int toupper(int c)函数的作用为将小写字母转化成大写字母;
上方习题中c-=32;我们可以用toupper(c)来实现;当然我们如果记不住字符转换函数的话,就记一下每个大写字母与其对应的小写字母之间差32个ASCLL值即可。
三、strlen函数
1.介绍
size_t strlen ( const char * str );
统计参数指向的字符串的⻓度。统计的是字符串中 '\0' 之前的字符的个数,如果该字符串没有‘\0’,该结果为随机值:
int main()
{
char p[]="ABCdef";
int i,j;
char q[]={'a','b'};
i=strlen(p);
j=strlen(q);
printf("%d %d",i,j);
}

我们由此可知:p达到了预期的目标,但q没有。
2.模拟实现:
该函数可有多钟实现方式:
指针法:
int mystrlen(char *p)
{
char *q=p;
while(*q)
{ q++;
}
return q-p;
}
int main()
{
char p[]="ABCdef";
int i,j;
i=mystrlen(p);
printf("%d",i);
}
统计个数求法:
int mystrlen(char *p)
{
int i=0;
while(*p)
{
p++;
i++;
}
return i;
}
int main()
{
char p[]="ABCdef";
int i,j;
i=mystrlen(p);
printf("%d",i);
}
递归求法:
int mystrlen(const char * str)
{
assert(str);
if(*str == '\0')
return 0;
else return 1 + mystrlen(str+1);
}
int main()
{
char p[]="ABCdef";
int i,j;
i=mystrlen(p);
printf("%d",i);
}
结果也一样正确。
四、strcpy函数
1.介绍
char* strcpy(char * destination, const char * source );
功能:字符串拷⻉,拷⻉到源头字符串中的 \0 为⽌,即将代码中source的字符全拷贝至 destination中。
destination :指针,指向⽬的地空间(目的字符串)
source :指针,指向源头数据(源头字符串)
返回值:strcpy 函数返回的⽬标空间的起始地址
int main()
{
char arr1[10] = {0};
char arr2[] = "hello";
strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}

我们由此可知:该函数将arr2的内容拷贝到了arr1中。
注意:
1.源字符串必须以 '\0' 结束。
2.该函数会将源字符串中的 '\0' 拷⻉到⽬标空间。
3.⽬标空间必须⾜够⼤,以确保能存放源字符串。
4.⽬标空间必须可修改,常量字符串进行拷贝是不行的。
2.模拟实现:
指针法:
char* mystrcpy(char *a1, char*a2)
{ char *ret=a1;
while(*a2)
{
*a1=*a2;
a1++,a2++;
}
*a1 = '\0';
return ret;
}
int main()
{
char arr1[10] = {0};
char arr2[] = "hello";
mystrcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}

运行结果一样的。
五、strcat函数
1.介绍
char * strcat ( char * destination, const char * source );
功能:字符串追加,将源头字符串中的全部字符,即将代码中source的字符全追加至 destination结尾。
destination :指针,指向⽬的地空间
source :指针,指向源头数据
返回值strcat 函数返回的⽬标空间的起始地址
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}

我们由此可知:该函数将arr2的内容追加到了arr1中。
注意:
1.源字符串必须以 '\0' 结束。
2.⽬标字符串中也得有 \0 ,否则没办法知道追加从哪⾥开始。
3.⽬标空间必须⾜够⼤,以确保能存放源字符串。
4.⽬标空间必须可修改,常量字符串进行拷贝是不行的。
这几条要求与strcpy要求类似。
2.模拟实现:
指针法:
char *mystrcat(char *a1,char *a2)
{
char *p=a1;
while(*a1)
{
a1++;
}
while(*a2)
{
*a1=*a2;
a1++;
a2++;
}
*a1='\0';
return p;
}
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
mystrcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}

运行结果一样的。
六、strcmp函数
1.介绍
int strcmp ( const char * str1, const char * str2 );
功能:用来比较str1 和str2 指向的字符串,从两个字符串的第⼀个字符开始⽐较,如果两个字符的ASCII码值相等,就比较下⼀个字符。直到遇到不相等的两个字符,或者字符串结束。
str1 :指针,指向要⽐较的第⼀个字符串。
str2 :指针,指向要⽐较的第⼆个字符串。
返回值:strcmp 函数返回的两个字符串比较的值。
返回值规定:
1.第⼀个字符串⼤于第⼆个字符串,则返回⼤于0的数字。
2.第⼀个字符串等于第⼆个字符串,则返回0。
3.第⼀个字符串⼩于第⼆个字符串,则返回⼩于0的数字。
代码示例:
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abq";
int ret = strcmp(arr1, arr2);
printf("%d\n", ret);
if(ret > 0)
printf("arr1 > arr2\n");
else if(ret == 0)
printf("arr1 == arr2\n");
else
printf("arr1 < arr2\n");
return 0;
}

对此代码解释:
strcmp比较大小为逐一比较,如果相等,则转向下一个字符。

2.strcmp函数的模拟实现:
指针法:
int mystrcmp(char *a,char *b)
{
while(*a==*b)
{
if(*a=='\0')
{
return 0;
}
a++;
b++;
}
return *a-*b;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abq";
int ret = mystrcmp(arr1, arr2);
printf("%d\n", ret);
if(ret > 0)
printf("arr1 > arr2\n");
else if(ret == 0)
printf("arr1 == arr2\n");
else
printf("arr1 < arr2\n");
return 0;
}
对该方法介绍一下:
通过循环比较字符直到不同或遇到结束符 '\0',返回字符差值,这样的方式即可得到结果。
七、strncpy函数
1.介绍
char * strncpy ( char * destination, const char * source, size_t num );
strncpy函数strcpy函数功能相同,字符串拷⻉,即将代码中source的字符拷贝至 destination中,但strncpy函数多出了一个参数,新增的参数含义为最多拷贝num个字符。
即:
destination :指针,指向⽬的地空间(目的字符串)。
source :指针,指向源头数据(源头字符串)。
num :从source指向的字符串中最多拷⻉的字符个数。
返回值:strcpy 函数返回的⽬标空间的起始地址。
使用例:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = { 0 };
char arr2[] = "abcdefghi";
char* str = strncpy(arr1, arr2, 5);
printf("%s\n", arr1);
printf("%s\n", str);
return 0;
}

strncpy函数strcpy函数功能相同,但也有区别的地方:
1.strcpy 函数拷⻉到\0 为⽌,如果⽬标空间不够的话,容易出现越界⾏为。
2.strncpy 函数指定了拷⻉的⻓度,源字符串不⼀定要有'\0', 同时在设计参数的时候,就会多⼀层思考:⽬标空间的⼤⼩是否够⽤。注意:strncpy
- 结束条件:复制
n个字符 或 遇到source 的'\0'时停止(以先到者为准)。- 自动添加
'\0':否!仅当source 长度 <n时,剩余部分用'\0'填充至n个字符;若 source 长度 ≥n,则不添加'\0'。注意strncpy手动补'\0'的情况。
2.模拟实现:
可用指针法:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
char* mystrncpy(char* a1, char* a2,int n)
{
char* ret = a1;
while ( n-- &&*a2)
{
*a1 = *a2;
a1++, a2++;
}
while (n > 0)
{
a1++;
*a1 = '\0';
n--;
}
return ret;
}
int main()
{
char arr1[20] = { 0 };
char arr2[] = "abcdefghi";
char* str = mystrncpy(arr1, arr2, 5);
printf("%s\n", arr1);
printf("%s\n", str);
return 0;
}

运行结果一样的。
八、strncat函数
1.介绍
char * strncat ( char * destination, const char * source, size_t num );
strncat函数strcat函数功能相同,功能:字符串追加,将源头字符串中的字符,即将代码中source的字符追加至 destination结尾,但最多追加num个字符。
destination :指针,指向⽬的地空间
source :指针,指向源头数据
num :从source指向的字符串中最多追加的字符个数。
返回值strncat 函数返回的⽬标空间的起始地址
使用例:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
char* str = strncat(arr1, arr2, 5);
printf("%s\n", arr1);
printf("%s\n", str);
}

strncat函数strcat函数功能相同,但也有区别的地方:
1.strncat 多了⼀个参数,num;
2. strcat函数在追加的时候要将源字符串的所有内容,包含 \0 都追加过去,但是strncat 的num数指定了追加的⻓度,同时函数中源字符串中不⼀定要有‘\0’了,strncat函数更加灵活,也更加安全。
2.模拟实现:
可用指针法:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
char* mystrncat(char* a1, char* a2,int num)
{
char* p = a1;
while (*a1)
{
a1++;
}
while (num--&&*a2)
{
*a1 = *a2;
a1++;
a2++;
}
*a1 = '\0';
return p;
}
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
char* str = mystrncat(arr1, arr2, 5);
printf("%s\n", arr1);
printf("%s\n", str);
}

运行结果一样的。
九、strncmp函数
1.介绍
int strncmp ( const char * str1, const char * str2, size_t num );
strncmp函数strcmp函数功能相同,功能:用来比较str1 和str2 指向的字符串,从两个字符串的第⼀个字符开始⽐较,如果两个字符的ASCII码值相等,就比较下⼀个字符。直到遇到不相等的两个字符,或者字符串结束,但比较的字符对数要在num个字符以内。
str1 :指针,指向要⽐较的第⼀个字符串。
str2 :指针,指向要⽐较的第⼆个字符串。
num :最多⽐较的字符个数。
返回值:strncmp 函数返回的两个字符串比较的值。
返回值规定:
1.第⼀个字符串⼤于第⼆个字符串,则返回⼤于0的数字。
2.第⼀个字符串等于第⼆个字符串,则返回0。
3.第⼀个字符串⼩于第⼆个字符串,则返回⼩于0的数字。
4.如果到了num对且这num对字符均相等,则返回0,按相等来看。
使用例:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcqw";
int ret1 = strncmp(arr1, arr2, 3);
printf("%d\n", ret1);
int ret2 = strncmp(arr1, arr2, 4);
printf("%d\n", ret2);
return 0;
}

strncmp函数strcpy函数功能相同,但也有区别的地方:
关键区别
1.比较长度限制:strcmp:无长度限制,会一直比较到字符串结束(存在越界风险,若字符串未正确以'\0'结尾)。
strncmp:通过第三个参数n明确限制比较的最大字符数,更安全,可避免因字符串未结束导致的无限比较。
2.返回值逻辑:两者返回值规则相同:
若s1 < s2:返回负数(通常是ASCII差值)
若s1 == s2:返回0
若s1 > s2:返回正数(通常是ASCII差值)
但 strncmp 的“相等”可能仅指前n个字符相等,而非整个字符串相等。
2.模拟实现:
可用指针法:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int mystrncmp(char* a, char* b,int num)
{
while (num&&*a == *b)
{
if (*a == '\0')
{
return 0;
}
a++;
b++;
num--;
}
if (num == 0)
{
return 0;
}
return *a - *b;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcqw";
int ret1 = mystrncmp(arr1, arr2, 3);
printf("%d\n", ret1);
int ret2 = mystrncmp(arr1, arr2, 4);
printf("%d\n", ret2);
return 0;
}

运行结果一样的。
十、strstr函数
1.介绍
char * strstr ( const char * str1, const char * str2);
1.介绍:strstr 函数,功能为查找 str2 指向的字符串在 str1 指向的字符串中第⼀次出现的位置。
2.简⽽⾔之:在⼀个字符串中查找⼦字符串。
3.strstr 的使⽤得包含 #include <string.h>头文件。
4.参数: str1 :指针,指向了被查找的字符串。
str2 :指针,指向了要查找的字符串。
5.返回值:
• 如果str1指向的字符串中存在str2指向的字符串,那么返回第⼀次出现位置的指针。
• 如果str1指向的字符串中不存在str2指向的字符串,那么返回NULL。
使用例:
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <string.h> int main() { char str[] = "This is a simple string"; char* p; p= strstr(str, "simple"); if (p != NULL) printf("%s\n", p); else printf("查找的字符串不存在\n"); return 0; }

2.模拟实现:
该函数的模拟实现较为复杂,需要建立三个指针,进行实现,分别为 1.指向被指向的字符串(被查找的字符串),该字符指针来代指该串、2.一个每次承接要查找的字符串的首地址,3.一个每次承接(1.)的指针的首地址。
可能有些模糊,先看代码:
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <string.h> char* mystrstr(char* str1,char* str2) { char* s1 = str1; char* s2, * s3; while (*s1) { s2 = s1; s3 = str2; while (*s2 && *s3) { if ((*s2) != (*s3)) break; s2++; s3++; } if (*s3 == '\0') return s1; s1++; } return NULL; } int main() { char str[] = "This is a simple string"; char* p; p= mystrstr(str, "simple"); if (p != NULL) printf("%s\n", p); else printf("查找的字符串不存在\n"); return 0; }

函数 mystrstr 解析
功能:在 str1 中查找 str2,若找到返回子串起始地址,否则返回 NULL。
实现逻辑:
-
初始化指针:
s1指向str1的当前比较起始位置(从首字符开始)。s2和s3作为临时指针,分别用于遍历str1的子串和str2本身。
-
外层循环:遍历
str1的每个字符(while (*s1)),作为子串匹配的起点。 -
内层循环:逐个字符比较
s1起始的子串与str2:- 若
*s2 == *s3,则两者都后移一位(s2++、s3++)。 - 若不相等,跳出内层循环,
s1后移一位(尝试下一个起始位置)。
- 若
-
匹配成功条件:当
s3遍历到str2的结束符'\0'(即*s3 == '\0'),说明str2已完全匹配,返回此时的s1(子串起始地址)。 -
未找到情况:若
str1遍历结束仍未匹配,返回NULL。
这是对应实现的讲解,运行结果一样的。
十一、strtok 函数
介绍
char *strtok(char *str, const char *delim);
功能:
1.分割字符串:根据 delim 参数中指定的分隔符,将输⼊字符串 str 拆分成多个⼦字符串。
2.修改原始字符串: strtok 会直接在原始字符串中插⼊ '\0' 终⽌符,替换分隔符的位置,因 此原始字符串会被修改。
参数:
1. str :⾸次调⽤时传⼊待分割的字符串;后续调⽤传⼊NULL ,表⽰继续分割同⼀个字符串。
2. delim :包含所有可能分隔符的字符串(每个字符均视为独⽴的分隔符)。
返回值:
1.成功时返回指向当前⼦字符串的指针。
2.没有更多⼦字符串时返回NULL。
注意点:
使⽤步骤
1.⾸次调⽤:传⼊待分割字符串和分隔符。
2.后续调⽤:传⼊ NULL 和相同的分隔符,继续分割。
3.结束条件:当返回 NULL 时,表示分割完成。
通过使用例,解释一下:
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <string.h> int main() { char arr[] = "192.168.6.111"; const char* sep = "."; const char* str = NULL; char buf[30] = { 0 }; strcpy(buf, arr); for (str = strtok(buf, sep); str != NULL; str = strtok(NULL, sep)) { printf("%s\n", str); } return 0; }

strcpy(buf, arr); 是将arr的值拷贝到buf中,
for (str = strtok(buf, sep); str != NULL; str = strtok(NULL, sep))
这句意为:在buf串中寻找sep的串,声明时可知,sep的串: const char* sep = ".";
- 首次调用:
strtok(buf, sep),传入原始字符串buf和分隔符sep,返回第一个子串地址。- 后续调用:
strtok(NULL, sep),传入NULL表示继续处理上一次的字符串,返回下一个子串地址,直至返回NULL(所有子串已拆分完毕)。
但使用本函数时也有些需要注意的点:
1.破坏性操作: strtok 会直接修改原始字符串,将其中的分隔符替换为 '\0' 。如果需要保留原 字符串,应先拷⻉⼀份原串。
2.连续分隔符:多个连续的分隔符会被视为单个分隔符,不会返回空字符串。
3.空指针处理:如果输⼊的str为NULL且没有前序调⽤,⾏为未定义。
接下来十二及以后是关于内存函数的相关知识:
十二、 memcpy函数
1.介绍
void * memcpy ( void * destination, const void * source, size_t num );
memcpy函数strcpy函数功能相似,不同的是memcpy 是完成内存块拷⻉的,不关注内存中存放的数据是什么,即将函数 memcpy 从source 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置。
注意:source 和 destination 有任何的重叠,复制的结果都是未定义的,更易懂的角度来说:
例:
int arr[5] = {1, 2, 3, 4, 5};
memcpy(arr+1, arr, 3*sizeof(int)); // 错误:目标区域 (arr+1) 与源区域 (arr) 重叠
memcpy不允许同名数组的复制。
destination :指针,指向⽬标空间,将拷⻉的数据存放在这⾥(归宿)。
source :指针,指向源空间,要拷⻉的数据从这⾥来(来源)。
num :从source要拷⻉的数据占据的字节数。
返回值:memcpy 函数返回的⽬标空间的起始地址。
使用例:
#include <stdio.h> #include <string.h> int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[10] = { 0 }; memcpy(arr2, arr1, 20); int i = 0; for (i = 0; i < 10; i++) { printf("%d ", arr2[i]); } return 0; }

例子讲解:
本代码中,是将 arr1 的前20字节数据复制到 arr2 中,然后打印 arr2 的所有元素。
memcpy(arr2, arr1, 20)表示复制 20字节 数据。由于每个int占4字节,因此实际复制20 / 4 = 5个元素。- 从
arr1的起始地址复制5个元素:1, 2, 3, 4, 5。 arr2初始化为全0,因此未被复制的后5个元素保持为0。
所以可知打印时后5个值为0.
2.模拟实现:
#include <stdio.h> #include <string.h> void * mymemcpy ( void * dst, const void * src, int n ) { void *ret=dst; while(n--) { *(char*)dst=*(char*)src; dst=(char*)dst+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 }; mymemcpy(arr2, arr1, 20); int i = 0; for (i = 0; i < 10; i++) { printf("%d ", arr2[i]); } return 0; }

对该实现的讲解:
首先模拟实现要满足原函数的参数,返回值等,所以要满足void * memcpy ( void * destination, const void * source, size_t num );的型状。
因为返回值:memcpy 函数返回的⽬标空间的起始地址。
所以用void *ret=dst;语句来接⽬标空间的起始地址。
memcpy 是完成内存块拷⻉的,不关注内存中存放的数据是什么,拷贝的是num个字节,但不同类对应着不同的字节数,所以存在拷贝不完全的情况,而char类型1个字节数,可以完成1个1个复制拷贝的情况,所以 *(char*)dst=*(char*)src; 拷贝完一个字符,就该向下拷贝,但二者均为void *类,所以应该通过强转之后移动地址,所以会有 dst=(char*)dst+1;
src=(char*)src+1; 语句,在结尾,完成返回值:memcpy 函数返回的⽬标空间的起始地址。 return ret;
运行结果一样的,看实现代码,我们可以发现,memcpy不能实现同名数组的复制,如果我们想实现,则可以靠二、memmove函数。
十三、memmove函数
1.介绍
void * memmove ( void * destination, const void * source, size_t num );
memmove函数也是完成内存块拷⻉的。
他与 memcpy函数功能一样,是完成内存块拷⻉的,不关注内存中存放的数据是什么,即将函数 memcpy 从source 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置。不过mommove可以正确处理目标空间与源空间重叠的情况。
源内存块和⽬标内存块是可以重叠的。
二者的参数相同,意义也一样的:
destination :指针,指向⽬标空间,将拷⻉的数据存放在这⾥(归宿)。
source :指针,指向源空间,要拷⻉的数据从这⾥来(来源)。
num :从source要拷⻉的数据占据的字节数。
返回值:memmove 函数返回的⽬标空间的起始地址。
使用例:
#include <stdio.h> #include <string.h> int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; memmove(arr1 + 2, arr1, 20); int i = 0; for (i = 0; i < 10; i++) { printf("%d ", arr1[i]); } return 0; }
![]()
讲解:
与在memcpy中的使用方法一样,不同的是该例为同名数组的拷贝,即将该数组的前5个值拷贝从(arr1 + 2)即arr[2]开始。
2.模拟实现:
#include <stdio.h> #include <string.h> void * mymemmove ( void * dst, const void * src, int n ) { void *ret=dst; if(dst<src) { while(n--) { *(char*)dst=*(char*)src; dst=(char*)dst+1; src=(char*)src+1; } } else { while(n--) *((char*)dst+n)=*((char*)src+n); } return ret; } int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; mymemmove(arr1 + 2, arr1, 20); int i = 0; for (i = 0; i < 10; i++) { printf("%d ", arr1[i]); } return 0; }
![]()
运行结果一样,接下来对该实现讲解一下:

核心逻辑分析
mymemmove通过判断目标地址(dst)和源地址(src)的位置关系,选择不同复制方向:
- 当
dst < src(目标在源左侧):从低地址到高地址复制(避免覆盖未复制的源数据)。(对应图中的一情况)- 当
dst >= src(目标在源右侧或重叠):从高地址到低地址复制(从n-1反向遍历)。(对应图中的二情况)输入参数
arr1初始值:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]mymemmove(arr1 + 2, arr1, 20):
dst = arr1 + 2(目标起始地址:第3个元素)src = arr1(源起始地址:第1个元素)n = 20(复制20字节,int占4字节 → 共复制 5个元素)关键判断
dst (arr1+2) > src (arr1)→ 进入else分支(从高地址到低地址复制)。在
else中拷贝。
while(n--)
*((char*)dst+n)=*((char*)src+n);最后: return ret;完成返回值: memmove函数返回的⽬标空间的起始地址。
十四、memset函数
1.介绍
void * memset ( void * ptr, int value, size_t num );
1.memset 函数是设置内存块的内容的,将内存中指定⻓度的空间设置为特定的内容。 2.memset 的使⽤需要包含<string.h>。
参数讲解:
1.ptr :指针,指向要设置的内存空间,也就是存放了要设置的内存空间的起始地址。
2.value :要设置的值,函数将会把value值转换成unsigned char的数据进⾏设置的。也就是以字节为单位来设置内存块的。
3.num :要设置的内存⻓度,单位是字节。
4.返回值:返回的是要设置的内存空间的起始地址。
使用例:
#include <stdio.h> #include <string.h> int main () { char str[] = "hello world"; memset (str,'x',6); printf(str); return 0; }

对于该例的解释:
初始字符串:
char str[] = "hello world"定义了一个字符数组,存储字符串"hello world"(共11个字符 + 1个结束符\0,总长度12字节)。
memset操作:
- 作用:将
str起始的 6个字节 全部设置为字符'x'。- 原字符串前6个字符是
"hello "(注意第6个字符是空格),替换后变为"xxxxxx"。剩余字符不变:
memset仅修改前6字节,原字符串从第7字节开始的内容("world\0")保持不变。- 拼接后结果为
"xxxxxxrld\0"(第7个字符是'r',因此输出xxxxxxrld)。所以是这样的结果。
注意:当有⼀块内存空间需要设置内容的时候,就可以使⽤memset函数,值得注意的是memset函数对内存 单元的设置是以字节为单位的。
2.模拟实现:
#include <stdio.h> #include <string.h> void * mymemset ( void * p, int value,int n) { void *ret=p; while(n--) { *(char*)p=value; p=(char*)p+1; } return ret; } int main () { char str[] = "hello world"; mymemset (str,'x',6); printf(str); return 0; }

讲解:
mymemset执行流程:
- 保存原始指针
ret = p,用于最终返回。- 循环6次(
n--从6到0),每次将p指向的字节设为'x',并将p按字节递增((char*)p + 1)。- 原字符串前6字节(
"hello ")被替换为"xxxxxx",后续字符"world\0"保持不变,拼接后结果为"xxxxxxrld\0"。printf输出:打印字符串至结束符\0,结果为xxxxxxrld。
十五、memcmp函数
1.介绍
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
功能:
1.⽐较指定的两块内存块的内容,⽐较从ptr1和ptr2指针指向的位置开始,向后的num个字节 2.memcmp 的使⽤需要包含
ptr1 :指针,指向⼀块待⽐较的内存块
ptr2 :指针,指向另外⼀块待⽐较的内存块
num :指定的⽐较⻓度,单位是字节
使用例:
#include <stdio.h> #include <string.h> int main() { char s1[] = "abcdefg"; char s2[] = "abcdEFG"; int n; n = memcmp(s1, s2, sizeof(s1)); if (n > 0) printf("'%s'>'%s'.\n", s1, s2); else if (n < 0) printf("'%s' <'%s'.\n",s1,s2); else printf("'%s' =='%s' .\n", s1, s2); return 0; }

讲解:
- 输出内容:
'abcdefg' < 'abcdEFG'.- 关键原因:
memcmp按字节逐一比较(区分大小写),在第5个字符处:
s1[4] = 'e'(ASCII值101)s2[4] = 'E'(ASCII值69)
因101 > 69,memcmp返回正数,n>0, 执行: if (n > 0) printf("'%s'>'%s'.\n", s1, s2);
注意:1.memcmp 函数是通过返回值告知⼤⼩关系的。
2.如果要⽐较2块内存单元的数据的⼤⼩,可以使⽤该函数,这个函数的特点就是可以指定⽐较长度。
2.模拟实现:
#include <stdio.h> #include <string.h> int mymemcmp ( const void * p1, const void * p2, int n ) { const char* s1 = (const char*)p1; const char* s2 = (const char*)p2; while (n-- > 0) { if (*s1 != *s2) { return *s1 - *s2; } s1++; s2++; } return 0; } int main() { char s1[] = "abcdefg"; char s2[] = "abcdEFG"; int n; n = mymemcmp(s1, s2, sizeof(s1)); if (n > 0) printf("'%s'>'%s'.\n", s1, s2); else if (n < 0) printf("'%s' <'%s'.\n",s1,s2); else printf("'%s' =='%s' .\n", s1, s2); return 0; }

结果一样,接下来,我将为大家讲解一下:
因为参数为void*类型,如果使用,需要 *(char*)p1 这种的强转,代码中每次比较和移动指针时都进行
(char*)强制转换,可通过临时变量优化,const char* s1 = (const char*)p1;
const char* s2 = (const char*)p2;通过char*类型,便于比大小和向下移动。while (n-- > 0)
{
if (*s1 != *s2)
{
return *s1 - *s2;
}
s1++;
s2++;
}
return 0;
- 循环条件:
n-- > 0表示从第 1 个字节开始,逐个比较n个字节(每轮循环后n减 1,直到n=0退出)。- 逻辑:
- 每次循环读取
s1和s2指向的当前字节(*s1/*s2)。- 若字节不同,直接返回两字节的 ASCII 码差值(例如
'e' - 'E' = 101 - 69 = 32,结果为正数)。- 若字节相同,指针后移(
s1++/s2++),继续比较下一个字节。- 若循环结束后所有
n字节均相同(未触发return *s1 - *s2),则显式返回 0。
总结
以上就是今天要讲的内容,
本文章讲解内容如下:
1. 字符分类函数
2. 字符转换函数
3. strlen的使用和模拟实现
4. strcpy的使用和模拟实现
5. strcat的使用和模拟实现
6. strcmp的使用和模拟实现
7. strncpy函数的使用
8. strncat函数的使用
9. strncmp函数的使用
10. strstr的使用和模拟实现
以及C语⾔内存函数知识的相关内容:
本篇文章涉及的C语⾔内存函数有:
1.memcpy函数
2.memmove函数
3.memset函数
4.memcmp函数
知识的相关内容,为篇文章知识的内容,希望大家能喜欢我的文章,谢谢各位。






