当前位置: 首页 > news >正文

【c语言】字符函数和字符串函数

目录

1.函数介绍

1.1 strlen

1.2 strcpy

 1.3 strcat

 1.4 strcmp

1.5 strncpy

1.6 strncat 

1.7 strncmp

1.8 strstr

1.9 strtok

 1.10 strerror

1.11 memcpy

1.12 memmove 

1.13 memset

​编辑

 1.14 memcmp


C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。
字符串常量适用于那些对它不做修改的字符串函数。

1.函数介绍

字符串函数:

1.1 strlen

  1.  字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )。
  2. strlen函数参数实际上是字符串首元素的地址
    #include <stdio.h>
    #include <string.h>
    int main() {const char* str = "abcdef";size_t len1 = strlen("abcdef");size_t len2 = strlen(str);printf("%d\n", len1);printf("%d\n", len2);return 0;
    }
  3. 注意函数的返回值为size_t,是无符号的( 易错 )
    #include <stdio.h>
    int main()
    {const char* str1 = "abcdef";const char* str2 = "bbb";//注意:两无符号整型的差也是无符号整型//-3补码:11111111111111111111111111111101// 作为无符号整型时是极大的数字//    3        -       6     =-3if (strlen(str2) - strlen(str1) > 0){printf("str2>str1\n");}else{printf("srt1>str2\n");}return 0;
    }
  4. 模拟实现strlen
    //1.计数器
    size_t my_strlen(char* str) {int count = 0;//创建了临时变量记录长度while (*str != '\0') {count++;str++;}return count;
    }
    int main() {char arr[] = "abc";size_t len = my_strlen(arr);//注意:数组名实际上是首元素的地址printf("%zd\n", len);return 0;
    }//使用assert断言以及const对计数器实现模拟strlen函数进行改造
    size_t my_strlen(const char* str) {//如果不写返回类型,编译器默认返回int型assert(str);//str不为空指针size_t count = 0;while (*str) {count++;str++;}return count;
    }
    int main() {char arr[] = "abcdef";printf("%zd\n", my_strlen(arr));return 0;
    }//2.递归
    size_t my_strlen(char* str) {if (*str == '\0') {return 0;}elsereturn 1 + my_strlen(str + 1);//注意:不能传参str++(先使用后++);最好不要使用带有副作用的参数:++str:str本身的值也会被改变
    }
    int main() {char arr[] = "abc";size_t len = my_strlen(arr);//注意:数组名实际上是首元素的地址printf("%zd\n", len);return 0;
    }//3.指针-指针
    #include <stdio.h>
    size_t my_strlen(char* str) {char* start = str;while (*str != '\0') {str++;}return str - start;
    }
    int main() {char arr[] = "abcdef";size_t len = my_strlen(arr);printf("%zd\n", len);return 0;
    }

1.2 strcpy

  1.  Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).
  2. 源字符串必须以 '\0' 结束。
  3. 会将源字符串中的 '\0' 拷贝到目标空间。
  4. 目标空间必须足够大,以确保能存放源字符串——程序员自己保证
  5. 目标空间必须可变。
    #include <stdio.h>
    #include <string.h>
    int main() {//目标空间必须可以修改char* dest = "abcdef";char* arr[] = "HELLO";strcpy(dest, arr);//error:发生访问冲突:常量字符串不能修改printf("%s\n", dest);return 0;
    }
  6. 模拟实现strcpy
    #include <stdio.h>
    #include <assert.h>
    char* my_strcpy(char* dest, char* src) {assert(dest != NULL);assert(src != NULL);char* ret = dest;while (*dest++ = *src++);return ret;
    }
    int main() {char arr1[10] = { 0 };char arr2[] = "hello c";printf("%s\n", my_strcpy(arr1, arr2));return 0;
    }

 1.3 strcat

strcat的使用:

  1. 找到arr1的末尾(\0)
  2. 再把arr2的内容追加到arr1后边 
  1. Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source, and a null-character is included at the end of the new string formed by the concatenation of both in destination.
  2. 源字符串必须以 '\0' 结束。(保证能找到目标空间的末尾)
  3. 目标空间必须有足够的大,能容纳下源字符串的内容
    #include <stdio.h>
    #include <string.h>
    int main() {//char arr1[] = "abc";//errchar arr1[20] = "abc";char arr2[] = "def";strcat(arr1, arr2);printf("%s\n",arr1);return 0;
    }
    下标越界
  4. 目标空间必须可修改
  5. 模拟实现
    #include <assert.h>
    #include <stdio.h>
    //模拟实现strcat
    //strcat函数返回的是目标空间的起始地址
    char* my_strcat(char* dest, const char* src) {char* ret = dest;assert(dest && src);//1.找到目标空间的起始地址while (*dest != '\0') {dest++;}//2.数据追加//相当于strcpywhile (*dest++ == *src++);return ret;
    }
    int main() {char arr1[20] = "abc";char arr2[] = "def";my_strcat(arr1, arr2);printf("%s\n", arr1);return 0;
    }
  6. 字符串自己给自己追加,如何?

