这篇博客将会简单整理并重点介绍处理**字符和各类字符串的库函数的使用和注意事项,**如果你觉得多少对你有点帮助,可以点赞收藏支持一波哦,欢迎大佬批评指正!!!
零. 前言
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。
字符串常量适用于那些对它不做修改的字符串函数。
一. 长度不受限制的字符串函数
1.1 strlen(求字符串长度)
形式:size_t strlen ( const char * str );
返回值:该函数的返回值为字符串的长度,不包括串结束符’\0’。
使用strlen函数时必须知道的几个关键点**:**
1.strlen()括号里的参数类型必须是字符指针(char*);
2.字符串末尾自带一个”\0′,且以 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ );
3.参数(括号里的内容)指向的字符串必须要以 ‘\0’ 结束;
4.注意函数的返回值为size_t,是无符号的。
例如:
#include<stdio.h>
#include<string.h>
int main() {
char arr[] = "China";
printf("%d\n", strlen(arr));
return 0;
}
输出结果:
易错题点1:字符数组内容的区别(忘记字符串末尾自带的’\0′)
例题1:
//该程序输出结果是什么?
#include<stdio.h>
#include<string.h>
int main() {
char arr1[] = "abc";
char arr2[] = { 'a','b','c' };
printf("strlen(arr1)=%d\n", strlen(arr1));
printf("strlen(arr2)=%d\n", strlen(arr2));
return 0;
}
输出结果:
解析如下:
****易错题点2:strlen函数返回值的类型(****strlen函数的返回值为无符号整数(size_t)
例题2:
//该程序输出结果是什么?
#include<string.h>
#include <stdio.h>
int main() {
const char* str1 = "abc";
const char* str2 = "abcdef";
if (strlen(str1) - strlen(str2) > 0)
{
printf("str1>str2\n");
}
else
{
printf("srt1<str2\n");
}
return 0;
}
输出结果:
解析如下:
输出结果:
模拟实现strlen函数:
//模拟实现strlen函数
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<assert.h>
#include<string.h>
size_t my_strlen(const char* p, int len) {
assert(p != NULL);
while (*(p + len++));
return len-1 ;
}
int main() {
size_t len = 0;
char arr[100];
gets_s(arr);
len = my_strlen(arr, len);
printf("len=%u\n",len );
return 0;
}
1.2 strcpy(字符串拷贝(复制)函数)
形式:char* strcpy(char * destination, const char * source );
语法:strcpy(字符数组1,字符串2)
返回值:该函数返回字符数组1的首地址
语义:strcpy是字符串拷贝函数的关键字;字符数组1为目标字符数组名,保存复制后的结果,字符串2可以是字符串或字符数组名,是源字符串,表示将字符串2的所有字符包括字符结束符’\0’依次复制到字符数组1中去。操作后字符串2的字符覆盖字符数组1中的原来字符。
使用strcpy函数时必须知道的几个关键点**:**
1.源字符串必须以 ‘\0’ 结束;
2.会将源字符串中的 ‘\0’ 拷贝到目标空间
3.目标空间必须足够大,能容纳下源字符串的内容;
4.目标空间必须可修改。
例如:
#include<stdio.h>
#include<string.h>
int main() {
char arr1[20] = "xxxxxxxxxxxxx";
char arr2[] = "China";
printf("%s\n", strcpy(arr1, arr2));
return 0;
}
输出结果:
易错点1:目标空间必须可修改(不能是常量字符串)
例题1:
//arr的值能拷贝到p当中吗?
#include<stdio.h>
#include<string.h>
int main() {
const char* p = "#############";
char arr[] = "hello";
strcpy(p, arr);
return 0;
}
答案是不能,因为char* p为常量字符串,里面的内容无法改变(字符指针char*的内容都是常量字符串)
易错点2:源字符数组要有’\0′
例题2:
//arr2的内容能拷贝到arr1里面去吗?
#include<stdio.h>
#include<string.h>
int main() {
char arr1[20] = "xxxxxxxxxxxxx";
char arr2[] = {'a','b','c'};
printf("%s\n", strcpy(arr1, arr2));
return 0;
}
**答案是能,但是因为arr2后面没有加’\0′,所以abc打印之后会继续打印(内容不定),直到找到’\0’为止.
**
输出结果:
模拟实现strlen函数:
//模拟实现strlen函数
//模拟实现字符串拷贝函数
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* scr) {
//把str指向的内容拷贝进dest指向的空间中
//本质上,希望dest指向的内容被修改,而str指向的内容不应该被修改
//const char* scr:防止字符串arr2的内容改变
//const在*的左边,表示修饰指针指向的内容,则指针指向的内容无法改变
//其具体用法会在const的介绍中详细介绍
char* ret = dest;
assert(scr != NULL);
assert(dest != NULL);
//assert():断言,若括号内的条件不满足,则会报错
//报错时在末尾会提供报错的具体代码的行号
while (*(dest++) = *(scr++));
//当*dest='\0'时,'\0'的ASCLL值为0.括号内值为假,则跳出循环
return ret;
}
int main() {
char arr1[20] = "xxxxxxxxxxxxx";
char arr2[] = "hello";
//strcpy(目的地,源头)
printf("%s\n", my_strcpy(arr1, arr2));//链式访问
return 0;
}
输出结果:
1.3strcat(字符串追加(连接)函数)
形式:char * strcat ( char * destination, const char * source );
语法:strcat(字符数组1,字符数组2)
返回值:该函数返回字符数组1的首地址。
语义:strcat是字符串连接函数的关键字;字符数组1是放置在前面的字符串,字符数组2是连接在字符串1之后的字符串,连接时覆盖字符串1后面的’\0’,将整个的字符串2都连接到字符串1的后面。
例如:
#include<stdio.h>
#include<string.h>
int main() {
char arr1[20] = "China";
char arr2[] = " NO.1";
printf("%s\n", strcat(arr1, arr2));
return 0;
}
输出结果:
使用strcpy函数时必须知道的几个关键点**:**
1.源字符串必须以 ‘\0’ 结束。
2.会将源字符串中的 ‘\0’ 拷贝到目标空间。
3.目标空间必须足够大,以确保能存放源字符串。
4.目标空间必须可变
接下来让我们思考几个问题: strcat(arr1,arr2)连接两个字符串时,arr2中的’\0’是否会一起带到arr1中来?arr1和arr2连接的过程是怎样的?通过下面的代码可以让我们做个小测试:
#include<stdio.h>
#include<string.h>
int main() {
char arr1[50] ="hello \0#########";
//在arr1中主动放一个\0,若arr2中的\0也会被跟着到arr1中来,则输出的结果就会是:
//hell0 world(说明第一个\0被覆盖了) char arr2[] = "world";
strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
输出结果到底如何?
调试结果:
跟据调试结果我们可以得出如下结论:
strcat函数在使用时,源字符串会自动寻找目标空间的’\0′, 然后源字符串的第一个字符将覆盖掉这个’\0’,接着将源字符串的内容以此拷贝进去,包括源字符串的’\0’,最后打印字符串时是以拷贝过去的’\0’作为结束标志。
模拟实现strcat函数:
//模拟实现strcat函数
//模拟实现字符串连接函数
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src) {
assert(dest != NULL && src != NULL);
char* ret = dest;//将目标空间的起始地址存在ret中
while (*dest)//1.找目标字符串中的\0
{
dest++;
}
while (*dest++ = *src++);//2.追加源字符串,包括\0
return ret;//返回目标空间的起始地址
}
int main() {
char arr1[50] = "China ";
char arr2[] = "NO.1";
printf("%s\n", my_strcat(arr1, arr2));//链式访问
return 0;
}
**输出结果:
**
注:strcat无法自己追加自己(strcat(arr1,arr1)为错误的写法),但strncat可以,strncat具体是个什么东西,以及到底是怎么实现的,在下面会做专门的讲解。
1.4 strcmp(字符串比较函数)
形式:int strcmp ( const char * str1, const char * str2 );
语法:strcmp (字符串1,字符串2)
返回值:
Ø若字符串1=字符串2,则函数值为0;
Ø若字符串1>字符串2,则函数值为一个正整数(正整数的值由编译器决定);
Ø若字符串1<字符串2,则函数值为一个负整数(负整数的值由编译器决定)。
语义:
Ø将两个字符串自左至右逐个字符相比,直到出现不同的字符或遇到’\0’为止。
Ø若全部字符都相同,则认为两个字符串相等。
Ø若出现不相同的字符,则以第一对不相同的ASCII码的大小字符的比较结果为准。
例如:
#include<stdio.h>
#include<string.h>
int main() {
char arr1[50] = "abce";
char arr2[] = "abcd";
printf("%d\n", strcmp(arr1, arr2));
return 0;
}
输出结果:
使用strcmp函数时必须知道的关键点**:**
strcmp函数进行比较时,是一个一个查找比较,如果此时发现两个字符不同,则比较其ASCLL值,最后的比较结果决定了strcmp函数的返回值,比如说:
char* p="abcdg"和char* q="abcdekkkkkkk"
虽然看起来q的总ASCLL值要比q大不少,但是用strcmp进行比较的结果也会是p>q(因为比较到g和e时,g的ASCLL值要大于e的ASCLL值):
#include<stdio.h>
#include<string.h>
int main() {
const char* p ="abcdg";
const char* q = "abcdekkkkkkk";
printf("%d\n", strcmp(p, q));
return 0;
}
输出结果:
模拟实现strcmp函数
//模拟实现strcmp函数
//模拟实现字符串比较函数
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<assert.h>//断言
int my_strcmp(const char* p, const char* q) {
assert(p != NULL && q != NULL);
while (*p == *q)//找不同的字符
{
if (*p == '\0')
{
return 0;
}
p++;
q++;
}
return *p - *q;//若q的ASCLL值大,则返回一个正数,反之则返回一个负数
}
int main() {
const char *p ="abcde";//p里面存的是首字符a的地址
const char* q = "abcdf";//q里面存的是首字符a的地址
int ret = my_strcmp(p, q);
if (ret > 0)
{
printf("p>q\n");//字符串p大于字符串q
}
else if (ret < 0)
{
printf("p<q\n");//字符串p小于字符串q
}
else
{
printf("p==q\n");//字符串p等于字符串q
}
return 0;
}
ps:这个程序中的一些代码其实可以适当简化,但是为了看得更清晰,所以我把步骤写详细了一点。
输出结果:
刚刚我们讲的strlen,strcpy,strcat,strcmp函数,都属于长度不受限制的字符串函数,接下来我们要讲的strncpy,strncat,strncmp函数,都属于长度受限制的字符串函数,那么两者之间到底有什么区别呢?
——————————————-分割线———————————————-
二.长度受限制的字符串函数
2.1 strncpy(字符串拷贝函数)
形式:char * strncpy ( char * destination, const char * source, size_t num );
num指的是拷贝字符的个数
例如:
#include<stdio.h>
#include<string.h>
int main() {
char arr1[20] = "abcdef";
char arr2[10]="opq";
strncpy(arr1, arr2, 2);
//将arr2中的前两个字符拷贝到arr1中
printf("%s\n",arr1);
return 0;
}
输出结果:
注:如果num大于源字符串的字符个数(包括\0),则会将源字符串的所有内容(不包括\0后面的)拷贝进目标字符串中,num的值比源字符串的字符个数多多少则会补多少\0。
例如:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main() {
char arr1[20] = "abcdefghijk";
char arr2[]="qwer";
strncpy(arr1, arr2, 8);
//将arr2中的前两个字符拷贝到arr1中
printf("%s\n",arr1);
return 0;
}
输出结果:
调试结果:
模拟实现strncpy函数:
//模拟实现strncpy函数
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strncpy(char* dest, const char* source, int num) {
assert(dest != NULL && source != NULL);
char* start = dest;//将arr1首地址存在start当中
while (num-- && (*dest++ = *source++));//拷贝,当num==0时跳出循环
if (num > 0)//补充\0
{
while (num--)
*dest++ = '\0';
}
return start;//返回arr1的首地址
}
int main() {
char arr1[20] = "abcdefghijk";
char arr2[]="qwer";
printf("%s\n", my_strncpy(arr1, arr2, 8));
return 0;
}
输出结果:
2.2 strncat(字符串连接函数)
形式:char * strncat ( char * destination, const char * source, size_t num );
num为连接字符的个数
例如:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main() {
char arr1[20] = "hello ";
char arr2[]="world";
strncat(arr1, arr2, 3);
//将arr2中的前3个字符连接在arr1后
printf("%s\n", arr1);
return 0;
}
输出结果:
注:当num的值大于源字符串的字符个数(包括\0)时,则会将源字符串的所有内容(不包括\0后面的)都连接到目标字符串中,
例如:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main() {
char arr1[20] = "hello ";
char arr2[]="world";
strncat(arr1, arr2, 8);//num大于5
printf("%s\n", arr1);
return 0;
}
输出结果:
调试结果:
模拟实现strncat函数:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strncat(char* dest, char* source, int num) {
assert(dest != NULL && source != NULL);
char* start = dest;//将目标字符串的首地址储存在start中
while (*dest++);
dest--;//让dest指向目标字符串中的\0
while (num-- && (*dest++ = *source++) );
//num决定连接到目标字符串的个数
if (num > 0) return start;
//当num大于源字符串字符个数时,返回目标字符串的首地址
//当num小于源字符串的字符个数(不包括\0)时,在目标字符串末尾主动放一个\0
*(dest + 1) = '\0';
return start;
}
int main() {
char arr1[20] = "hello ";
char arr2[]="world";
my_strncat(arr1, arr2, 8);
printf("%s\n",arr1);
return 0;
}
输出结果:
2.3 strncmp(字符串比较函数)
形式:int strncmp ( const char * str1, const char * str2, size_t num );
num为源字符串跟目标字符串进行比较的字符个数
例如:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main() {
char arr1[20] = "abcdefeghi";
char arr2[]="abcdd";
int ret = strncmp(arr1, arr2, 8);
printf("%d\n", ret);
return 0;
}
输出结果:
——————————————-分割线———————————————-
三.字符串查找函数
注:有*号的只需了解就行
3.1 strstr(判断源字符串是否为目标字符串的子串)
形式:char * strstr ( const char *str2, const char * str1);
语法:strstr(arr1,arr2)
返回值:返回第一次出现的源字符串的地址
语义:判断源字符串是否为目标字符串的子串,若找到了,则返回子串的地址,
若找不到,则返回一个空指针(NULL)
注:子串的意思不是目标字符串有源字符串的内容就行,而是内容要跟源字符串一模一样,且不能分散开,比如“abcd”是“abcdefg”的子串,而”ade”则不是“abcdefg”的子串。
例如:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main() {
char arr1[20] = "abcdefeghi";
char arr2[] = "cde";
char arr3[] = "abced";
char arr4[] = "aeh";
printf("%s\n", strstr(arr1, arr2));
printf("%s\n", strstr(arr1, arr3));
printf("%s\n", strstr(arr1, arr4));
return 0;
}
输出结果:
模拟实现strstr函数:
//模拟实现strstr函数
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strstr(const char* dest,const char* source) {
assert(dest != NULL && source != NULL);
const char* s1 = NULL;
const char* s2 = NULL;
const char* cp = dest;
if (*source == '\0')
{
return (char*)dest;
//将const char* 类型的dest强制类型转换为返回值的类型(char*)
}//防止传过来的source内容为空
while (*dest)
{
s1 = cp;
s2 = source;
while (*s1 && *s2 && (*s1 == *s2))
{
//*s1 && *s2:当s1或s2为\0时,则跳出循环
s1++;
s2++;
}
if (*s2 == '\0')
{
return (char*)cp;
//将const char*类型的cp强制类型转换为返回值的类型(char*)
}
cp++;
}
return NULL;//找不到则返回空指针
}
int main() {
char arr1[20] = "abbbcdefg";
char arr2[]="abbc";
char* ret = my_strstr(arr1, arr2);
if (ret == NULL)
{
printf("找不到\n");
}
else
{
printf("找到了:%s\n", ret);
}
return 0;
}
输出结果:
*3.2 strtok(字符串分割)
形式:char * strtok ( char * str, const char * sep );
语法: strtok(arr1,arr2)
返回值:分割字符串的起始地址
语义:
- sep参数是字符串,定义了用作分隔符的字符集合**(例如: char* p=”@. “)**
- 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
- strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。)
- 当strtok函数的第一个参数不为 NULL 时,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
- 当strtok函数的第一个参数为 NULL 时,函数将在同一个字符串中被保存的位置开始,查找下一个标 记。 如果字符串中不存在更多的标记,则返回 NULL 指针。
写法一 ( 在str字符串中分隔符较少时使用 ) :
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main() {
char arr[] = "1255416747@qq.com";
const char* p = "@.";//分隔符
char tmp[30] = { 0 };
strcpy(tmp, arr);
//为了防止arr的内容被改变,将arr的内容赋值到tmp当中
char* ret = strtok(tmp, p);
printf("%s\n", ret);//返回1255416747的首地址
//第一次:strtok的第一个参数不为NULL,将@改为\0
ret = strtok(NULL, p);
printf("%s\n", ret);//返回qq的首地址
//第二次:strtok从NULL (\0) 开始,将 . 改为\0
ret = strtok(NULL, p);
printf("%s\n", ret);//返回com的首地址
//第三次:strtok从NULL开始,以末尾的\0结束
return 0;
}
输出结果:
写法二(巧妙运用for循环,任何情景下都可以使用):
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main() {
char arr[] = "1255416747@qq.com";
const char* p = "@.";//分隔符
char tmp[20] = { 0 };
strcpy(tmp, arr);
char* ret = NULL;
for (ret = strtok(tmp, p); ret != NULL; ret = strtok(NULL, p))
{
//ret!=NULL : 当ret为空指针(\0)时,跳出循环
printf("%s\n", ret);
}
return 0;
}
输出结果:
今天的文章字符函数和字符串函数汇总分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/19336.html