🤡博客主页:醉竺
🥰本文专栏:《C语言深度解剖》
😻欢迎关注:感谢大家的点赞评论+关注,祝您学有所成!
✨✨💜💛想要学习更多C语言深度解剖点击专栏链接查看💛💜✨✨
目录
0. 前言
1. 函数介绍
1.1 strlen
1.3 strcat
1.4 strcmp
1.5 strncpy
1.6 strncat
1.7 strncmp
1.8 strstr
1.9 strtok
1.10 strerror
1.11 memcpy
1.12 memmove
1.13 memcmp
1.14 memset
2. 库函数的模拟实现
2.1 模拟实现strlen
2.2 模拟实现strcpy
2.3 模拟实现strcat
2.4 模拟实现strstr
2.5 模拟实现strcmp
2.6 模拟实现memcpy
2.7 模拟实现memmove
本章重点
重点介绍处理字符和字符串的库函数的使用和注意事项
字符串长度
- strlen
长度不受限制的字符串函数
- strcpy
- strcat
- strcmp
长度受限制的字符串函数介绍
- strncpy
- strncat
- strncmp
字符串查找
- strstr
- strtok
错误信息报告
- strerror
字符操作
内存操作函数
- memcpy
- memmove
- memset
- memcmp
0. 前言
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在 常量字符串中或者字符数组中。
字符串常量 适用于那些对它不做修改的字符串函数。
1. 函数介绍
1.1 strlen
size_t strlen(const char* str);
- 字符串已'\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。
- 参数指向的字符串必须要以 '\0' 结束。
- 注意函数的返回值为size_t,是无符号的( 易错 )
- 学会strlen函数的模拟实现
1.2 strcpy
1.3 strcat
将源字符串的副本附加到目标字符串。目标字符串中的终止空字符 '\0' 被源字符串的第一个字符覆盖,并返回目标字符串的起始地址。
1.4 strcmp
int strcmp ( const char * str1, const char * str2 );
strcmp 函数比较两个字符串 s1 和 s2,按照字典顺序(即 ASCII 值的大小)进行比较。比较过程从两个字符串的第一个字符开始,直到遇到一个不同的字符或者达到其中一个字符串的末尾。
- 如果 s1 和 s2 相等,返回 0。
- 如果 s1 小于 s2(即 s1 的第一个不匹配字符的 ASCII 值小于 s2 对应字符的 ASCII 值),返回一个小于 0 的值。
- 如果 s1 大于 s2(即 s1 的第一个不匹配字符的 ASCII 值大于 s2 对应字符的 ASCII 值),返回一个大于 0 的值。
1.5 strncpy
它的作用是将源字符串的一部分复制到目标字符串中,并且可以指定复制的最大字符数。
- dest:指向目标字符串的指针,目标字符串必须有足够的空间来保存复制的字符以及终止符 '\0'。
- src:指向源字符串的指针,该字符串的一部分将被复制到目标字符串中。
- n:要复制的最大字符数。如果 src 的长度小于 n,则 dest 中的剩余空间将填充为 '\0';如果 src 的长度大于或等于 n,则只有前 n 个字符会被复制到 dest 中。
使用 strncpy 函数时,需要注意的是:
- 如果 src 的长度小于 n,dest 中剩余的空间将被填充为 '\0',确保目标字符串以终止符结束。
- 如果 src 的长度大于或等于 n,dest 不会自动添加终止符 '\0',这可能导致 dest 不是有效的 C 字符串。
- strncpy 不会检查 dest 的空间是否足够,因此在使用前应该确保 dest 有足够的空间。
1.6 strncat
将源字符串的前num个字符以及一个终止的空字符追加到目标字符串。
char *strncat(char *dest, const char *src, size_t n);
- dest:指向目标字符串的指针,目标字符串必须有足够的空间来保存附加的字符以及终止符 '\0'。
- src:指向源字符串的指针,该字符串的一部分将被附加到目标字符串的末尾。
- n:要附加的最大字符数。如果 src 的长度小于 n,则整个 src 会被附加到 dest;如果 src 的长度大于或等于 n,则只有前 n 个字符会被附加到 dest 中,并且 strncat 会自动在末尾添加终止符 '\0'。
使用 strncat 函数时,需要注意的是:
- 目标字符串 dest 必须有足够的空间来保存附加的字符以及终止符 '\0',否则可能会导致缓冲区溢出。
- 目标字符串 dest 必须以空字符 '\0' 结尾,因为 strncat 函数会从 dest 的空字符开始附加 src。
- strncat 不会检查 dest 的空间是否足够,因此在使用前应该确保 dest 有足够的空间。
1.7 strncmp
int strncmp ( const char * str1, const char * str2, size_t num );
它的作用是比较两个字符串的前 num个字符,根据比较结果返回一个整数值。
- s1:指向第一个字符串的指针。
- s2:指向第二个字符串的指针。
- n:要比较的最大字符数。
strncmp 函数比较两个字符串 str1 和 str2 的前 num 个字符,按照字典顺序(即 ASCII 值的大小)进行比较。比较过程从两个字符串的第一个字符开始,直到遇到一个不同的字符、达到 num 个字符或者达到其中一个字符串的末尾。
strncmp 函数的返回值如下:
- 如果 str1 和 str2 的前 n 个字符相等,返回 0。
- 如果 s1 的前 num 个字符小于 str2 的前 num 个字符(即 str1 的某个字符的 ASCII 值小于 str2 对应字符的 ASCII 值),返回一个小于 0 的值。
- 如果 str1 的前 num 个字符大于 str2 的前 num 个字符(即 str1 的某个字符的 ASCII 值大于 str2 对应字符的 ASCII 值),返回一个大于 0 的值。
1.8 strstr
char * strstr ( const char *str1, const char * str2);
它的作用是在一个字符串 str1 中查找子字符串 str2 的第一次出现。如果找到子字符串,strstr 函数返回指向 str1 中子字符串第一次出现的指针;如果没有找到,返回 NULL。
- str1:指向要搜索的字符串的指针。
- str2:指向要查找的子字符串的指针
请注意,strstr 函数区分大小写,因此在搜索时它会考虑字符的大小写。如果你需要进行不区分大小写的搜索,你可能需要使用自定义函数或者库提供的其他函数。
1.9 strtok
char * strtok ( char * str, const char * sep );
- sep参数是个字符串,定义了用作分隔符的字符集合。
- 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标 记。
- strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。)
- strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串 中的位置。
- strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标 记。
- 如果字符串中不存在更多的标记,则返回 NULL 指针。
1.10 strerror
char * strerror ( int errnum );
- errnum:一个整数,表示错误码。
strerror 函数是 C 语言标准库中的一个函数,用于将错误码转换为错误消息字符串。它的作用是根据错误码 errnum 返回一个指向描述该错误的消息的指针。
字符分类函数:
函数 | 如果他的参数符合下列条件就返回真 |
---|---|
iscntrl | 任何控制字符 |
isspace | 空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’ |
isdigit | 十进制数字 0~9 |
isxdigit | 十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F |
islower | 小写字母a~z |
isupper | 大写字母A~Z |
isalpha | 字母a~z或A~Z |
isalnum | 字母或者数字,a~z,A~Z,0~9 |
ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) |
isgraph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符和空白字符 |
字符转换:
1.11 memcpy
void *memcpy(void *dest, const void *src, size_t n);
- 从源内存地址 src 开始拷贝 n 个字节到目标内存地址 dest。
- dest 是指向用于存储拷贝内容的目标数组的指针。
- src 是指向要拷贝的数据源的指针,src 和 dest 的类型通常是一个无类型指针(void pointer),这意味着它们可以指向任何类型的数据。
- memcpy 拷贝指定数量的字节,不会在目标数组中添加空字符,也不会因为遇到空字符而停止拷贝。
- n 是一个 size_t 类型的值,代表要拷贝的字节数。
使用 memcpy 函数时需要小心,因为它不检查数组边界或重叠。如果目标和源内存区域重叠,拷贝的结果是未定义的。如果需要处理可能重叠的内存区域,应该使用 memmove 函数。
在这个例子中,字符串 "Hello World" 被复制到 dest 数组中,包括字符串结束的空字符。函数返回的是 dest 的指针,但在实际使用中通常不需要接收这个返回值。
1.12 memmove
void * memmove ( void * destination, const void * source, size_t num );
- 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的
- 如果源空间和目标空间出现重叠,就得使用memmove函数处理
在这个例子中,memmove将字符串 “can be very” 从索引15开始的位置复制到索引20开始的位置,覆盖了 “can be very” 后面的字符。最终打印的字符串将是 “memmove can be very useful”。
请注意,memmove和 memcpy的功能类似,但是 memcpy不保证在源地址和目标地址重叠时能正确工作,而 memmove可以。因此,当你不确定源地址和目标地址是否重叠时,应该使用 memmove来确保程序的正确性。
虽然 memmove 可以处理重叠的内存区域,但如果您确定内存区域不会重叠,使用 memcpy 函数会更高效,因为 memcpy 不会处理重叠的情况,因此通常执行速度更快。
1.13 memcmp
int memcmp(const void *ptr1, const void *ptr2, size_t num);
memcmp 是 C 语言标准库中的一个函数,用于比较两个内存块中的前 num 个字节。它按字节逐个比较两个内存块,直到遇到不同的字节或者比较完指定的字节数。
参数说明:
- ptr1:指向第一个内存块的指针。
- ptr2:指向第二个内存块的指针。
- num:要比较的字节数。
返回值:
- 如果 ptr1 和 ptr2 指向的前 num 个字节都相同,memcmp 返回 0。
- 如果 ptr1 指向的字节大于 ptr2 指向的字节,memcmp 返回一个正数。
- 如果 ptr1 指向的字节小于 ptr2 指向的字节,memcmp 返回一个负数。
1.14 memset
void *memset(void *ptr, int value, size_t num);
memset 函数会将 ptr 指向的内存区域的前 num 个字节设置为 value 的值。这个函数通常用于初始化内存块,或者将内存块中的数据清零。函数的返回值是指向被设置内存区域的指针,即 ptr。
2. 库函数的模拟实现
2.1 模拟实现strlen
三种方式:
方式1:计数器方式
int my_strlen(char* str)
{
int length = 0;
while (*str != '\0')
{
length++;
str++;
}
return length;
}
方法2:(不能创建临时变量计数器)递归
int my_strlen(char* str)
{
if (*str == '\0')
return 0;
else
return 1 + my_strlen(str + 1);
}
方法3:指针-指针的方式
int my_strlen(char* str)
{
char* start = str;
while (*str != '\0')
{
str++;
}
return str - start;
}
int my_strlen(char* str)
{
char* start = str;
while (*str++ != '\0')
{
;
}
return str - start - 1;
}
2.2 模拟实现strcpy
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
while (*dest++ = *src++)
{
;
}
return ret;
}
2.3 模拟实现strcat
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;
/*if (*str1 > *str2)
return 1;
else
return -1;*/
}
2.4 模拟实现strstr
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
char* s1 = NULL;
char* s2 = NULL;
char* cp = (char*)str1;
while (*cp)
{
s1 = cp;
s2 = (char*)str2;
while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
return cp;
cp++;
}
return NULL;
}
2.5 模拟实现strcmp
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;
/*if (*str1 > *str2)
return 1;
else
return -1;*/
}
int my_strcmp(const char* src, const char* dst)
{
int ret = 0;
assert(src != NULL);
assert(dest != NULL);
while (!(ret = *(unsigned char*)src - *(unsigned char*)dst) && *dst)
++src, ++dst;
if (ret < 0)
ret = -1;
else if (ret > 0)
ret = 1;
return(ret);
}
2.6 模拟实现memcpy
void* my_memcpy(void* dest, void* src, size_t num)
{
void* ret = dest;
assert(dest && src);
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
2.7 模拟实现memmove
void* my_memove(void* dest, 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;
}
到这里C语言库函数的学习基本就结束了~如果本篇文章对您有帮助,麻烦点赞收藏以及评论一下把~