注意:给自己追加可能会覆盖源字符串的'\0',导致源字符串不以'\0'结尾,但是否覆盖取决于库函数的具体实现,c语言没有规定标准的库函数实现,所以尽量不要给自己追加

 1.4 strcmp

  1. This function starts comparing the first character of each string. If they are equal to each other, it continues with the following pairs until the characters differ or until a terminating null-character is reached.
  2. 标准规定:
  • 第一个字符串大于第二个字符串,则返回大于0的数字
  • 第一个字符串等于第二个字符串,则返回0
  • 第一个字符串小于第二个字符串,则返回小于0的数字

注意:模拟实现

#include <assert.h>
#include <stdio.h>
//strcmp的模拟实现
int my_strcmp(const char* str1,const char* str2) {//只比较不改变assert(str1 && str2);while (*str1 == *str2) {if (*str1 == '\0')return 0;str1++;str2++;}if (*str1 > *str2)return 1;elsereturn -1;//VS环境下返回的数字是1,-1
}
//注意:可以改成
int my_strcmp(const char* str1,const char* str2) {//只比较不改变assert(str1 && str2);while (*str1 == *str2) {if (*str1 == '\0')return 0;str1++;str2++;}return *str1 - *str2;//返回值符合要求
}
int main() {char arr1[] = "abe";char arr2[] = "abd";//使用strcmp比较字符串大小if (my_strcmp(arr1, arr2) > 0) {printf(">\n");}else if (my_strcmp(arr1, arr2) < 0) {printf("<\n");}elseprintf("=\n");return 0;
}

长度不受限制的字符函数:strcpy,strcat,strcmp

长度受限制的字符函数: strncpy,strncat,strncmp(相对安全)

1.5 strncpy

  1. Copies the first num characters of source to destination. If the end of the source C string (which is signaled by a null-character) is found before num characters have been copied, destination is padded with zeros until a total of num characters have been written to it.
  2. 拷贝num个字符从源字符串到目标空间(只拷贝num个字符,不会自动补充'\0')
  3. 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个
    #include <string.h>
    int main() {char arr1[20] = "xxxxxxxxxxxxx";char arr2[] = "abc";strncpy(arr1, arr2, 6);return 0;
    }

1.6 strncat 

  1. Appends the first num characters of source to destination, plus a terminating null-character.注意:加上'\0'
    #include <string.h>
    int main() {char arr1[10] = "abc\0xxxx";char arr2[] = "eee";strncat(arr1, arr2, 2);return 0;
    }
  2. If the length of the C string in source is less than num, only the content up to the terminating null-character is copied.(如果源字符串的长度小于num,就按照源字符串的长度追加)

1.7 strncmp

比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。

1.8 strstr

在字符串中找字符串

strstr会返回str1中str2第一次出现的位置,如果str1中没有str2,就返回NULL 

strstr的使用:

#include <stdio.h>
#include <string.h>
int main() {char arr1[] = "abcdefg";char arr2[] = "def";char* ret = strstr(arr1, arr2);if (ret == NULL) printf("找不到\n");elseprintf("%s\n", ret);return 0;
}

 

strstr的模拟实现:

#include <assert.h>
#include <stdio.h>
//strstr的模拟实现
const char* my_strstr(const char* str1, const char* str2) {//创建匹配指针const char* cp;const char* s1;const char* s2;//空指针不能接引用assert(str1 && str2);if (*str2 == '\0') {return str1;}cp = str1;while (*cp) {s1 = cp;s2 = str2;//匹配的条件/*if (*s1 && *s2 && *s1 == *s2) {s1++;s2++;}*///注意:判断是否匹配应该是个循环,否则无论是否匹配cp都++// 正确的是cp++一次,判断整个字符串是否匹配,不相等时cp++,不相等是因为str2到末尾时返回cpwhile (*s1 && *s2 && *s1 == *s2) {s1++;s2++;}if (*s2 == '\0') {return cp;}cp++;}return NULL;
}
int main() {char arr1[] = "abbbcdef";char arr2[] = "bbc";const char* ret = my_strstr(arr1, arr2);if (ret == NULL) {printf("没有找到\n");}elseprintf("%s\n", ret);return 0;
}

注意:

  1. 创建用来遍历的指针
  2. 空指针不能解引用
  3. str2为空字符串的情况
  4. 指针的使命(作用)
  5. 循环的条件
  6. 判断匹配的条件(没有到字符串末尾且对应相等)
  7. 不匹配时的两种情况
  8. 函数的返回值

