C语言-字符函数和字符串函数

 学习流程

———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 

字符转化函数和字符分类函数

字符转换函数

islower(判断小写)

在C语言中:
- `islower` 函数用于检查一个字符是否是小写字母。

如果字符是小写字母,函数返回非零值(通常为 `1`);如果字符不是小写字母,函数返回 `0`。

#include <ctype.h>
int islower(int c);

代码举例(这里代码举例主要包括代码的模拟和代码的使用)

举例1

当你调用islower函数时,如果你传递的字符是小写字母,它将返回非零值(通常是1),否则返回0。在C语言中,非零值通常被视为“真”,而0被视为“假”。
#include <stdio.h>
#include <ctype.h> // 需要包含ctype.h头文件

int main() {
    char ch = 'a'; // 例子中的字符
    if (islower(ch)) {//这里判断是不是小写
        printf("字符 %c 是小写字母。\n", ch);
    } else {
        printf("字符 %c 不是小写字母。\n", ch);
    }
    return 0;
}
在这个例子中,如果ch是小写字母,islower函数将返回1,if语句中的条件将为真,然后会打印出相应的消息。如果ch不是小写字母,islower将返回0,if语句中的条件将为假,打印出另一个消息。

举例2

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<ctype.h>
//islower的使用
//在C语言中,可以使用islower函数来判断一个字符是否为小写字母。
// islower函数是ctype.h头文件中的一个函数,它的返回值是一个非零值(通常是1),表示字符是小写字母;
// 返回值为0表示字符不是小写字母。
// 大写字母A的ASCII码值比小写字母a的ASCII码值大32。

int main()
{
	//首先我们这里先模拟判断和转化的条件都是什么
	char arr1[] = "I Am A Studen"; 
	int a = 0;
	while (arr1[a] != '\0')//这里是只要遇见的不是最后一个字符后面的'\0'也就是一直循环 遇见也就说明循环结束
	{
		if (arr1[a] >= 'a' && arr1[a] <= 'z')//这里是一个取值范围 这里可以理解为ASCII码值的取值范围 在C语言里面有不能访问的地址 a-z大小写也都是不能访问地址
		{
			arr1[a] -= 32;// 大写字母A的ASCII码值比小写字母a的ASCII码值大32。所以按照逻辑来进行说明的话 需要+-32
		}
		a++;//这里数组的形式 也就遍历数组  每次数组的那个元素 满足条件的情况下 就会转化 不满足条件的情况下则++ 到下一个元素
	}
	printf("a-z计算                  %s\n", arr1);//这里采取一个打印说明

	char arr2[] = "I Am A Studen";
	int b= 0;
	while (arr2[b] != '\0')
	{
		if (islower(arr2[b]))//这里和上面是没有什么区别的 这里的islower的作用就是判断出是不是小写 如果是小写 那么返回值不是0 (如果不是小写,返回值是0,跳出循环) 满足小写条件的时候 进入计算条件
		{
			arr2[b] -= 32;// 大写字母A的ASCII码值比小写字母a的ASCII码值大32。
		}
		b++;//剩下的逻辑和之前的逻辑判断是一样的
	}
	printf("islower(找到小写字母)  %s\n", arr2);
	return 0;
}

———————————————————————————————————————————

isupper(判断大写)

- `isupper` 函数用于检查一个字符是否是大写字母。

如果字符是大写字母,函数返回非零值(通常为 `1`);如果字符不是大写字母,函数返回 `0`。
 

#include <ctype.h>
int isupper(int c);

代码举例(这里代码举例主要包括代码的模拟和代码的使用)

char arr6[] = "I Am A Studen";
int f = 0;
while (arr6[f] != '\0')
{
	if (isupper(arr6[f]))//这里是一个判断函数 也就是判断是不是大写 如果是大写  此时也就进行下一步的函数计算
	{
		arr6[f] = tolower(arr6[f]);//这里是把大写转化为小写 等同于+32
	}
	f++;
}
printf("isupper(判断大写)和toupper(大写转化为小写)结合:%s\n", arr6);

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————


字符分类函数

tolower(大写转化为小写)

- `tolower` 函数用于将一个字符转换为小写字母。

如果字符是大写字母,函数将其转换为对应的小写字母;如果字符已经是小写字母或者不是字母,则不进行任何转换,函数返回转换后的字符。
 

#include <ctype.h>
int tolower(int c);

代码举例(这里代码举例主要包括代码的模拟和代码的使用)

	char arr6[] = "I Am A Studen";
	int f = 0;
	while (arr6[f] != '\0')
	{
		if (isupper(arr6[f]))//这里是一个判断函数 也就是判断是不是大写 如果是大写  此时也就进行下一步的函数计算
		{
			arr6[f] = tolower(arr6[f]);//这里是把大写转化为小写 等同于+32
		}
		f++;
	}
	printf("isupper(判断大写)和toupper(大写转化为小写)结合:%s\n", arr6);
	//注意事项 这里需要知道 转化和判断函数使用的时候 需要相对结合的准确 因为如果是判断一个大写 但是是把小写转化为大写字符 此时也就是无意义的代码 是没有必要的

