【C语言】字符函数和字符串函数

㊙️小明博客主页:➡️ 敲键盘的小明 ㊙️
✅关注小明了解更多知识☝️


文章目录

  • 前言
  • 库函数的学习网站
  • 一、求字符串长度
    • 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;
}

代码运行结果:

剩下的函数大家可以自行尝试一下。



完结

好啦,阅读到这里就已经看完了本期博客的全部内容了

相关推荐
qystca2 小时前
洛谷 B3637 最长上升子序列 C语言 记忆化搜索->‘正序‘dp
c语言·开发语言·算法
网易独家音乐人Mike Zhou8 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
搬砖的小码农_Sky10 小时前
C语言:数组
c语言·数据结构
ahadee14 小时前
蓝桥杯每日真题 - 第19天
c语言·vscode·算法·蓝桥杯
Theliars15 小时前
C语言之字符串
c语言·开发语言
Reese_Cool15 小时前
【数据结构与算法】排序
java·c语言·开发语言·数据结构·c++·算法·排序算法
惜.己15 小时前
Jmeter中的断言(二)
测试工具·jmeter·1024程序员节
搬砖的小码农_Sky16 小时前
C语言:结构体
c语言·数据结构
平头哥在等你17 小时前
求一个3*3矩阵对角线元素之和
c语言·算法·矩阵
尹蓝锐18 小时前
C语言-11-18笔记
c语言