🌈write in front :
🔍个人主页 : @啊森要自信的主页
✏️真正相信奇迹的家伙,本身和奇迹一样了不起啊!
欢迎大家关注🔍点赞👍收藏⭐️留言📝>希望看完我的文章对你有小小的帮助,如有错误,可以指出,让我们一起探讨学习交流,一起加油鸭。
文章目录
- 📝前言
- 🌠 库函数strncpy
- 🌉strncpy 模拟实现
- 🌠strncat 函数的使⽤
- 🌉strncat 模拟实现
- 🌠strncmp函数的使⽤
- 🌉strncmp模拟实现
- 🌠strerror
- 🌉 perror
- 🚩总结
📝前言
本小节,阿森继续和你一起学习5个字符串函数:strncpy
,strcnat
,strncmp
的使用和两种模拟实现方法,他们和strcpy
等函数比较多了一个n
,实现方法有很大区别,还有strerror
和perror
的使用,学习这些库函数,可以更好的方便操作字符和字符串,文章干货满满,接下来我们就学习一下这些函数吧!
strcpy
、strcat
这类函数不安全,因为它们在复制字符串时不检查目标缓冲区的大小,可能会导致缓冲区溢出。
而strncpy
、strncat
、strncmp
这类函数相对来说更安全,因为它们在复制/追加字符串时会限定最大长度参数n
,避免无限制地写入目标缓冲区。
点击—>手把手教你配置VS的常见函数如何不报错!
🌠 库函数strncpy
strncpy函数用于将一个字符串拷贝到另一个字符串中,可以限定拷贝的字符数。
函数原型:
char * strncpy ( char * destination, const char * source, size_t num );
dest - 目标字符串,用于接收拷贝内容。
src - 源字符串,从中拷贝内容。
num - 要拷贝的字符数。
返回值:
返回目标字符串dest
的指针。
注意点:
-
检查
dest
空间是否足以容纳src
的n
个字符及结尾'\0'
。strncpy
不会检查dest
的长度,如果dest
空间不足可能会导致缓冲区溢出。 -
拷⻉
num
个字符从源字符串到⽬标空间。拷贝num
个就num
个,不会拷贝多,也不会自己添加\0。
-
如果源字符串的⻓度⼩于
num
,则拷⻉完源字符串之后,在⽬标的后边追加0
,直到num
个。
例子:
# define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "Hello World";
char str2[7];
str2[5] = '\0';
strncpy(str2, str1, 5);
printf("str1: %s\n", str1);
printf("str2: %s\n", str2);
return 0;
}
输出:
str1: Hello World
str2: Hello
🌉strncpy 模拟实现
对于strncpy函数,阿森给你带来了两种模拟实现方法,详解如下:
- 主函数(两种模拟实现都可以用这个进行测试)
int main()
{
char dest[20]="xxxxxxxxxxxxxxxxx";
char src[] = "hello";
size_t set = strlen(src);
printf("%d\n", set);
my_strncpy(dest, src, 3);
printf("%s\n", dest);
my_strncpy(dest, src, 9);
printf("%s\n", dest);
return 0;
}
- 用数组模拟实现
char* my_strncpy(char* dest, const char* src, size_t n)
{
if (dest == NULL || src == NULL) //检查dest和src参数是否合法,如果任意一个为NULL则直接返回NULL。
{
return NULL;
}
char* result = dest;// 保存dest的地址值,后面返回时使用
size_t i;
for ( i = 0; i < n && src[i] != '\0'; i++) //使用for循环复制字符。
{ // i < n判断是否已经复制n个字符
dest[i] = src[i]; // src[i] != '\0' 判断当前源字符串字符是否结束判断是否已经复制n个字符
}//复制源字符串当前字符到目标字符串
// 添加'\0'填充 (如果源字符串的⻓度⼩于`num`,则拷⻉完源字符串之后,在⽬标的后边追加`0`,直到`num`个。)
while (i < n) 如果for循环结束但i未达到n,使用while循环填充'\0'。
{
dest[i++] = '\0';//将目标字符串当前位置字符填充为'\0'
} //dest[i++] = '\0'先dest[i]='\0',后i++
return result;
}
输出:
5
helxxxxxxxxxxxxxx
hello
调试界面:
- 用指针实现
char* my_strncpy(char* dest, const char* src, size_t n)
{
assert(dest);//利用断言需要使用头文件#include<assert.h>
assert(src);
char* destPtr = dest;//定义dest和src的指针变量destPtr和srcPtr,用于遍历字符串。
const char* srcPtr = src;
while (n-- > 0) //使用while循环遍历n个字符
{
if (*srcPtr != '\0') //检查当前源字符串srcPtr指向的字符是否为'\0'结束符
{
*destPtr++ = *srcPtr++;//如果不是结束符,就将源字符串当前字符复制到目标字符串,
} //并同时将两个指针前移到下一个字符。
else //如果是结束符,进入else块
{
*destPtr++ = '\0';//将目标字符串当前字符设置为结束符'\0'
} //然后destPtr再++
}
return dest; //返回目标字符串首地址。
}
输出:
*destPtr++ = *srcPtr++
先进行一次赋值(*dest = *src
),然后并使指针后移(dest=dest+1
,src=src+1
)
*destPtr++ = ‘\0’将目标字符串当前字符设置为结束符'\0'
,然后destPtr
再++
🌠strncat 函数的使⽤
strncat函数用于连接两个字符串,将源字符串src
连接到目标字符串dest
的结尾,最多连接n
个字符。
strncat函数的原型:
char *strncat(char *dest, const char *src, size_t n);
dest:目标字符串,其内容将在其后追加源字符串内容。
src:源字符串,其内容将被追加到目标字符串结尾。
n:要从源字符串中追加到目标字符串中的最大字符数。
- 返回值:
函数返回目标字符串dest
的指针。
例子:
#include <string.h>
int main()
{
char dest[100] = "Hello";
char src[] = " World";
strncat(dest, src, 6);
printf("%s\n", dest);
}
输出:
输出 Hello World
🌉strncat 模拟实现
- 主函数
int main()
{
char str1[100] = "hello";
char str2[100] = " world";
my_strncat(str1, str2, 5);
printf("%s\n", str1);
return 0;
}
- 用数组模拟实现
char* my_strncat(char* dst, const char* src, size_t n)
{
char* tmp = dst;
while (*dst)//使用while循环遍历dst字符串。
{
dst++;//找到字符串结束位置'\0'。
}
int i;
for (i = 0; src[i] && i < n; i++)// i < n 判断是否超过最大复制长度n
{ //src[i] 判断源字符串是否结束
dst[i] = src[i];
}
dst[i] = 0;//在目标字符串末尾添加字符串结束标记'\0'。
return tmp;
}
输出:
hello worl
- 用指针实现
char* my_strncat(char* dest, const char* src, size_t n)
{
//参数检查
if (dest == NULL || src == NULL)
{
return NULL;
}
char* ptr = dest;
//找到目标字符串结尾
while (*dest != '\0')
{
dest++;
}
while (n-- > 0 && *src != '\0')
{
*dest++ = *src++;
}
*dest = '\0';
return ptr;//添加字符串结束符
}
输出:
首先,
n--
表示先使用n
的值来进行比较是否>0
,因为&&
是逻辑与运算符,*src != ‘\0’ 表示判断指针src
所指向的字符是否为字符串的结束符\0
,这两个条件验证真假后,最后n
的值才减1
。
🌠strncmp函数的使⽤
strncmp用于比较两个字符串的前n
个字符。(比较的不是字符串的长度无关,只与对应位置的字符内容有关。)
strncmp
函数原型:
int strncmp(const char *str1, const char *str2, size_t n);
str1 - 要比较的第一个字符串的指针
str2 - 要比较的第二个字符串的指针
n - 将被比较的最大字符数
返回值:
- 如果
str1
小于str2
,返回值小于0
- 如果
str1
大于str2
,返回值大于0
- 如果
str1
等于str2,返回值等于0
注意点:
-
如果
n
的值大于两个字符串中任意一个字符串的长度,比较将会超出字符串的范围,可能导致内存访问错误。因此,在使用strncmp
函数时,需要确保n
的值不会超过任意一个字符串的长度。 -
strncmp
函数返回的结果是一个整数,可以通过结果的正负值来判断两个字符串的大小关系。 -
比较规则与
strcmp
函数一致,按ASCII
码顺序比较每个字符。
使用示例:
int main()
{
char str1[] = "hello";//注意字符串结尾后面还有\0
char str2[] = "hello world";
int result1 = strncmp(str1, str2, 5);
// 只比较前5个字符,结果为0,表示相等
printf("%d\n", result1);
int result2 = strncmp(str1, str2, 6);
// 比较前6个字符,结果为负数,表示str1小于str2
printf("%d\n", result2);
}
输出:
🌉strncmp模拟实现
int my_strncmp(const char* s1, const char* s2, size_t n)
{
int i = 0;
//这是一个 for 循环,用于迭代比较两个字符串中的字符。
for (; i < n && s1[i] != '\0' && s2[i] != '\0'; i++)
{
if (s1[i] != s2[i])
{
return s1[i] - s2[i];//如果当前位置的两个字符不相等,返回它们的差值。
}
}
if (i <= n)
{
return s1[i] - s2[i];//如果 i 小于等于 n,但是循环结束了(即至少一个字符串已经达到结束符 ‘\0’),则返回当前位置字符的差值。
}
return 0;
}
int main()
{
char s1[] = "hello";
char s2[] = "helloworld";
int result = my_strncmp(s1, s2, 5);
printf("result = %d\n", result);
return 0;
}
运行:
监视:
图解:
🌠strerror
错误码 | 错误描述 |
---|---|
0 | No error |
1 | Operation not permitted |
2 | No such file or directory |
3 | No such process |
4 | Interrupted function call |
5 | Input/output error |
6 | No such device or address |
7 | Arg list too long |
8 | Exec format error |
9 | Bad file descriptor |
strerror函数用于将错误码转换为对应的错误信息字符串。
函数原型如下:
char *strerror(int errnum);
errnum: 错误码号,通常是系统调用或库函数返回的错误号。
strerror
函数接受一个整型参数errnum
,表示错误码。它会返回一个指向错误信息字符串的指针。
注意点:
- 在不同的系统和C语⾔标准库的实现中都规定了⼀些错误码,⼀般是放在
errno.h
这个头⽂件中#include <errno.h> - C语⾔程序启动的时候就会使⽤⼀个全⾯的变量
errno
来记录程序的当前错误码,只不过程序启动的时候errno
是0
,表⽰没有错误。 - 当我们在使⽤标准库中的函数的时候发⽣了某种错误,就会讲对应
的错误码,存放在errno
中 - 以每⼀个错误码都是有对应的错误信息的
- strerror函数返回的是一个静态字符串指针,不需要手动释放内存。
举栗子:
#include <errno.h>
#include <string.h>
#include <stdio.h>
int main()
{
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d: %s\n",i, strerror(i));
}
return 0;
}
输出:
如何使用strerror
函数打印打开文件失败的错误信息:
int main()
{
FILE* pFile;
pFile = fopen("unexist.txt", "r");//使用fopen函数打开文件"unexist.txt",以只读方式打开。
if (pFile == NULL)//判断打开结果pFile是否为NULL,NULL表示打开失败。
printf("Error opening file unexist.ent: %s\n", strerror(errno));
else
printf("打开文件成功\n");
return 0;
}
输出:
分析:
Error opening file unexist.ent: No such file or directory
说明打开文件"unexist.txt"
失败,失败原因是文件不存在(ENOENT错误码)。
如果加上"unexist.txt"
该文件,就会显示打开成功!
🌉 perror
perror
函数用于打印错误信息。它的功能与strerror
函数类似,但打印方式不同。
perror函数原型:
void perror(const char *s);
s: 可选的错误前缀信息。
简意:
perror
函数直接打印到标准错误输出,打印完参数部分的字符串后,再打印⼀个冒号和⼀个空格,再打印错误信息。(此代码结果为下面代码运行)
详解:
将errno
设置的错误号转换为错误描述字符串,然后打印到标准错误输出stderr上。如果s
不为空,则在错误描述前加上s
后跟 冒号 ":"
。(stderr
是预定义的一个文件输出流,它用于输出错误和诊断信息。stderr
默认连接到控制台,输出到屏幕。所以向stderr
输出的信息直接打印在屏幕上。)
使用perro
r函数需要包含错误头文件errno.h。
栗子:
int main()
{
FILE* pFile;
pFile = fopen("unexist.txt", "r");
if (pFile == NULL)
//printf("Error opening file unexist.ent: %s\n", strerror(errno));
perror("Error opening file:");
else
printf("打开文件成功\n");
return 0;
}
运行结果:
🚩总结
这次阿森和你一起学习6个C语言中常用的基本字符操作函数,但阿森会慢慢和你一起学习。感谢你的收看,如果文章有错误,可以指出,我不胜感激,让我们一起学习交流,如果文章可以给你一个小小帮助,可以给博主点一个小小的赞😘