D14—C语言字符与字符串函数深度解析:从使用到内核原理
本文全面介绍了C语言中常用的字符串处理函数,包括字符分类/转换函数(islower、toupper等)、字符串操作函数(strlen、strcpy、strcat、strcmp)及其安全版本(strncpy、strncat、strncmp),以及字符串查找函数strstr和分割函数strtok。文章不仅详细讲解每个函数的基本用法和注意事项,还提供了模拟实现的代码示例。此外,还介绍了错误处理函数str
6.strncpy、strncat和strncmp的基本概念
前言
字符串处理是C语言编程中的核心技能之一。尽管C语言没有内置的字符串类型,但通过标准库提供的一系列函数,我们可以高效、灵活地处理文本数据。本文将深入探讨C语言字符串处理的方方面面,不仅涵盖基本函数使用,还会揭示常见陷阱、性能考虑和实际应用场景。
1. 字符分类、转换函数基本用法
1.1 字符分类函数基本概念与用法
C语言中有一系列的函数是专门做字符分类的,也就是⼀个字符是属于什么类型的字符的。 这些函数的使⽤都需要包含一个头文件是 ctype.h。

int islower ( int c );
islower 是能够判断参数部分的 c 是否是小写字母的。 通过返回值来说明是否是小写字母,如果是小写字母就返回非0的整数,如果不是小写字母,则返回0。
那我们可以利用这个函数做一个小练习,写⼀个代码,将字符串中的小写字⺟转⼤写,其他字符不变。
#include<stdio.h>
#include<ctype.h>
int main()
{
char s1[] = "I love C";
char* p = s1;
while (*p)
{
if (islower(*p))
{
*p -= 32;
}
p++;
}
printf("%s", s1);
return 0;
}
1.2 字符转换函数基本概念与用法
int tolower ( int c ); //将参数传进去的⼤写字⺟转⼩写
int toupper ( int c ); //将参数传进去的⼩写字⺟转⼤写
#include<stdio.h>
#include<ctype.h>
int main()
{
char s1[] = "I love C";
char* p = s1;
while (*p)
{
if (islower(*p))
{
*p=toupper(*p);
}
p++;
}
printf("%s", s1);
return 0;
}
2.strlen基本概念与模拟实现
2.1 初步认识与使用strlen函数
size_t strlen ( const char * str );
注意事项:
字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前⾯出现的字符个数(不包含 '\0' )。
参数指向的字符串必须要以 '\0' 结束。
注意函数的返回值为 size_t,是⽆符号的( 易错 )。
strlen的使⽤需要包含头⽂件(string.h)。
用法示例:
#include<stdio.h>
#include<string.h>
int main()
{
char s[] = "hello world";
printf("%zd", strlen(s));
return 0;
}
2.2 strlen函数的模拟实现
在这里给出三种方法:
方法一:指针-指针
#include<stdio.h>
int my_strlen(const char* s)
{
char *p=(char*)s;
while (*s != '\0')
{
s++;
}
return s-p;
}
int main()
{
char s[] = "I love C" ;
printf("%d",my_strlen(s));
return 0;
}
方法二:创建计数器,遍历整个字符数组
#include<stdio.h>
int my_strlen(const char* s)
{
int count = 0;
while (*s != '\0')
{
count++;
s++;
}
return count;
}
int main()
{
char s[] = "I love C" ;
printf("%d",my_strlen(s));
return 0;
}
方法三:递归
#include<stdio.h>
int my_strlen(const char* s)
{
if (*s == '\0')
{
return 0;
}
else
{
return 1 + my_strlen(s + 1);
}
}
int main()
{
char s[] = "I love C";
printf("%d",my_strlen(s));
}
3.strcpy函数的基本概念与模拟实现
3.1 初步认识strcpy函数
char* strcpy(char * destination, const char * source );
其实就是把一个字符串复制到另一个字符串上,得到完全相同的两个字符串。
注意事项:
源字符串必须以 '\0' 结束。
会将源字符串中的 '\0' 拷贝到目标空间。
⽬标空间必须⾜够⼤,以确保能存放源字符串。
⽬标空间必须可修改。
基本用法:
#include<stdio.h>
#include<string.h>
int main()
{
char s[] = "*****************";
char s2[] = "asdfghj";
strcpy(s, s2);
printf("%s", s);
}
我们调试时发现,s2中的'\0'也会被复制过来。
3.2 strcpy函数的模拟实现
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dst, const char* src)
{
char* ret=dst;
assert(dst != NULL && src != NULL);
/*while (*dst++ = *src++)
;
return ret;*/
while (*src != '\0')
{
*dst++ = *src++;
}
*dst = '\0';
return ret;
}
int main()
{
char s1[] = "I love C";
char s2[50] = { 0 };
char* ret=my_strcpy(s2, s1);
printf("%s", ret);
}
4.strcat的基本概念与模拟实现
4.1 初步认识strcat