———————————————————————————————————————————

toupper(小写转化为大写) 

- `toupper` 函数用于将一个字符转换为大写字母。

如果字符是小写字母,函数将其转换为对应的大写字母;如果字符已经是大写字母或者不是字母,则不进行任何转换,函数返回转换后的字符。

#include <ctype.h>
int toupper(int c);

代码举例(这里代码举例主要包括代码的模拟和代码的使用)

//试题1 用库函数把my name laoda 用库函数转化一下 转化为大写
//试题2 不用库函数my name laoda 用库函数转化一下 转化为大写
//试题1 解答
char str1[] = "my name laoda ";
int g = 0;
while (str1[g] != '\0')
{
	if (islower(str1[g]))//判断是不是小写 判断成功 然后进去转化
	{
		str1[g] = toupper(str1[g]);//这是我出的一个简单的题目 也就是一个用逻辑代码完成 一个用库函数完成 islower判断是不是小写 toupper小写转化为大写
	}
	g++;
}
printf("题目1  %s\n", str1);


//试题2解答和试题1形成对比
char str2[] = "my name laoda ";
int h = 0;
while (str2[h] != '\0')//这里也就是一个基本的逻辑代码 只要遇见的不是最后一个字符'\0'就继续循环
{
	if (str2[h] >= 'a' && str2[h] <= 'z')
	{
		str2[h] -= 32;// 大写字母A的ASCII码值比小写字母a的ASCII码值大32。
	}
	h++;
}
printf("题目2  %s\n", str2);

———————————————————————————————————————————

字符转化函数和字符分类函数代码总结

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<ctype.h>
//islower的使用
//在C语言中,可以使用islower函数来判断一个字符是否为小写字母。
// islower函数是ctype.h头文件中的一个函数,它的返回值是一个非零值(通常是1),表示字符是小写字母;
// 返回值为0表示字符不是小写字母。
// 大写字母A的ASCII码值比小写字母a的ASCII码值大32。

int main()
{
	//首先我们这里先模拟判断和转化的条件都是什么
	char arr1[] = "I Am A Studen"; 
	int a = 0;
	while (arr1[a] != '\0')//这里是只要遇见的不是最后一个字符后面的'\0'也就是一直循环 遇见也就说明循环结束
	{
		if (arr1[a] >= 'a' && arr1[a] <= 'z')//这里是一个取值范围 这里可以理解为ASCII码值的取值范围 在C语言里面有不能访问的地址 a-z大小写也都是不能访问地址
		{
			arr1[a] -= 32;// 大写字母A的ASCII码值比小写字母a的ASCII码值大32。所以按照逻辑来进行说明的话 需要+-32
		}
		a++;//这里数组的形式 也就遍历数组  每次数组的那个元素 满足条件的情况下 就会转化 不满足条件的情况下则++ 到下一个元素
	}
	printf("a-z计算                  %s\n", arr1);//这里采取一个打印说明

	char arr2[] = "I Am A Studen";
	int b= 0;
	while (arr2[b] != '\0')
	{
		if (islower(arr2[b]))//这里和上面是没有什么区别的 这里的islower的作用就是判断出是不是小写 如果是小写 那么返回值不是0 (如果不是小写,返回值是0,跳出循环) 满足小写条件的时候 进入计算条件
		{
			arr2[b] -= 32;// 大写字母A的ASCII码值比小写字母a的ASCII码值大32。
		}
		b++;//剩下的逻辑和之前的逻辑判断是一样的
	}
	printf("islower(找到小写字母)  %s\n", arr2);


	char arr3[] = "I Am A Studen";
	int c = 0;
	while (arr3[c] != '\0')
	{
		if (arr3[c] >= 'A' && arr3[c] <= 'Z')
		{
			arr3[c] += 32;// 大写字母A的ASCII码值比小写字母a的ASCII码值大32。
		}
		c++;
	}
	printf("A-Z计算                  %s\n", arr3);

	char arr5[] = "I Am A Studen";
	int e = 0;
	while (arr5[e] != '\0')
	{
		if (isupper(arr5[e]))
		{
			arr5[e] += 32;// 大写字母A的ASCII码值比小写字母a的ASCII码值大32。
		}
		e++;
	}
	printf("isupper(找到大写字母)  %s\n\n\n", arr5);
	printf("结合使用(这里需要知道,如果判断为是大写isupper,那么此时返回的不是空指针,也就是此时转化是用toupper进行接收,转化为小写。):\n");
	char arr4[] = "I Am A Studen";
	int d = 0;
	while (arr4[d] != '\0')
	{
		if (islower(arr4[d]))//这里进行判断是不是小写 满足条件此时进行if里面的函数计算
		{
			arr4[d] = toupper(arr4[d]);//这里是一个转化的函数 这里是把小写转化为大写的库函数 这里等同于-32
		}
		d++;
	}
	printf("islower(判断小写)和toupper(小写转化为大写)结合:%s\n", arr4);

	char arr6[] = "I Am A Studen";
	int f = 0;
	while (arr6[f] != '\0')
	{
		if (isupper(arr6[f]))//这里是一个判断函数 也就是判断是不是大写 如果是大写  此时也就进行下一步的函数计算
		{
			arr6[f] = tolower(arr6[f]);//这里是把大写转化为小写 等同于+32
		}
		f++;
	}
	printf("isupper(判断大写)和toupper(大写转化为小写)结合:%s\n", arr6);
	//注意事项 这里需要知道 转化和判断函数使用的时候 需要相对结合的准确 因为如果是判断一个大写 但是是把小写转化为大写字符 此时也就是无意义的代码 是没有必要的

	//试题1 用库函数把my name laoda 用库函数转化一下 转化为大写
	//试题2 不用库函数my name laoda 用库函数转化一下 转化为大写
	//试题1 解答
	char str1[] = "my name laoda ";
	int g = 0;
	while (str1[g] != '\0')
	{
		if (islower(str1[g]))
		{
			str1[g] = toupper(str1[g]);//这是我出的一个简单的题目 也就是一个用逻辑代码完成 一个用库函数完成 islower判断是不是小写 toupper小写转化为大写
		}
		g++;
	}
	printf("题目1  %s\n", str1);


	//试题2解答
	char str2[] = "my name laoda ";
	int h = 0;
	while (str2[h] != '\0')//这里也就是一个基本的逻辑代码 不过多的说明
	{
		if (str2[h] >= 'a' && str2[h] <= 'z')
		{
			str2[h] -= 32;// 大写字母A的ASCII码值比小写字母a的ASCII码值大32。
		}
		h++;
	}
	printf("题目2  %s\n", str2);

	return 0;
}

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

