㊙️小明博客主页:➡️ 敲键盘的小明 ㊙️
✅关注小明了解更多知识☝️
文章目录
- 前言
- 库函数的学习网站
- 一、求字符串长度
-
- 1.1 strlen
-
- 1.1.1 strlen函数
- 1.1.2 模拟实现strlen
-
- 计数器法
- 递归法(不创建临时变量)
- 指针 - 指针
- 二、长度不受限制的字符串函数
-
- 2.1 strcpy
-
- 2.1.1 strcpy函数
- 2.1.2 模拟实现strcpy
- 2.2 strcat
-
- 2.2.1 strcat函数
- 2.2.2 模拟实现strcat
- 2.3 strcmp
-
- 2.3.1 strcmp函数
- 2.3.2 模拟实现strcmp
- 三、 长度受限制的字符串函数
-
- 3.1 strncpy
-
- 3.1.1 strncpy函数
- 3.1.2 模拟实现strncpy
- 3.2 strncat
-
- 3.2.1 strncat函数
- 3.2.2 模拟实现strncat
- 3.3 strncmp
-
- 3.3.1 strncmp函数
- 3.3.2 模拟实现strncmp
- 四、字符串查找
-
- 4.1 strstr
-
- 4.1.1 strstr函数
- 4.1.2 模拟实现strstr
- 4.2 strtok
-
- 4.2.1 strtok函数
- 4.2.2 模拟实现strtok
- 五、错误信息报告
-
- 5.1 strerror
-
- 5.1.1 strerror函数
- 六、字符操作
-
- 介绍
- 完结
前言
提示:本篇文章为C语言中常用的字符串函数和内存函数的详细笔记和完整的源码,内容如若有误,请联系小明及时更正。
- 转载请注明原创,谢谢。
提示:以下是本篇文章正文内容:
本篇所讲的是C语言中常用的字符串函数如strlen、strcpy、strcmp、strcat等的原理和使用,还有一些字符函数,如isspace和isgraph等,以及模拟实现这些函数的方法。
同时,还讨论了内存函数memcpy、memmove、memset和memcmp的功能和应用。
库函数的学习网站
小明给大家将网址放在在下面:
c p l u s p l u s cplusplus cplusplus网站:http://www.cplusplus.com/reference/
或者大家可以直接点击下方链接访问:
由于新版的 cplusplus 网站没有搜索功能,所以在打开 cplusplus 网站后,点击右上角 [ Legacy version ],转到旧版本。
旧版:
一、求字符串长度
1.1 strlen
1.1.1 strlen函数
求字符串长度
c
size_t strlen ( const char * str );
** 这个函数的返回值是size_t , 参数是 const char* str,这里用const修饰说明str指向的字符串是不能被修改的。
该函数的作用是求字符串的长度,字符串的结束标志是 '\0' ,strlen函数返回的是字串的长度,即:到 '\0' 之前的字符个数(不包括'\0')。
该函数的参数指向的字符串的必须要以 '\0' 结束,它的返回值是:size_t(无符号整形)。**
c
#include<stdio.h>
#include<string.h>
int main()
{
char* str1 = "Hello World!";//12个
char* str2 = "XiaoMing";//8个
printf("%d\n", strlen(str1));
printf("%d\n", strlen(str2));
return 0;
}
代码运行结果:
当然,我们知道在字符串中末尾是有 '\0' 的,如果我们直接不写 '\0' 会怎样呢?
c
#include<stdio.h>
#include<string.h>
int main()
{
char str1[] = "abcdef";
char* str2 = "abcdef";
char str3[] = { 'a', 'b', 'c', 'd', 'e','f' };
printf("str1 = %d\n", strlen(str1));
printf("str2 = %d\n", strlen(str2));
printf("str3 = %d\n", strlen(str3));
return 0;
}
代码运行结果:
在这里我们可以看到 str3 有 33 个字节,这是为什么呢?让我们打开调试窗口观察一下:
在这里可以清楚的看到 str1 末尾是有 '\0 ' 存在的,而在 str3 中则并没有 '0' ,这就导致 strlen 函数的参数会一直向后查找,直到遇到 '0' 才会结束!!!
但是,这个数组的 '、0' 在哪里?抱歉,不晓得,所以 str3 求得的是一个随机值。
关于这个函数再来看一个笔试题:
c
#include<stdio.h>
#include<string.h>
int main()
{
const char* str1 = "abcdef";
const char* str2 = "bbb";
if (strlen(str2) - strlen(str1) > 0)
{
printf("str2 > str1\n");
}
else
{
printf("str1 > str2\n");
}
return 0;
}
可以先思考一下结果会是什么呢?
代码运行结果:
为什么是str2 > str1?
因为 strlen 的返回值是 size_t(unsigned int) 他们的返回值是恒大于等于0的,两者作差也是恒大于等于0的,因此走了 if 这样写达不到我们的效果,要比较两个字符串的长度大小。
所以我们写成下面这个样子:
c#include<stdio.h> #include<string.h> int main() { const char* str1 = "abcdef";> const char* str2 = "bbb"; if (strlen(str2) > strlen(str1)) { printf("str2 > str1\n"); } else { printf("str1 > str2\n"); } return 0; }
代码运行结果:
1.1.2 模拟实现strlen
计数器法
c
size_t my_strlen(const char* str)
{
assert(str);//避免str是空指针
int count = 0;
while (*str++)
{
count++;
}
return count;
}
代码运行结果:
递归法(不创建临时变量)
c
size_t my_strlen(const char* str)
{
assert(str);
if (*str == '\0')
return 0;
else
return 1 + my_strlen(str + 1);
}
代码运行结果:
指针 - 指针
c
size_t my_strlen(const char* str)
{
assert(str);
const char* ret = str;
while (*str)
str++;
return str - ret;
}
代码运行结果:
二、长度不受限制的字符串函数
2.1 strcpy
2.1.1 strcpy函数
拷贝字符串
c
char * strcpy ( char * destination, const char * source );
1 . 源字符串必须以 '\0' 结束 。 ---- 将strSource中一直到 '\0' 之前的字符以及 '\0' 拷贝到strDestination,所以一定要有 '\0' 。
2 . 会将源字符串中的 '\0' 拷贝到目标空间 。 --- 从strSource中的所有字符都拷贝上去(包括 '\0' )。
3 . 目标空间必须足够大,以确保能存放源字符串 。 --- 被拷贝的空间一定要是足够大的空间能够存放拷贝的字符串。
4 . 目标空间必须可变。--- 如果给的是一个常量字符串那肯定用不了strcpy了,只有数组是可变的才能用strcpy。
c
#include<stdio.h>
#include<string.h>
int main()
{
char str1[10] = "xiaoming";
char str2[] = "haha";
strcpy(str1, str2);
printf("%s\n", str1);
return 0;
}
代码运行结果:
但是为什么不是拷贝成"hahaming"呢?还是老方法,打开调试看一下:
原来如此,strcpy拷贝时,将 '\0' 也拷贝了来,虽然 str1 中还有剩余的字符,但是在输出时,printf 读到 '\0' 便停止了下来。
2.1.2 模拟实现strcpy
c
char* my_strcpy(char* dest, const char* src)
{
assert(dest);
assert(src);
char* ret = dest;
while (*src)
{
*dest = *src;
dest++;
src++;
}
*dest = '\0';
return ret;
}
代码运行结果:
大致流程为:
** 这个代码就是 * src 先解引用赋值给 *dest 然后连个都++;等 *src = '\0' 赋值给 *dest 的时候 while 循环判断为假结束,此时正好把 src 里面的所有内容都拷贝过去了。
**
我们可以再优化一下代码:
c
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
while (*dest++ = *src++)
{
;
}
return ret;
}
代码运行结果:
2.2 strcat
2.2.1 strcat函数
追加字符串
c
char * strcat ( char * destination, const char * source );
1 . 源字符串必须以'\0'结束
2 . 目标空间必须有足够的大,能容纳下源字符串的内容。
3 . 目标空间必须可修改 。
c
#include<stdio.h>
#include<string.h>
int main()
{
char str1[20] = "abcdef";
char str2[] = "123456";
strcat(str1, str2);
printf(str1);
return 0;
}
代码运行结果:
我们可以清楚的看到str1** 成功的追加到了str2 后面。**
2.2.2 模拟实现strcat
参数和 strcpy 的一模一样
c
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
while (*dest)
{
dest++;
}
while (*dest++ = *src++)
{
;
}
return ret;
}
代码运行结果:
既然能把str1** 追加到str2 后面。那么,str1 能不能追加到str1 自己的后面呢?**
即:字符串能不能给自己追加?我们来测试一下:
c
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
while (*dest)
{
dest++;
}
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char str1[20] = "abcdef";
char str2[] = "123456";
strcat(str1, str2);
printf(str1);
return 0;
}
代码运行结果:
我们可以看到代码崩掉了!!!
2.3 strcmp
2.3.1 strcmp函数
字符串比较
c
int strcmp ( const char * str1, const char * str2 );
老样子,直接上例子:
c
#include<stdio.h>
#include<string.h>
int main()
{
char str1[] = "acbdef";
char str2[] = "abcdef";
if (strcmp(str1, str2) > 0)
printf("str1 > str2\n");
else if (strcmp(str1, str2) < 0)
printf("str1 <str2\n");
else
printf("str1 == str2\n");
return 0;
}
代码运行结果:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
2.3.2 模拟实现strcmp
c
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 || *str2)
{
if (*str1 > *str2)
return 1;
else if (*str1 < *str2)
return -1;
str1++;
str2++;
}
return 0;
}
代码运行结果:
三、 长度受限制的字符串函数
3.1 strncpy
3.1.1 strncpy函数
c
char * strncpy ( char * destination, const char * source, size_t num );
c
#include<stdio.h>
#include<string.h>
int main()
{
char str1[20] = "abcdef";
char str2[] = "123456";
strncpy(str1, str2, 4);
printf("%s\n", str1);
return 0;
}
代码运行结果:
3.1.2 模拟实现strncpy
c
char* my_strncpy(char* dest, const char* src, size_t num)
{
assert(dest && src);
char* ret = dest;
while (num--)
{
*dest++ = *src++;
}
return ret;
}
代码运行结果:
3.2 strncat
3.2.1 strncat函数
c
char * strncat ( char * destination, const char * source, size_t num );
直接上例子:
c
#include<stdio.h>
#include<string.h>
int main()
{
char str1[20] = "ming";
char str2[] = "5201314";
strncat(str1, str2, 3);
printf("%s\n", str1);
return 0;
}
3.2.2 模拟实现strncat
c
char* my_strncat(char* dest, const char* src, size_t num)
{
assert(dest && src);
char* ret = dest;
while (*dest)
{
dest++;
}
while (num--)
{
*dest++ = *src++;
}
return ret;
}
代码运行结果:
3.3 strncmp
3.3.1 strncmp函数
c
int strncmp ( const char * str1, const char * str2, size_t num );
c
#include<stdio.h>
#include<string.h>
int main()
{
char str1[] = "abcdef";
char str2[] = "abcdou";
if (strncmp(str1, str2, 6) > 0)
printf(">");
else if (strncmp(str1, str2, 6) < 0)
printf("<\n");
else
printf("==\n");
return 0;
}
代码运行结果:
3.3.2 模拟实现strncmp
c
int my_strncmp(const char* str1, const char* str2, size_t num)
{
assert(str1 && str2);
while ((num) && (*str1 == *str2))
{
num--;
str1++;
str2++;
}
if (num != 0)
{
if (*str1 > *str2)
return 1;
else
return -1;
}
else
{
return 0;
}
}
代码运行结果:
四、字符串查找
4.1 strstr
4.1.1 strstr函数
在字符串中查找子字符串
c
const char * strstr ( const char * str1, const char * str2 );
char * strstr ( char * str1, const char * str2 );
c
#include<stdio.h>
#include<string.h>
int main()
{
char* str1 = "xiaoming";
char* str2 = "ao";
char* ret = strstr(str1, str2);
if (ret != NULL)
{
printf(ret);
}
else
{
printf("没有!\n");
}
return 0;
}
sep参数是个字符串,定义了用作分隔符的字符集合
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
如果字符串中不存在更多的标记,则返回 NULL 指针。
4.1.2 模拟实现strstr
c
const char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
const char* cp = str1;
const char* s2 = str2;
const char* s1 = cp;
if (*str2 == '\0')
{
return str1;
}
while (*cp)
{
s1 = cp;
s2 = str2;
while (*s2 && *s1 && (*s2 == *s1))
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return cp;
}
cp++;
}
return NULL;
}
代码运行结果:
4.2 strtok
4.2.1 strtok函数
按标记分隔字符串
c
char * strtok ( char * str, const char * delimiters );
c
#include<stdio.h>
#include<string.h>
int main()
{
char str[] = "xiaoming.688@qq.com";
char delimiters[] = ".@";
char copy[20];
strcpy(copy, str);
char* ret = strtok(copy, delimiters);
printf("%s\n", ret);
ret = strtok(NULL, delimiters);
printf("%s\n", ret);
ret = strtok(NULL, delimiters);
printf("%s\n", ret);
ret = strtok(NULL, delimiters);
printf("%s\n", ret);
return 0;
}
代码运行结果:
再来个简单的例子:
c
#include<stdio.h>
#include<string.h>
int main()
{
char str[] = "192.168.1.1";
char sep[] = ".";
char copy[20];
strcpy(copy, str);
char* ret;
for (ret = strtok(copy, sep); ret != NULL; ret = strtok(NULL, sep))
{
printf("%s\n", ret);
}
return 0;
}
代码运行结果:
4.2.2 模拟实现strtok
c
char* my_strtok(char* str, const char* delimiters) {
static char* nextToken = NULL; // 用于保存下一个标记的位置
if (str != NULL) {
nextToken = str; // str!=NULL,表示第一次调用,设置初始位置
}
if (nextToken == NULL || *nextToken == '\0') {
return NULL; // 没有更多的标记可供拆分
}
// 跳过开始的分隔符字符
while (*nextToken && strchr(delimiters, *nextToken)) {
nextToken++;
}
if (*nextToken == '\0') {
return NULL; // 已到达字符串末尾
}
// 找到标记的起始位置
char* currentToken = nextToken;
// 继续查找下一个分隔符位置,将其替换为\0字符
while (*nextToken && !strchr(delimiters, *nextToken)) {
nextToken++;
}
if (*nextToken) {
*nextToken = '\0'; // 替换为 \0字符
nextToken++; // 下一个标记的起始位置
}
return currentToken; // 返回当前拆分的标记
}
int main() {
char str[] = "192.168.1.1";
char delimiters[] = ".";
char copy[30];
strcpy(copy, str);
char* ret;
for (ret = my_strtok(copy, delimiters); ret != NULL; ret = my_strtok(NULL, delimiters))
{
printf("%s\n", ret);
}
return 0;
}
代码运行结果:
五、错误信息报告
5.1 strerror
5.1.1 strerror函数
获取指向错误信息字符串的指针
c
char * strerror ( int errnum );
c
#include<stdio.h>
#include<string.h>
int main()
{
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%s\n", strerror(i));
}
return 0;
}
代码运行结果:
翻译:
让我们举个例子:
此时我们的文件目录里是没有 "data.txt" 文件的:
c
#include<errno.h>
int main()
{
//打开文件用fopen他的返回值是一个FILE类型的指针
FILE* pf = fopen("deta.txt", "r");//r是只读,"data.txt"是当前路径下
if (pf == NULL)
{
printf("%s\n", strerror(errno));
exit(-1);//异常退出
}
//读写...
//关闭文件
fclose(pf);
return 0;
}
代码运行结果:
六、字符操作
介绍
** 我们之前就了解过一些字符函数:比如putchar、getchar等;其实还有很多的字符函数比如:判断空间大写转小写,小写转大写!下面我们来看看:**
函数 | 作用 |
---|---|
int tolower ( int c) | 字母大写转小写 |
int toupper ( int c) | 字母小写转大写 |
函数 | 如果他的参数符合下列条件就返回真 |
---|---|
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 | 任何可打印字符,包括图形字符和空白字符 |
我们简单的用转换大写函数来举个例子:
c
#include<stdio.h>
#include <ctype.h>
int main()
{
int i = 0;
char str[] = "XiaoMing.\n";
char c;
while (str[i])
{
c = str[i];
if (isupper(c))
{
c = tolower(c);
}
putchar(c);
i++;
}
return 0;
}
代码运行结果:
剩下的函数大家可以自行尝试一下。
完结
好啦,阅读到这里就已经看完了本期博客的全部内容了