其实就是在目标字符从'\0'开始,把源字符串给复制上去。
源字符串必须以 '\0' 结束。
目标字符串中也得有 \0 ,否则没办法知道追加从哪里开始。
目标空间必须有⾜够的⼤,能容纳下源字符串的内容。
目标空间必须可修改。
代码实现:
#include<stdio.h>
#include<string.h>
int main()
{
char s1[100] = "I love C " ;
char s2[] = "and I am learning C";
printf("%s",strcat(s1, s2));
return 0;
}
4.2 strcat的模拟实现
#include<stdio.h>
#include<assert.h>
char* my_strcat( char* s1, const char* s2)
{
assert(s1 && s2 != NULL);
char* ret = s1;
while (*s1!='\0')
s1++;
while ((*s1++ = *s2++))
;
return ret;
}
int main()
{
char s1[100] = "I love C " ;
char s2[] = "and I am learning C";
printf("%s",my_strcat(s1, s2));
return 0;
}
5.strcmp的基本概念与模拟实现
5.1 初步认识strcmp

就是对两个字符串进行大小的比较,比较规则:
第⼀个字符串大于第⼆个字符串,则返回大于0的数字
第⼀个字符串等于第⼆个字符串,则返回0
第⼀个字符串小于第⼆个字符串,则返回小于0的数字
那么如何判断两个字符串? 比较两个字符串中对应位置上字符ASCII码值的大小。
代码示例:
#include<stdio.h>
#include<string.h>
int main()
{
char s1[100] = "I love C " ;
char s2[] = "and I am learning C";
int ret = strcmp(s1, s2);
if (ret < 0)
{
printf("%s is less than %s\n", s1, s2);
}
else if (ret > 0)
{
printf("%s is greater than %s\n", s1, s2);
}
else
{
printf("%s is equal to %s\n", s1, s2);
}
return 0;
}
5.2 strcmp的模拟实现
#include<stdio.h>
int my_strcmp(char* s1, char* s2)
{
while (*s1 && *s2)
{
if (*s1 != *s2)
{
return *s1 - *s2;
}
}
}
int main()
{
char s1[100] = "I love C " ;
char s2[] = "and I am learning C";
int ret = my_strcmp(s1, s2);
if (ret < 0)
{
printf("%s is less than %s\n", s1, s2);
}
else if (ret > 0)
{
printf("%s is greater than %s\n", s1, s2);
}
else
{
printf("%s is equal to %s\n", s1, s2);
}
return 0;
}
6.strncpy、strncat和strncmp的基本概念
6.1 初步认识strncpy

规则:
拷贝num个字符从源字符串到目标空间。
如果源字符串的长度小于num,则拷贝完源字符串之后,在⽬标的后边追加0,直到num个。
6.2 初步认识strncat

#include <stdio.h>
#include <string.h>
int main ()
{
char str1[20];
char str2[20];
strcpy (str1,"To be ");
strcpy (str2,"or not to be");
strncat (str1, str2, 6);
printf("%s\n", str1);
return 0;
}
6.3 初步认识strncmp
int strncmp ( const char * str1, const char * str2, size_t num );
⽐较str1和str2的前num个字符,如果相等就继续往后⽐较,最多比较num个字⺟,如果提前发现不⼀样,就提前结束,大的字符所在的字符串大于另外⼀个。如果num个字符都相等,就是相等返回0。
总结,其实就是多加了一个参数,对字符串大小进行了限制,增强了安全性。
7.认识strstr函数与模拟实现
7.1 初步认识strstr函数