strcmp(对比函数模拟和使用)

strcmp的语法

 字符串的比较目的

这里比如你写一个网站,对照自己的用户和密码进行对比

使用(对比)方式

strcmp(arr1,arr2)

常量

arr1这里是常量 数组名是个地址所以是拷贝

所以不能这样对比

而是

常量字符串这里是把首字符给p 也就是首字符的地址给p

对比方式

如果要比较两个字符串的内容

比较逻辑 一一对应的比较

四种情况 这里比较的是内容

使用逻辑

上面的小于 返回值是-1

这里比较的对应ASCII码值的大小 遇见不一样的直接跳出循环 遇见一样但继续 一个一个进行对比 对比完进行下一个

返回数值逻辑 vs这里是-1 1 0 进行判断的 不一定一定是-1 1

 使用的代码实现

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<string.h>
int main()
{
	char arr1[] = "sadaasd";
	char arr2[] = "sadaasd";
	char arr3[] = "sadasd ";
	char arr4[] = "sadasde";
		int ret1 = strcmp(arr1, arr2);//这里需要说明一下 是一个一个对应 什么意思呢就是 ss 对照  aa dd aa。。。所以返回0
	int ret2 = strcmp(arr1, arr3);//ss aa dd aa as这里是a的ASCII码值比s小 所以返回-1 此时不进行下面的循环
	int ret3 = strcmp(arr4, arr3);
	//这里的逻辑对比是一一对比,如果 前面的字符大 此时也就返回1 小的情况下 也就返回-1 相等的情况下 也就是整个字符串相等 返回0 
	printf("%d\n%d\n%d\n\n", ret1, ret2, ret3);
	printf("%d\n%d\n%d\n\n", ret1, ret2, ret3);
}

 

 代码讲解

`strcmp` 是一个在 C 语言中常用的字符串比较函数,它的作用是比较两个字符串的内容是否相同。`strcmp` 函数的定义通常在 `<string.h>` 头文件中,其原型如下:

int strcmp(const char *str1, const char *str2);

`strcmp` 函数按照 ASCII 码的值逐个字符比较两个字符串。它的比较方式如下:
1. 从第一个字符开始比较,如果两个字符串的对应字符的 ASCII 码值相同,则继续比较下一个字符。
2. 如果两个字符串的对应字符的 ASCII 码值不同,`strcmp` 函数返回两个 ASCII 码值之间的差值的符号位,即如果第一个不同字符的 ASCII 码值在字符串1中大于字符串2中的对应字符,则返回正值;如果小于,则返回负值;如果相等,则继续比较下一个字符。
3. 如果在某个字符上比较结果不同,则函数停止比较并返回结果,不会等到字符串结束。
4. 如果两个字符串完全相同,`strcmp` 函数返回 `0`。
这里有一些例子来说明 `strcmp` 的比较方式:



// 比较 "hello" 和 "hello"
strcmp("hello", "hello") → 返回 0
// 比较 "hello" 和 "world"
strcmp("hello", "world") → 返回负值
// 比较 "hello" 和 "Hello" (注意大小写)
strcmp("hello", "Hello") → 返回负值
// 比较 "hello" 和 "hell" (较短的字符串)
strcmp("hello", "hell") → 返回正值

需要注意的是,`strcmp` 函数对比较的字符串大小写敏感。如果你需要进行不区分大小写的比较,可以使用 `strcasecmp` 函数(在 `<string.h>` 中定义)或者在 `strcmp` 函数中自己转换字符到小写或大写进行比较。
在使用 `strcmp` 函数时,应该注意比较的两个字符串都是以 `\0`(空字符)结尾的,因为如果两个字符串前缀相同,只有比较到字符串末尾或者发现一个字符不同时,`strcmp` 才会返回不同的结果。 

