字符函数,字符串函数,内存函数

Typora图片上云折腾了好久,最后终于成功了

文章目录

ctype.h

1.字符判断

判断是否是对应内容,如果是就返回非0,不是就返回0

  • iscntrl:控制字符:https://c.biancheng.net/c/ascii/中的黄色的(码值为0\~31和127)
    • 常见的有\n ,\0
  • isspace:
    1. 空格 ' '(ASCII 32)
    2. 换页 \f(12)
    3. 换行 \n(10)
    4. 回车 \r(13)
    5. 制表符 \t(9)
    6. 垂直制表符 \v(11)
  • isdigit:数字字符
  • isxdigit:16进制数字所包含的字符:09,az、A~Z
  • islower:小写字母
  • isupper:大写字母
  • isalpha:字母
  • isalnum:字母+数字
  • ispunct:标点符号:! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ { | } ~`
  • isgraph:能看见的:ASCII 33 ~ 126(所有可打印字符,排除空格' '
  • isprint:能打印的:ASCII 32 ~ 126(所有可打印字符,加上空格' '

2.字符转换

  • tolower:小写转大写,其他不变
  • toupper:大写转小写,其他不变

具体实践:将商标中的小写转化成大写

  1. 通过字符判断函数
cpp 复制代码
//将商标中的小写字母转为大写
#include<stdio.h>
#include<string.h>
#include<ctype.h>
char* atoA(char* s,int size)
{
	char* ret = s;
	for (int i = 0; i < size; i++)
	{
		if (islower(s[i]))
			s[i] = s[i] - 32;
	}
	
	return ret;
}



int main()
{
	char s[] = "12310Tag";
	int size = strlen(s) / sizeof(s[0]);
	printf("%s\n", atoA(s, size)); 

	return 0;
}
  1. 通过字符转换函数
cpp 复制代码
char* atoA(char* s, int size)
{
	char* ret = s;
	for (int i = 0; i < size; i++)
	{
		s[i] = toupper(s[i]);
	}

	return ret;
}

string.h

strlen

基本功能和条件:

  • 计算字符串长度
  • 传参是char*,返回值是size_t
  • 字符串要有\0(使用双引号初始化),strlen只读到\0

一般示例(求学生英文作文中包含空格的字数):

cpp 复制代码
//求作文中包含空格的字数
#include<stdio.h>
#include<string.h>
int main()
{
	char s[] = "this is my article";
	printf("%zu\n", strlen(s));
	return 0;
}
  • 特殊事例(字符串中有'\0'的情况):
cpp 复制代码
#include<stdio.h>
#include<string.h>
int main()
{
	char s[] = "this \0 is my article";
	printf("%zu\n", strlen(s));
	return 0;
}

和sizeof的比较:

  • 二者返回值都是size_t类型
  • strlen只计算字符数组的长度,sizeof计算任意数组的字节长度
  • strlen计算到\0截止,sizeof不会截止(计算含\0的字符串,后者会计算\0,不能用于遍历

三种方法模拟实现strlen

  1. 直接进行计数
cpp 复制代码
#include<stdio.h>
size_t mystrlen(char* s)
{
	size_t len = 0;
	while (*s != '\0')
	{
		len++;
		s++;
	}
	return len;
}


int main()
{
	char s[] = "hello i am 12310";
	printf("%zu\n", mystrlen(s));
	return 0;
}
  1. 使用递归的方式
cpp 复制代码
#include<stdio.h>
size_t mystrlen(char* s)
{
	size_t len = 0;
	if (*s == '\0')
		return 0;

	return (mystrlen(s+1) + 1);
}


int main()
{
	char s[] = "hello i am 12310";
	printf("%zu\n", mystrlen(s));
	return 0;
}
  1. 两个指针相减的方式
cpp 复制代码
size_t mystrlen(char* s)
{
	char* tmp = s;
	while (*s)
	{
		s++;
	}
	return (s - tmp);

}

strcpy/strncpy

  • 功能:将source中的内容复制到destination中

  • 条件:

    • 传参
      • 前者传两个参数
        第一个是目标地址
        第二个是source的地址
      • 后者传3个参数,前两个参数和前者一样, 多了一个长度的参数
  • 一般示例:

    • strcpy(将nihao复制到hello的位置):
      • strcpy会将后面的斜杠零也复制进去,导致使用printf的时候打印到第一个\0就会停
cpp 复制代码
#include<stdio.h>
#include<string.h> 
int main()
{
	char s[] = "hello i am 12310";
	char s0[] = "nihao";
	printf("%s\n", strcpy(s, s0));
	return 0;
}
  • strncpy:
    • 这里只将你好的长度作为第3个参数传进去了,所以不会复制结尾的斜杠0,使用printf自然也不会出问题
cpp 复制代码
#include<stdio.h>
#include<string.h> 
int main()
{
	char s[] = "hello i am 12310";
	char s0[] = "nihao";
	printf("%s\n", strncpy(s, s0, strlen(s0)) );
	return 0;
}

特殊情况

  1. Source未满 \ Source中(第N个字符前)有\0

内存情况:

(该放进去的位置还是会放进去,只是不够的补成了\0)

模拟实现

  • strcpy
cpp 复制代码
#include<stdio.h>
char* mystrcpy(char* dest, char* sou)
{
	char* ret = dest;
	//从第一个字符开始,将sou中的复制到dest中
	while (*sou)
	{
		*dest = *sou;
		dest++;
		sou++;
	}
	*dest = '\0';
	return ret;
} 
int main()
{
	char s[] = "hello i am 12310";
	char s0[] = "nihao";
	printf("%s\n", mystrcpy(s, s0 ) );
	return 0;
}
  • 缺点:逻辑清晰,但是代码行数比较多。而且当sou到遇\0了就会停止进行复制

微微改造:后置加加,先使用再加加。相当于和前面一样那两句

再次微微改造:这个语句等号语句,它的返回值相当于就是赋值后的内容,那么当赋值为斜杠铃之后会停止,这时候斜杠0已经放进去了,不需要在结尾再单独再最后放一个\0

最终版本

cpp 复制代码
#include<stdio.h>
char* mystrcpy(char* dest, char* sou)
{
	char* ret = dest;
	//从第一个字符开始,将sou中的复制到dest中
	while (*(dest++) = *(sou++))
	{
		;
	} 
	return ret;
} 
int main()
{
	char s[] = "hello i am 12310";
	char s0[] = "nihao";
	printf("%s\n", mystrcpy(s, s0 ) );
	return 0;
}
  • strncpy

    • 注意事项:我这个句子比较简便,我在while循环中直接&&上了一个num,可是可是while循环中的这个句子它是会从前往后顺下去,也就是说当你的num已经变成0时,前面的 (dest++) = (sou++)多进行了一次进行了4次而不是num中的3。这时候会导致错误: 就比如我例子中,你好最后的\0也添加进去了,所以打印的时候只打印出了你好

    • 错误示例:

    • 正确示例:

二者的区别:

  • 后者有复制数的限制,参数也多一个
  • 在有\0的字符串的复制中,前者只能起到覆盖作用,因为斜杠铃是必须会加进去的

strcat/strncat

基本用法

  • 将source字符串添加到destination字符串的末尾
  • 前者直接添加,后者有添加数的限制

注意事项:

我图中出现的错误:虽然我想把S0添加到S的后面,但是他们在初始化的时候字符串的长度已经把数组长度给填满了,所以已经没位置给我的S0了。所以如果想使用的话,字符串在创建的时候必须要给更长的长度

错误示例:

正确示例

特殊情况

  1. sou中间有\0:只会添加\0之前的内容

模拟实现

  • strcat

  • strncat

二者的区别

  • 后者多了一个参数,更灵活更安全。
  • 后者的source也不需要有\0了

strcmp/strncmp

基本用法

  • 传参基本和前两个一样
  • 返回值是int类型
    • 如果前者大就返回正数
    • 后者大就返回负数
    • 一样大返回0
  • 按照字典去比较

模拟实现

  • strcmp
cpp 复制代码
#include<stdio.h>
int cmp(char* s1, char* s2)
{
	//前面的元素可能相同,所以先找到第一个不相同的元素
	while (*s1 == *s2)
	{
		s1++;
		s2++;
	}
	return(*s1 - *s2);
}


int main()
{
	char s1[] = "hello";
	char s2[] = "he";

	if ( cmp(s1, s2 ) > 0)
		printf("hello 的字典序更大");
	else if ( cmp(s1, s2 ) < 0)
		printf("hi 的字典序更大");
	else
		printf("两者 字典序一样大");

	return 0;
}
  • strncmp:

错误示范(原因是: 我只想比较前两个字符,while循环也的确只进行了两次,但是两次++之后得到的是第三个字符,通过第三个字符来比较自然是不合理的):

正确示范

得到的教训:

While循环实际进行的次数,和我们想要的次数 的关系 比较难以确定

  • 如本题中:想要while中的数组只比较下标为0和1的元素,但是因为while执行两次,最后指针指向的下标是2,想让比较过的元素在最后相减,就不行

strstr

基本用法:

  • 传参传两参数
    第一个参数是一个字符串的地址
    第二个参数是想找的字符串的地址
  • 返回值也是个地址,是想找的字符串在第一个字符串中 第一次出现的位置。
    • 如果不存在返回NULL

例子:

找到abcddfghi中的dd,并改为de

模拟实现

cpp 复制代码
//模拟实现strstr
#include<stdio.h>
char* mystrstr(char* str, char* target)
{
	//从第一个元素开始,双层循环,外层是对str的遍历,内层是判断这次的位置p开始的字符和target中的是否匹配
	//如果能一直匹配直到target的\0,就return p。
	while (*str)
	{
		char* p = str;
		char* posoft = target;//position of target指向target的首
		while (*posoft && *p&& (*posoft==*p) )//当然还有前提条件就是遍历的两个量都不是\0
		{
			posoft++;
			p++;
		}
		if (*posoft == '\0')
			return str; 
		str++;
	}
	return NULL;
}


int main()
{
	char s[] = "hello this is the ans";
	char s1[] = "the";

	//如果能找到'the'就从the开始打印
	printf("%s\n", mystrstr(s, s1));


	return 0;
}

strtok

基本用法

  • 最基本的用法
    • 第一次使用传参的第一个参数是一个字符串的 地址
      第二个参数也是一个字符串,字符串中的每一个字符都会被当做想要截断的字符(对应位置在被截断的时候会变为\0)
    • 在第二次以及以后的传参,第一个参数变成了NULL
    • 每次都会返回一个地址:截断所得的 前一段的地址
    • 当整个字符串已经都被截断过了,返回的就是NULL
cpp 复制代码
#include<string.h>
#include<stdio.h>
int main()
{
	char s[] = "emm,it's hard to tell.maybe,next time";
	printf("%s\n", strtok(s, ",."));
	printf("%s\n", strtok(NULL, ",."));
	printf("%s\n", strtok(NULL, ",."));
	printf("%s\n", strtok(NULL, ",."));


	return 0;
}
  • 一般用法(使用for循环):
cpp 复制代码
#include<string.h>
#include<stdio.h>
int main()
{
	char s[] = "emm,it's hard to tell.maybe,next time";
 
	for (char *tmp=strtok(s,".,");tmp!=NULL;tmp=strtok(NULL,".,"))
	{
		printf("%s\n", tmp); 
	}

	return 0;
}

注意事项

  • 这会将整个字符串截断,字符串应该提前复制保留
  • 传的第二个参数是个字符串,其中的每个字符都会被当做单独的 要去截断的字符,截段意味着把它变成\0

strerror与perror

  • 前者的使用需要配合errno(需要单独的头文件errno.h),当运行有错误的时候,使用strerror,参数为errno,打印的时候使用%s,就能打印对应错误信息
cpp 复制代码
#include <stdio.h>
#include <string.h>
#include <errno.h>

int main() {
    // 尝试打开一个不存在的文件
    FILE *fp = fopen("不存在的文件.txt", "r");

    // 判断:fopen 失败会返回 NULL
    if (fp == NULL) {
        // 用 strerror 翻译 errno 里的错误码
        printf("文件打开失败!原因:%s\n", strerror(errno));
        //打印出来的结果:"文件打开失败!原因:No such file or directory"
        return 1;
    }

    fclose(fp);
    return 0;
}
  • 后者的使用更加简洁,(不过头文件相同)。
    • 传的参数是一个字符串,打印错误时会在字符串后面添上一个冒号再添加上对应的错误
cpp 复制代码
perror("打开文件失败");
//打印的结果:"打开文件失败: No such file or directory"

memcpy

基本用法

  • 使用时的目的和strcpy相似
  • 前两个参数和strcpy相同,但是第三个参数变成了字节长度

和strcpy对比

  • 前者仅对字符串有效,后者没有限制
  • 前者在遇到\0时停止,并且会把\0也添加上去。后者不会

局限

  • memcpy无法处理内存重叠的情况(此时使用memmove)

模拟实现

cpp 复制代码
#include<stdio.h>
void* mymemcpy(void* dest, void* sou, int len)
{
	void* ret = dest;
	//逐个字节进行修改
	for (int i = 0; i < len; i++)
	{
		*((char*)dest + i) = *((char*)sou + i);
	}
	return dest;
}
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int u[3] = { 3,3,5 };
	mymemcpy(arr, u, sizeof(u));
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

memmove

基本用法

  • 基本用法和memcpy相同,但是能处理内存重叠的情况
  • 在模拟实现的时候需要考虑从前往后还是从后往前

和memcpy对比

  • memmove能够应对内存重叠的情况

模拟实现

cpp 复制代码
#include<stdio.h>
void* mymemmove(void* dest, void* sou, int len)
{
	void* ret = dest;
	//逐个字节进行修改
	if ((char*)sou + len - 1 >= dest && sou < dest)
	{
		while(len--)
		{
			*((char*)dest + len) = *((char*)sou + len);
		}
	} 
	else
	{
		for (int i = 0; i < len; i++)
		{
			*((char*)dest + i) = *((char*)sou + i);
		}
	}
	return dest;
}
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	mymemmove(arr+3, arr, 20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

memset

基本用法

  • 第一个参数是一个地址

    第二个参数是想把地址上设置的内容,可以是数字也可以是字符

    第三个参数是想设置的字节数

局限性

  • 在设置字符的时候自然是没有问题,因为一个字符就是一个字节
  • 但是如果想将整型数组设置的话就不太行,毕竟一个征信的是4个字节嘛
    • 一般对整型数组使用的时候只设****0

memcmp

基本用法

  • 传的参数和strcmp相似,第三个参数的含义变了------>相比较的字节数

和strcmp对比

  • strcmp只能比较字符串,而且只能比较到一个字符串的\0 。而memcmp没有限制
相关推荐
chao1898442 小时前
基于改进二进制粒子群算法的含需求响应机组组合问题MATLAB实现
开发语言·算法·matlab
独特的螺狮粉2 小时前
古诗词飞花令随机出题小助手:鸿蒙Flutter框架 实现的古诗词游戏应用
开发语言·flutter·游戏·华为·架构·开源·harmonyos
cch89182 小时前
C++、Python与汇编语言终极对比
java·开发语言·jvm
never forget shyang2 小时前
CCS20.2.0使用教程
c语言·git·单片机
吃着火锅x唱着歌2 小时前
深度探索C++对象模型 学习笔记 第三章 Data语意学(2)
c++·笔记·学习
Chockmans2 小时前
2026年3月青少年软件编程(Python)等级考试试卷(六级)
开发语言·python·青少年编程·蓝桥杯·pycharm·python3.11·python六级
Python大数据分析@2 小时前
使用Python和亮数据采集器搭建专利查询GUI系统
开发语言·python
Imxyk2 小时前
P9242 [蓝桥杯 2023 省 B] 接龙数列
c++·算法·图论
码云数智-园园2 小时前
Python 列表与元组:从核心区别到实战选型
运维·服务器·windows