功能就是在str1中查找str2,如果不存在就返回空指针,否则返回str2在str1中首次出现的指针。
功能示例:
#include<stdio.h>
#include<string.h>
int main()
{
char s1[] = "asdfghjkl";
char s2[] = "fghjkl";
char s3[] = "fkl";
printf("%s\n", strstr(s1, s2));
printf("%s", strstr(s1, s3));
return 0;
}
7.2 strstr函数的模拟实现
#include<stdio.h>
char* my_strstr(const char* s1, const char* s2)
{
if (!s1 || !s2)
{
return NULL;
}
char* p1=NULL;
char* p2=NULL;
char* curr = s1;
while (*curr)
{
p1 = curr;
p2 = s2;
while (*p1 && *p2 && (*p1 == *p2))
{
p1++;
p2++;
}
if (*p2 == '\0')
{
return curr;
}
curr++;
}
return NULL;
}
int main()
{
char s1[] = "hello world";
char s2[] = "world";
printf("%s",my_strstr(s1, s2));
return 0;
}
这里直接采取暴力搜索的方法,全部遍历,一个一个比对。
8.strtok的使用
char * strtok ( char * str, const char * sep);
1.sep参数指向⼀个字符串,定义了用作分隔符的字符集合
2.第⼀个参数指定⼀个字符串,它包含了0个或者多个由sep字符串中⼀个或者多个分隔符分割的标
记。
3.strtok函数找到str中的下⼀个标记,并将其用 \0 结尾,返回⼀个指向这个标记的指针。(注:
strtok函数会改变被操作的字符串,所以被strtok函数切分的字符串⼀般都是临时拷贝的内容并且
可修改。)
4.strtok函数的第⼀个参数不为 NULL ,函数将找到str中第⼀个标记,strtok函数将保存它在字符串
中的位置。
5.strtok函数的第⼀个参数为 NULL ,函数将在同⼀个字符串中被保存的位置开始,查找下⼀个标
记。
6.如字符串中不存在更多的标记,则返回 NULL 指针。
#include<stdio.h>
#include<string.h>
int main()
{
char s[]="1234.567.890";
char* sep=".";
char* str = NULL;
for (str = strtok(s, sep);str !=NULL;str = strtok(NULL, sep))
{
printf("%s\n", str);
}
return 0;
}
9.strerror的使用
char* strerror ( int errnum );
strerror 函数可以把参数部分错误码对应的错误信息的字符串地址返回来。
在不同的系统和C语言标准库的实现中都规定了⼀些错误码,⼀般是放在 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("%s\n", strerror(i));
}
return 0;
}
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{
FILE * pFile;
pFile = fopen ("unexist.ent","r");
if (pFile == NULL)
printf ("Error opening file unexist.ent: %s\n", strerror(errno));
return 0;
}

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{
FILE * pFile;
pFile = fopen ("unexist.ent","r");
if (pFile == NULL)
perror("Error opening file unexist.ent");
return 0;
}

总结:
C语言字符串处理是每个C程序员必须掌握的核心技能。从基本的strcpy、strcat、strcmp到更安全的strncpy、strncat、strncmp,再到高级的strstr、strtok和错误处理函数strerror,我们不仅学习了这些函数的使用方法,还深入探讨了它们的模拟实现、常见陷阱和性能优化。
记住,C语言字符串处理的关键在于安全和效率。始终注意缓冲区边界,确保字符串以\0结尾,避免缓冲区溢出和未定义行为。同时,根据应用场景选择合适的函数和算法,避免不必要的性能损耗。
希望本文能帮助你更好地理解和应用C语言的字符串函数,写出更安全、高效、可靠的代码。实践出真知,多写代码,多思考,你一定会成为字符串处理的高手!
更多推荐

所有评论(0)