模拟实现(版本1)

这里加上const进行限制 防止进行修改

函数实现的逻辑 等于就继续 不等于就直接返回

在相等逻辑里面 进行判断 也就是同时遇见最后一个字符0 跳出循环

模拟实现(版本2)

不给予准确数值 返回就可以

模拟实现代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<string.h>
//这里采取的是两个模拟函数 也就是两个版本
int MY_strcmp(char* arr1, char* arr2)
{
	while (*arr1 == *arr2)这里代码循环的条件就是只要相等就继续++ 然后遇见最后一个字符'\0'自然会跳出循环
	{
		if (*arr1 == '\0')//遇见'\0'说明已经遇到最后一个字符了 也就可以跳出循环
			return 0;
		*arr1++; *arr2++;
	}
	return *arr1 - *arr2;//其他情况下 直接可以返回指针的差值不一定是非得是1 =1 0 这样的数值 因为每个编译器的返回数值是不一样的  我这里是vs的编译器
}
int my_strcmp(char* arr1, char* arr2)
{
	while (*arr1 == *arr2)//这里不能while(*arr1++==*arr2++)因为 这里是先++后等于 也就是 第一个元素和第二个元素的对比 所以不能这样的
	{
		if (*arr1 == '\0')//遇见'\0'说明已经遇到最后一个字符了 也就可以跳出循环
		{
			return 0;
		}
		*arr1++;//这里相等就继续++
		*arr2++;//同理
	}
	if (*arr1 > *arr2)//这里是按照*arr1也就是元素的ASCII码值进行对比的  如果大于 就返回1 小于就返回-1 不需要判断0 因为相等会继续循环
	{
		return 1;
	}
	else
	{
		return -1;
	}
}
int main()
{
	char arr1[] = "sadaasd";
	char arr2[] = "sadaasd";
	char arr3[] = "sadasd ";
	char arr4[] = "sadasde";
	int ret1 = strcmp(arr1, arr2);//这里需要说明一下 是一个一个对应 什么意思呢就是 ss 对照  aa dd aa。。。所以返回0
	int ret2 = strcmp(arr1, arr3);//ss aa dd aa as这里是a的ASCII码值比s小 所以返回-1 此时不进行下面的循环
	int ret3 = strcmp(arr4, arr3);
	//这里的逻辑对比是一一对比,如果 前面的字符大 此时也就返回1 小的情况下 也就返回-1 相等的情况下 也就是整个字符串相等 返回0 
	printf("%d\n%d\n%d\n\n", ret1, ret2, ret3);

	int ret4 = my_strcmp(arr1, arr2);
	int ret5 = my_strcmp(arr1, arr3);
	int ret6 = my_strcmp(arr4, arr3);
	printf("%d\n%d\n%d\n\n", ret4, ret5, ret6);
    //这里大家可以自己看看
	int ret7 = MY_strcmp(arr1, arr2);
	int ret8 = MY_strcmp(arr1, arr3);
	int ret9 = MY_strcmp(arr4, arr3);
	printf("%d\n%d\n%d", ret7, ret8, ret9);

}

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

strcpy(复制函数的使用和模拟)

语法讲解

`strcpy` 是一个在 C 语言中常用的字符串拷贝函数,它定义在标准库头文件 `<string.h>` 中。该函数用于将源字符串(source string)复制到目标字符串(destination string)中,直到遇到换行符、null 字符(`'\0'`),或者到达目标字符串末尾。

函数原型定义如下:
```c
char *strcpy(char *dest, const char *source);
```
参数说明:
- `dest`:指向目标字符串的指针,即要复制字符串到的位置。
- `source`:指向源字符串的指针,即要被复制的内容。

返回值:
- 函数返回指向目标字符串的指针,即 `dest`。
功能:
- 将源字符串 `source` 中的字符逐个复制到目标字符串 `dest` 中,直到遇到换行符、null 字符或目标字符串结束。
- 复制过程中,如果源字符串长度大于目标字符串的长度,则会截断源字符串;如果两者长度相同,则只有前 n-1 个字符被复制(n 为目标字符串的长度)。
- 该函数不检查目标字符串是否有足够的空间来存放源字符串的内容,因此如果目标字符串空间不足,可能会导致缓冲区溢出。