注意:上述strstr的模拟实现是一种暴力求解方式,更高效的算法:KMP

1.9 strtok

  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>
//strtok函数
int main() {char arr1[] = "haodanlu@yeah.net@hehe";char arr2[] = "234.4342.235";//IP地址,点分十进制char buf1[200] = { 0 };strcpy(buf1, arr1);//使用strtok时用的是源字符串的临时拷贝char buf2[200] = { 0 };strcpy(buf2, arr2);char* p1 = "@.";char* s = NULL;for (s = strtok(buf1, p1); s != NULL; s = strtok(NULL, p1)) {//haodanlu@yeah.net@heheprintf("%s\n", s);}//strtok只有第一次使用需要传非空指针,for循环的初始化部分只执行一次,将strtok的第一次调用放在初始化部分//strtok可以调用多次,但第一次调用和之后调用不一样char* p2 = ".";char* s = NULL;for (s = strtok(buf2, p2); s != NULL; s = strtok(NULL, p2)) {//234.4342.235printf("%s\n", s);}return 0;}

 1.10 strerror

strerror将错误码翻译为错误信息,返回错误信息的起始地址

(无论是使用库函数,还是在软件设计过程中发生错误时,都会产生错误码)

c语言中使用库函数的时候,如果发生错误,就会将错误码放在errno的变量中,errno是一个全局变量,可以直接使用

打开文件的例子
fopen以读的形式打开文件:如果文件存在,打开成功,如果文件不存在,打开失败 

#include <stdio.h>
#include <string,h>
int main() {FILE* pf = fopen("add.txt", "r");//以读的形式打开add.txtif (pf == NULL) {printf("打开文件失败,原因是:%s\n", strerror(errno));return 1;}elseprintf("打开文件成功\n");return 0;
}

注意:不要隐藏文件的扩展名

#include <stdio.h>
#include <string.h>
//perror 头文件stdio.h
// 直接打印错误信息所对应的错误码
// printf+strerror
// perror在打印的时候:自定义信息: xxxxxxxxxxxx(自定义信息:(空格)(错误码错对应的信息)
// 方便,但是功能单一,无法实现单纯的获得错误码,而是直接打印
int main() {FILE* pf = fopen("add.txt", "r");//以读的形式打开add.txtif (pf == NULL) {perror("打开文件失败");return 1;}elseprintf("打开文件成功\n");return 0;
}

 字符函数:

  1. 字符分类函数
  2. 字符转换函数 
字符分类函数
函数如果他的参数符合下列条件就返回真
iscntrl任何控制字符
isspace空白字符:空格‘’,换页‘R’,换行‘N’,回车‘V’,制表符‘N’或者垂直制表符‘V’
isdigit十进制数字 0~9
isxdigit十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower小写字母a~z
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
isprint任何可打印字符,包括图形字符和空白字符

 eg:islower

#include <stdio.h>
#include <ctype.h>
//islower
int main() {int ret = islower('a');printf("%d\n", ret);return 0;
}

//islower
// 之前判断小写字母的方法
int main() {char ch = 'w';if (ch >= 'a' && ch <= 'z') {printf("是小写字母\n");}elseprintf("非小写\n");return 0;
}

可以写成:

int main() {char ch = 'A';if (islower(ch)) {printf("是小写字母\n");}elseprintf("非小写\n");return 0;
}

字符转换函数:

int tolower ( int c );
int toupper ( int c );
注意:参数与返回值的类型都是int,字符数据可以以ASCII码值形式存入int型中,但int型不能存入char中 

 练习:

#include <stdio.h>
#include <ctype.h>
int main() {//Test String.\nchar arr[] = "Test String.\n";char* p = arr;while (*p) {*p = tolower(*p);//注意:转换以后要赋值才能改变*p//注意:tolower只能处理大写字母,小写字母(其他)不做改变p++;}printf("%s", arr);return 0;
}

内存相关函数: 

1.11 memcpy

头文件:string.h

注意:参数的指针类型不为具体的类型, 可以接受任意类型

模拟实现memcpy:

#include <assert.h>
//模拟实现
//注意:memcpy可以操作多个类型,用void*接收;
void* my_memcpy(void* dest, const void* src, size_t sz) {assert(dest && src);void* ret = dest;while (sz--) {*(char*)dest = *(char*)src;dest = (char*)dest + 1;//强制类型转换是临时的src = (char*)src + 1;//注意两种写法://1./*(char*)dest++;(char*)src++;*///++的优先级高于强制类型转化,void*类型的指针不能直接进行+-操作,不正确//2./*++(char*)dest;++(char*)src;*///c++下无法编译}return ret;
}
int main() {int arr1[10] = { 0 };int arr2[] = { 1,2,3,4,5 };//把arr2中前5个整数的数据拷贝到arr1中my_memcpy(arr1, arr2, 20);return 0;
}

memcpy是否能拷贝数组的数据给自身?

注意:不重叠内存的拷贝,可以使用memcpy;重叠内存的拷贝,可以使用memmove

1.12 memmove 

#include <string.h>
int main() {int arr[] = { 1,2,3,4,5,6,7,8,9,10 };memmove(arr + 2, arr, 20);return 0;
}

事实上,VS环境下使用memcpy也可以实现重叠部分的拷贝:

 标准值规定,memcpy来实现按不重叠的内存拷贝(只需要达成60分),重叠的内存拷贝,有memmove来实现;但是,当前的VS2022这个环境中memcpy也能实现重叠,当前环境下的memcpy100分

 memmove的模拟实现:

注意:如果src与dest的重叠部分在src的前面,就从前往后拷贝;如果在src的后面,就从后往前拷贝;总之,重叠部分的数据要先拷贝,否则会被覆盖

 

可以有两种拷贝方法:

1.dest<src:从前往后;dest>src:从后往前

2.src<dest<(char*)src+sz:从后向前;else:从前向后

这里方法一更简单,使用该方法进行模拟:

#include <assert.h>
#include <stdio.h>
//memmove的模拟实现
void* my_memmove(void* dest, const void* src, size_t sz) {assert(dest && src);void* ret = dest;if (dest < src) {int i = 0;for (i = 0; i < sz; i++) {*(char*)src = *(char*)dest;src = (char*)src + 1;//注意:强制类型转换是临时的,下一次使用仍要强转dest = (char*)dest + 1;}}else {while (sz--) {*((char*)dest + sz) = *((char*)src + sz);}}return ret;
}
int main() {int arr[] = { 1,2,3,4,5,6,7,8,9,10 };my_memmove(arr + 2, arr, 20);int i = 0;for (i = 0; i < 10; i++) {printf("%d ", arr[i]);}return 0;
}

1.13 memset

 注意:memset是以字节为单位来设置内存

#include <string.h>
#include <stdio.h>
int main() {char arr[] = "hello haodan";memset(arr + 6, 'x', 3);printf("%s\n", arr);return 0;
}
#include <string.h>
int main() {int arr[10] = { 0 };memset(arr, 1, 40);//数组会变成10个1吗?//不会:memset以字节为单位设置内存,而不是以整型为单位设置//memset不能将元素变为全1,但是可以设置为全0(每个字节都为0)//memset更适合设置字符return 0;
}

 1.14 memcmp

以字节为单位比较 

#include <string.h>
#include <stdio.h>
int main() {int arr1[] = { 1,2,3,4,5 };//第13个字节:04int arr2[] = { 1,2,3,7 };//第13个字节:07int arr3[] = { 1,2,3,0x11223304 };//第13个字节:04printf("%d\n", memcmp(arr1, arr2, 13));//返回小于0的数字printf("%d\n", memcmp(arr1, arr3, 13));//返回0return 0;
}

 

http://www.xdnf.cn/news/249553.html

相关文章:

  • PB的框架advgui反编译后控件无法绘制的处理(即导入pbx的操作步骤)
  • 编程题python常用技巧-持续
  • 【java WEB】恢复补充说明
  • 基于hr2管理系统的学习
  • BG开发者日志501:故事模式的思路2
  • 2025五一杯数学建模B题:矿山数据处理问题,详细问题分析,思路模型
  • 有没有贴吧备份的网站,备份贴吧网站数据的方法
  • 【c++】【STL】queue详解
  • 【业务领域】PCIE协议理解
  • 三维装配可视化界面开发笔记
  • 2024年US SCI1区TOP:自适应变异麻雀搜索算法AMSSA+地铁隧道变形预测,深度解析+性能实测
  • 小刚说C语言刷题—1602总分和平均分
  • xml 和 yaml 的区别
  • 冒泡排序:从入门到入土(不是)的奇妙旅程
  • 文章记单词 | 第55篇(六级)
  • 字节跳动社招 BSP驱动工程师
  • 猫,为什么是猫?
  • JavaScript基础-比较运算符
  • 前端八股 5
  • 2025深圳杯、东三省数学建模B题数模AI全网专业性第一
  • 2025五一杯C题五一杯数学建模思路代码文章教学:社交媒体平台用户分析问题
  • 文章记单词 | 第53篇(六级)
  • windows中Python的pip工具换源的方法及其原理
  • HOOK上瘾思维模型——AI与思维模型【88】
  • HW1 code analysis (Machine Learning by Hung-yi Lee)
  • gephi绘图
  • 自动剪辑批量混剪视频过原创软件工具视频帧级处理技术实践批量截图解析
  • Python实例题:Python实现Python解释器
  • C++的基础知识(引用、类、封装、继承、多态、函数重载)
  • 驱动读写实验