示例代码:
```c
#include <stdio.h>
#include <string.h>
int main() {
    char source[] = "Hello, World!";
    char dest[20]; // 确保这个数组足够大,能够存放 source 字符串
    // 使用 strcpy 拷贝字符串
    strcpy(dest, source);
    printf("Copied string: %s\n", dest);
    return 0;
}
```
当运行上述代码时,它会输出:
```
Copied string: Hello, World!

注意事项:
- 使用 `strcpy` 时要注意目标字符串数组的大小,以避免缓冲区溢出的问题。
- 在实际项目中,为了安全起见,更推荐使用其他的字符串拷贝函数,如 `strncpy`,它允许你指定最大拷贝的字符数,从而减少溢出的风险。
- 在某些嵌入式系统或对性能要求极高的场景中,可能不会使用标准库中的函数,而是采用更高效的实现方式。 

也就是可以理解为

strcpy(拷贝(arr2)(空),被拷贝(arr1)(有数值));

拷贝原理 

就是逐个拿下来,直到遇见最后一个字符‘\0’,然后把'\0'也拿过来

图解 

需要知道常量字符串是不能被修改的

数组就是变量

char*arr="ffsdfsd";这个是常量

strcpy的模拟

版本1

版本2

后置++ 也就是先拷贝 后++

并且这个函数的返回值是char

返回值是需要返回起始地址

模拟代码总结

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcat(char* arr1, char* arr2)
{
	assert(arr1 && arr2);//保证代码的健壮性
	char* ret = arr1;
	while (*arr1++ = *arr2++)//这里不建议*arr1++=*arr2++ 可以写成把++ 放在循环里面写 这样初学者出错的可能性比较小,这里的意思就是当把arr2里面的元素放到arr1里面的时候 此时在++直到遇见字符串0 跳出循环
	{
		;
	}
	*arr1 = *arr2;//这里是关键的一步骤 也就是把'\0'放到最后 可以看看最后我写的图解

	return ret;//这里是返回首元素的地址 返回首元素的地址进行打印
}
void  MY_strcat(char* arr1, char* arr2)
{
	assert(arr1 && arr2);//保证代码的健壮性
	while (*arr1++ = *arr2++)//这里是同理的
	{
		;
	}
	*arr1 = *arr2;//这里是关键的一步骤 也就是把'\0'放到最后
}
int main()
{
	char arr1[] = "ao;isdh;a";
	//这里是strcpy函数的使用 也就是arr2 是空的 这里需要记住 如果你要拷贝的函数占据的是10个字节 那么你拷贝到的空间 最少也得10个空间 这样才能放下这些元素
	char arr2[100] = { 0 };
	char arr3[100] = { 0 };
	char arr4[100] = { 0 };

	strcpy(arr2, arr1);
	//这里是strcpy函数的使用 也就是arr2 是空的 这里需要记住 如果你要拷贝的函数占据的是10个字节 那么你拷贝到的空间 最少也得10个空间 这样才能放下这些元素
	printf("%s \n%s\n\n", arr1, arr2);

	char* my = my_strcat(arr3, arr1);
	printf("%s \n%s\n\n", arr1, my);

	 MY_strcat(arr4, arr1);
	printf("%s \n%s\n\n", arr1, arr4);
	return 0;
}

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

strcat(拼接函数的使用和模拟)

strcat的语法

`strcat` 是 C 语言标准库中的一个字符串拼接函数,它用于将一个字符串(source)拼接到另一个字符串(destination)的末尾。该函数定义在 `<string.h>` 头文件中。
函数原型如下:

```c
char *strcat(char *dest, const char *source);
```

参数说明:
- `dest`:指向目标字符串的指针,拼接后的字符串将存储在这里。
- `source`:指向源字符串的指针,要拼接的字符串。
返回值:
- 函数返回指向目标字符串 `dest` 的指针。
功能:
- 将源字符串 `source` 的所有字符复制到目标字符串 `dest` 的末尾,直到遇到 null 字符(`'\0'`)为止。
- 在拼接过程中,如果目标字符串不以 null 字符结尾,`strcat` 会自动在其后面添加一个 null 字符,以确保拼接后的字符串以 null 字符结束。

示例代码:
```c
#include <stdio.h>
#include <string.h>
int main() {
    char dest[20] = "Hello"; // 确保这个数组足够大,能够存放拼接后的字符串
    char source[] = ", World!";
    // 使用 strcat 拼接字符串
    strcat(dest, source);
    printf("Concatenated string: %s\n", dest);
    return 0;
}
```
当运行上述代码时,它会输出:
```
Concatenated string: Hello, World!
```


注意事项:
- 与 `strcpy` 类似,`strcat` 也不会检查目标字符串数组是否有足够的空间来存放拼接后的字符串,因此同样要注意避免缓冲区溢出。
- 在实际项目中,为了安全起见,更推荐使用 `strncat` 函数,它允许你指定最大拼接的字符数,从而减少溢出的风险。
- 如果你需要在一个已存在的字符串后面添加一个特定的字符或字符串,可以使用 `strcpy` 和 `strcat` 的组合来实现。例如,如果你想在每个单词后面添加一个逗号和空格,你可以先用 `strcpy` 复制单词,然后手动添加逗号和空格。

也就是理解为

strcat(arr1(前面的函数),arr2(后面的函数))

也就是打印的 时候打印arr1就可以

而且是要给arr1 足够大的空间 这样才能保证后续工作的进行

函数来连接字符串

strcat的模拟

1 找到目标空间‘\0’

2 拷贝数据

3 目标空间可修改

所以简单的说就是先打印 再进行拷贝 两个循环

最后返回的是目标空间的起始地址,

简单的说就是

图解

库里面的参考实现代码

基本上差不多

代码版本1 

模拟实现 `strcat` 函数:
```c
char *my_strcat(char *dest, const char *source) {
    // 找到目标字符串的末尾
    char *dest_end = dest + strlen(dest);
    // 将源字符串的字符复制到目标字符串末尾
    while (*source) {
        *dest_end++ = *source++;
    }
    // 添加 null 字符,确保字符串以 null 字符结束
    *dest_end = '\0';
    // 返回指向目标字符串的指针
    return dest;
}
int main() {
    char dest[20] = "Hello";
    char source[] = ", World!";
    // 使用模拟的 my_strcat 拼接字符串
    dest = my_strcat(dest, source);
    printf("Concatenated string: %s\n", dest);
    return 0;
}
```
这个模拟实现的基本逻辑与标准库中的 `strcat` 相同,但它不依赖于标准库函数,可以作为了解字符串拼接内部实现的一种方式。

 代码版本2

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<string.h>
char* my_strcat(char* arr1, char* arr2)
{
	assert(arr1 && arr2);
	char* ret = arr1;//首元素地址 方便返回首元素地址
	while (*arr1 != '\0')*arr1++;//这里的++不能写到循环里面 因为这里的++是对比之后进行++ 导致跳出循环 也就是等于当指针等于字符0的时候此时应该跳出循环 但是 ++ 导致又不等于0 指向的是'\0'的后一位

	while (*arr1 = *arr2)
	{
		*arr1++; *arr2++;
	}
	*arr1 = *arr2;//这里也就是把'\0'放到结尾 其实放不放都可以 因为你已经完成任务 具体的目的要根据实际来进行写
	return ret;
}
int main()
{
	char arr1[100] = "slaidg ";//拼接的时候 需要注意 这里是常量字符创 也就无法改变空间的 所以 这里arr1的空间 要比arr2的空间大才行 不然会导致程序崩溃
	char arr2[] = "dfilsug ";
	strcat(arr1, arr2);
	printf("%s\n\n", arr1);

	char arr3[] = "dfilsug ";
	my_strcat(arr1, arr3);
	printf("%s\n\n", arr1);

	return 0;
}

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

strncmp strncat strncpy长度受限制的字符串函数

首先 我们需要知道 这几个的语法格式差不多 

这里传递的size_t的长度是传递的字节长度 不是个数 也就这里int*是四个字节 char*是一个字节 如果是整数进行交换 。此时也就需要20个字节,这样可以交换五个整数

这里差异只是多一个参数 也就是size_t sz长度参数

strncpy(拷贝)

这里只是多一个字节长度

这里拷贝三个

这里是让拷贝几个就拷贝几个 没有’\0‘

这里拷贝8个 这里是不足八个 加上'\0'

strncpy代码的模拟实现

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<string.h>
void my_strncpy(char* str1, char* str2, size_t sz)
{
	assert(str1 && str2);
	while (sz > 0)//这里是字节交换 每次sz会进行减少一个字节
	{
		*str1 = *str2;//这里进行复制
		str1++; //各自向后移动
		str2++;
		sz--;//字节每次减少1 
	}
}
int main()
{
	char arr1[] = "dfhguil";
	char arr2[100] = { 0 };
	my_strncpy(arr2, arr1, 3);
	printf("%s", arr2);
	return 0;
}

strncat(拼接)

举例1

这里是追加三个字节

举例2

这里是追加3个 从前往后走进行追加 把该追加的追加之后 会放一个字符串

举例3

把该追加的追加之后 加上字符串0 之后就不继续添加了

strncat的模拟实现

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<string.h>
void my_strncat(char* str1, char* str2, size_t sz)
{
	assert(str1 && str2);//这里使用一个断言 也就是只要不是空指针 就可以继续运行

	while (*str1 != '\0')//这里是先找到第一个数组的'\0'然后跳出循环
		str1++;

	while (sz != 0)//其次和之前的循环方式一样 每次减少一个字节 ++ 把数值赋给arr1 但是这里需要知道 这里是常量字符串 所以需要arr1 的空间大于arr2
	{
		*str1 = *str2;
		str1++;
		str2++;
		sz--;
	}
	*str1 = '\0';//最后是把'\0'放到字符串的结尾
}
int main()
{
	char arr1[100] = "hello ";
	char arr2[] = "bitttt";
	my_strncat(arr1, arr2, 3);
	printf("%s", arr1);
	return 0;
}

strncmp(比较)

这里是比较前两个字节  

在给定 的长度范围内进行比较

这里你让我拷贝 我就进行拷贝 存在越界访问的情况

这里拷贝出来 但是程序崩溃了

strncmp的模拟实现

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<string.h>
char* my_strcmp(char* str1, char* str2, size_t sz)
{
	assert(str1 && str2);

	while (*str1 == *str2)//这里代码循环的条件就是只要相等就继续++ 然后遇见最后一个字符'\0'自然会跳出循环
	{
		if (sz == 0)//这里每次循环的时候会进行sz的-- 也就是对照字符的-- 如果不满足相等的条件 此时也就会跳出循环
		{
			return 0;
		}
		str1++; str2++;
		sz--;
	}
	return *str1 - *str2;

	//while (*str1 == *str2 && sz > 0)
	//{

	//	str1++; 
	//	str2++;
	//	sz--;
	//}
	//return sz == 0 ? 0:(*str1 - *str2);//这里是使用一个三目操作符 如果满足sz循环 也就是返回0  不然就是返回差值 这样也是允许的

}

int main()
{
	char arr1[100] = "hello ";
	char arr2[] = "bitttt";
	int ret = my_strcmp(arr1, arr2, 3);
	printf("%d", ret);
	return 0;
}

 

代码使用的总结

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<string.h>
int main()
{
	//拼接限制函数的使用  这里拼接三个
	char arr1[100] = "slaidg ";
	char arr2[] = "dfilsug ";
	strncat(arr1, arr2, 3);
	printf("%s\n\n", arr1);

	//拷贝限制函数的使用 这里拷贝五个
	char arr3[] = "slaidg ";
	char arr4[100] = { 0 };
	strncat(arr4, arr3, 5);
	printf("%s\n\n", arr4);


	//比较函数的使用
	char arr5[] = "slaidg ";
	char arr6[] = "dfilsug ";
	char arr7[] = "slaidg ";
	int ret1 = strcmp(arr5, arr6, 3);//不相同 1   前三位比较的时候上面的大 arr5大
	int ret2 = strcmp(arr5, arr7, 3);//相同   0   前三位一样大
	printf("%d\n%d\n", ret1, ret2);
}

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

strstr(字符串里查找字符串)

语法格式

库函数实现的逻辑 

1,返回一个指向str2在str1中第一次出现的位置,如果str2不是p,则返回一个空指针,函数返回字符串str2在字符串str1中第一次出现的位置)

2,匹配过程不包括终止的空字符,但它将停止

举例

这里是arr1里面有没有出现p这个字符

这里是找到了

举例2

没有找到

返回类型是char*

接收数值来一个

在(1,3)

1里面寻找有没有3

模拟strstrhanhsu

这里是不希望被修改的 所以加上const 这样就不能修改了

这里我们分析查找的时候的多种情况

第一种情况的实现

第二种情况的实现

这里需要总有一个指针指向初始的位置

一个指针记录到哪里

还有一个指针记住从哪开始匹配

这里进行比较 不相等 cur向后走一步

也就是开始尝试匹配 这个需要一个指针进行记录 这里的记录指针是不动的

这里是对比的函数里面是不遇见0 的

但是这里需要知道的是 *s2 等于\0的时候 也就找完了(找得到)

最后找不到 也就是遍历结束的时候 跳出循环 所以 此时也就返回空指针

但是这还需要知道 如果arrr2是空字符串(进行特殊场景处理)但是需要强制类型转化 (因为是空指针)

在循环条件里面已经限制arr1比arr2小了

上述的模拟是暴力查找 也就是不讲究效率的问题

这里说明一下 这里虽然初始化为空指针 但是没有对他使用 所以就相当于一条野狗先拴住 不靠近 用的时候再进行赋值

kmp算法(解决的也是字符串里面寻找字符串)(实现和理解更复杂)(有兴趣研究一下)

strstr函数模拟的实现

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
//strstr函数的模拟
char* my_strstr(const char* arr1, const char* arr2)
{
	assert(arr1 && arr2);//这里防止是传参过来的是空指针 进行一个断言
	const char* s1 = NULL;
	const char* s2 = NULL;
	//这里需要创建两个指针变量 一个负责指向开始的位置 一个负责移动如果不满足条件的情况下 需要返回新的循环继续开始循环 直到遇见 或者循环结束
	
	if (*arr2 == '\0')//这里首先判断是不是直接传过来空的字符 是的话 直接返回
		return (char*)arr1;//这里是进行一个强制类型转化 因为 const是
	//对于const存在的变量,通常不需要进行显式的类型转换,因为它的值是固定的。
	// 然而,如果你需要将const变量用于不同的类型或者接口,你可能需要进行类型转换。
	// 这通常涉及到将const变量指向的内存地址转换为其他类型指针,或者将它的值转换为其他格式或类型。
	
	while (*arr1)//这里首先是指向数组 也就是只要数组循环不结束 也就一直寻找 下面会进行数组首元素的++ 这里解应用是因为补解应用就是地址 我们是寻找元素的
	{
		s1 = arr1;//这里进行赋值  把之前的空指针进行等于两个指针变量,也就是此时也就可以满足一个进行移动 一个进行计数 到那个位置了
		s2 = arr2;//这里进行赋值  把之前的空指针进行等于两个指针变量,
		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)//这里的循环条件是 只要每次对比的一样 并且不遇见最后一个字符'\0'就继续循环
		{
			s1++;//这里如果 s2 是三个字符 也就是循环三次 三次结束没有找到 s2也就是也就不等于s1了 此时也就跳出循环了
			s2++;//然后 此时*arr1原本指向的字符如果是首元素 s1循环结束之后指向的是首元素+3个字符 这个时候没有满足条件 然后s1这个时候也就没有什么作用了 重新赋值所以arr++ 也就变成第二个元素 重新进行计算
		}
		if (*s2 == '\0')//这里是当对比字符串循环结束的时候 说明找到了元素 也就可以进行返回数值了
		{
			return (char*)arr1;//这里是找到这个元素所在位置 然后进行强制转化 把数值传递过去 传递的是开始找到的元素的位置 然后打印
		}
		arr1++;//这里是训话每次结束之后 进行++ 也就是元素向后移动一位 集训进行对比
	}
	return NULL;//如果最后循环结束 还没找到 此时也就返回空指针


}
//strstr函数的使用和模拟
int main()
{
	char arr[] = "a b bbc def\n";
	const char *p1 = "bbc";
	char* ret = strstr(arr, p1);//这里是一个strstr函数的使用
	if (p1==NULL)
		printf("没找到。\n");
	else
		printf("找到了:%s\n", ret);


	char arr1[] = "abbbcdef\n";
	const char* p2 = "bbc";
	char* re = my_strstr(arr1, p2);
	if (p2 == NULL)
		printf("没找到。");
	else
		printf("找到了:%s\n", re);

	printf("找到了:%s\n", arr1);



	return 0;
}

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

strtok(切片的使用)

使用规则

使用的基本情况

strcpy

第二次调用的时候传的是空指针

所以打印出来的是

每一次调用函数都会把当前函数的地址记住

所以二次调用的时候 传的是null 连起始位置都不传了 只是传null

但是需要知道的是 当知道三段 你调用第四段的时候 那个时候第三段已经进行销毁 所以这里第四段会返回空指针

升级版本

讲解,这里调用strtok(arr(原始数组),sep(去掉的字符))

这里只要遇见的不是null 也就是最后一步之前 都不是null 所以 切片成功

这里for循环二次调用一下

代码的使用

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "shdphasuid\0as;k\0hjsdab";
	const char* p = "@.";
	strtok(arr1, p);//这个函数的特性就是 一次只打印一段
	printf("%s\n\n\n\n\n", arr1);


	char* ret = NULL;
	char arr2[] = "shdphasuid@ask@hjsdab";
	ret = strtok(arr2, p);
	printf("1 %s\n", ret);

	ret=strtok(NULL, p);
	printf("2 %s\n", ret);

	ret=strtok(NULL, p);
	printf("3 %s\n", ret);//这里需要知道的是 二次接受的时候需要接受空指针 当空指针运行结束的时候 这里也就是三段 三次 结束之后 空指针也就是没了

	ret = strtok(NULL, p);
	printf("4 %s\n\n\n\n\n", ret);//此时显示的是空指针

	printf("strtok函数的循环使用:\n");
	char arr3[] = "shdphasuid@ask@hjsdab";//这里巧妙的运用循环 也就是利用函数的特性 第一次需要传参数arr3 后面是null 只要不等于null那么久一直循环
	for (ret = strtok(arr3, p); ret != NULL; ret = strtok(NULL, p))
	{
		printf("%s ", ret);
	}

}

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

strerror(打印错误信息)和perror(获得错误信息) 

strerror

语法格式

返回类型是char*

都需要头文件 errno.h

这里是错误码 每一个错误码代表一个错误信息

错误码

对照的错误信息

每一种编译器在写的时候已经规定好了 错误码对应的错误信息都是什么

strerror把错误信息打印出来

插入

打开文件的代码

fopen

这里是fopen打开这个文件

当打开失败的时候会显示(返回)null

所以我们接受的如果是null 让他打印错误码

需要头文件 errno.h

打开失败的原因 没有这个文件 或者这个文件夹

这里是把错误码翻译成错误信息

perror

语法格式

返回类型是char*

都需要头文件 errno.h

这个函数的库函数

这里依旧会打印错误信息

perror是有能力直接打印错误信息的

所以perror相当于

这里不能不写 因为这里传参是需要一个字符串的 所以必须传一个字符串

哪怕是空字符串

但是空指针是不行的

代码的使用

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<errno.h>
//这里进行解释一下 这个函数是打印错误信息的函数 
int main()
{
	//这里是打印出错误信息
	for (int i = 0; i < 10; i++)//这里举例是循环打印出前十个错误信息 这里错误信息的头文件是error 使用函数是strerror()
	{
		printf("%d:       >%s\n", i, strerror(i));
	}


	//判断出错误举例
	FILE* PF = fopen("dsfsfdhgsaf", "r");//这里fopen是一个打开文件的函数 这里打开的是一个空文件 这里不是讲解的主要内容
	if (PF == NULL)
	{
		printf("1 错误信息>%s\n", strerror(errno));//这里可以把strerror(errno)这里理解为是打印错误信息的固定代码
		
		perror("2 错误信息>");//这里是和printf类似于 这里是一个打印错误信息的函数 他等同于错误信息1 但是这里需要知道的是 这个
		//perror里面需要传递一个参数 因为这里是一个函数 函数的标准是需要接受一个文件的 传递一个空格也是可以的 

		return 1;//打印完错误信息返回程序
	}
	//fclose(PF);//这里的意思是关闭打开的文件 也不是讲解的重点

	return 0;
}

 错误信息找不到文件

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

 0 的理解

绿色的本质都是0

  • 54
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值