字符函数和字符串函数
字符函数字符串函数,strlen,strstr,strcat strcmp
C语言中的字符操作、字符串处理以及内存管理是程序开发的基础核心能力。本笔记将系统地介绍这三类标准库函数的应用方法:
• 字符函数 - 用于单个字符的检测与转换,如判断字符类型、大小写转换等
• 字符串函数 - 处理以null结尾的字符序列,包括拷贝、连接、比较等操作
• 内存函数 - 直接操作内存区块,实现高效的数据复制、填充和比较
通过学习这些基础但强大的库函数,您将能够编写更健壮、高效的C语言程序。接下来我们就开始学习这些函数。
字符函数
字符分类函数
C语言中有一系列的函数是专门做字符分类的,也就是一个字符是属于什么类型的字符。这些函数的使用都需要包含头文件ctype.h
以下是主要的字符分类函数:
| 函数名 | 判断条件(参数符合条件返回真) |
|---|---|
| isalnum | 字符是字母或数字(0-9, A-Z, a-z) |
| isalpha | 字符是字母(A-Z, a-z) |
| iscntrl | 字符是控制字符(ASCII 0-31或127) |
| isdigit | 字符是十进制数字(0-9) |
| isgraph | 字符是可显示字符(非空格且可打印) |
| islower | 字符是小写字母(a-z) |
| isprint | 字符是可打印字符(包括空格) |
| ispunct | 字符是标点符号(非空格、数字和字母的可打印字符) |
| isspace | 字符是空白字符(空格、\t、\n、\v、\f、\r) |
| isupper | 字符是大写字母(A-Z) |
| isxdigit | 字符是十六进制数字(0-9, A-F, a-f) |
| 这些函数的用法非常相似: |
int isdigit (int a);
这是用来判断字符a是否为数字字符的函数,如果字符a是数字的话就返回非0的整数,如果不是数字就返回0。
练习: 将字符串的小写字母改为大写字母
#include <stdio.h>
#include <ctype.h>
int main()
{
char str[]="Hello World!";
int i=0;
while(str[i])
{
if(islower(str[i]))
{
str[i]-=32;
}
i++;
}
printf("%s",str);
return 0;
}
字符转换函数
int tolower (int a)//将参数传进去的大写字母转成小写字母
int toupper (int a)//将参数传进去的小写字母转成大写字母
知道这个函数后上面的练习就可以写成
#include <stdio.h>
#include <ctype.h>
int main()
{
char str[]="Hello World!";
int i=0;
while(str[i]) // 遍历字符串,直到遇到字符串结束符'\0'
{ // 使用toupper函数直接转换字符,替代原有的ASCII值减法
str[i] = toupper(str[i]);
i++;
}
printf("%s",str);
return 0;
}
字符串函数
strlen
size_t strlen(const char* str);
功能:统计str指向的字符串长度。统计的是字符串中\0 之前的字符的个数。并且该函数不会计算空终止符本身。
#include <stdio.h>
#include <string.h> // 使用strlen必须包含这个头文件
int main() {
// 定义3个不同的字符串示例
char str1[] = "Hello"; // 普通字符串
char str2[] = "Hi!\n"; // 包含转义字符
char str3[] = ""; // 空字符串
// 计算并打印每个字符串的长度
printf("字符串\"%s\"的长度:%zd\n", str1, strlen(str1));
printf("字符串\"%s\"的长度:%zd\n", str2, strlen(str2));
printf("字符串\"%s\"的空字符串长度:%lu\n", str3, strlen(str3));
// 对比:数组的总大小(包含\0) vs strlen计算的长度
printf("\n对比:str1数组总大小(字节):%zd\n", sizeof(str1));
printf("str1的字符长度(strlen):%zd\n", strlen(str1));
return 0;
}
使用注意事项:
- 字符串要以==‘\0’作为结束标志,所以strlen求的是字符串’\0’前面的个数且不包含’\0’==的个数。
- 参数指向的字符串必须要以==‘\0’==结束。
- 注意函数的返回值为size_t,是无符号的
- strlen的使用需要包含头文件<string.h>
模拟实现:
size_t my_strlen(const char* str) {
size_t count = 0;
assert(str);
while (*str) {
count++;
str++;
}
return count;
}
strcpy
char* strcpy(char* dest,const char* sour);
功能: 字符串拷贝,拷贝的源头字符串中的\0为止
参数: dest:指针,指向目的地空间。sour:指针指向源头数据
返回值: 函数返回目标空间的起始地址
#include <stdio.h>
#include <string.h> // 使用strcpy必须包含这个头文件
int main() {
// 1. 基础用法:将源字符串拷贝到目标字符串
char source[] = "Hello World!"; // 源字符串
char target[50]; // 目标数组,需确保空间足够大
// 执行拷贝:把source的内容复制到target中
strcpy(target, source);
printf("基础拷贝结果:\n");
printf("目标字符串:%s\n\n", target);
// 2. 错误示范(仅演示,不要运行!)
// char small_arr[3];
// strcpy(small_arr, "超长字符串"); // 目标数组空间不足,会导致内存溢出
return 0;
}
使用注意事项:
- 源字符串必须以
\0结束 - 会将源字符串中的
\0拷贝到目标空间。 - 目标缓冲区必须足够大,以容纳包括空终止符在内的副本。
- 目标必须是可修改 / 可写入的内存。
模拟实现
#include <stdio.h>
#include <string.h>
char* my_strcpy(char* dest, const char* soure)
{
char* ret = dest;
while (*dest++ = *soure++)
{
;
}
return ret;
}
int main()
{
char dest[100];
char arr[10] = "hello!";
my_strcpy(dest, arr);
printf("%s", dest);
return 0;
}
strcat
char* strcat(char* dest , const char* source)
功能: 字符串追加,把soure指向的源字符串中的所有字符都追加到destination指向的空间中。
参数: destination:指针,指向目的地空间 source:指针,指向源头数据
返回值: strcat函数返回的目标空间的起始地址
#include <stdio.h>
#include <string.h>
int main()
{
char dest[100] = "Hello ";
char arr[10] = "World!";
strcat(dest, arr);
printf("%s", dest);
return 0;
}
使用注意事项:
- 源字符串必须以
\0结束。 - 目标字符串中也有
\0,否则没办法追加从哪里开始。 - 目标空间必须有足够大的空间,能容纳下字符串的内容。
- 目标空间必须可修改。
模拟实现
#include <stdio.h>
#include <assert.h>
char* my_strcat(char* dest, char* sour)
{
assert(dest != NULL);
assert(sour != NULL);
char* ret = dest;
while (*dest)
{
dest++;
}
while (*sour)
{
*dest = *sour;
dest++;
sour++;
}
return ret;
}
int main()
{
char dest[100] = "Hello ";
char arr[10] = "World!";
my_strcat(dest, arr);
printf("%s", dest);
return 0;
}
strcmp
int strcmp(const char* str1, const char* str2);
功能: 从两个字符串的第一个字符开始比较,如果两个字符串ASCII码值相等,就比较下一个字符,直到遇到不相等的字符或\0。
返回值:
- 若
str1<str2,返回负整数; - 若
str1==str2,返回0; - 若
str1>str2,返回正整数。
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "abcfde";
char str[] = "asdf";
int count = strcmp(arr, str);
if (count > 0)
{
printf("arr>str\n");
}
else if (count < 0)
{
printf("arr<str\n");
}
else {
printf("arr==str\n");
}
return 0;
}
模拟实现
#include <stdio.h>
#include<assert.h>
int my_strcmp(const char* arr, const char* str)
{
assert(arr);
assert(str);
while (*arr == *str && *arr != '\0')
{
arr++;
str++;
}
return *arr - *str;
}
int main()
{
char arr[] = "adef";
char str[] = "adef";
int count = my_strcmp(arr, str);
if (count > 0)
{
printf("arr>str\n");
}
else if (count < 0)
{
printf("arr<str\n");
}
else {
printf("arr==str\n");
}
return 0;
}
strncpy
char* strncpy(char* dest, const char* src, size_t n);
功能: 复制最多n个字符从src到dest,如果src长度小于n,剩余部分用’\0’填充
参数: dest:指针,指向目的地空间,src:指针,指向源头数据。n:从src指向的字符串中最多拷贝的字符个数
返回值: 函数返回的目标空间的起始地址。
代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[10] = { 0 };
char str[] = "abcdfe";
strncpy(arr, str,5);
printf("%s", arr);
return 0;
}
比较strcpy和strncpy
strcpy函数拷贝到\0为止,如果目标空间不够的话,容易出现越界。
srencpy函数的指定了拷贝的长度,源字符串不一定要有\0,同时在设计参数的时候,就会多一层思考:目标空间的大小是否够用,strncpy相对更安全;注意的是,dest目标空间要初始化一下,这样如果拷贝源字符串时没有拷贝\0,也能打印出字符串
strncat
char* strncat(char* dest,const cahr* src,size_t n);
功能: 字符串追加;将src,指向的字符串的内容,追加到dest指向的空间,最多追加n个字符。
参数: dest:指针,指向目的地空间。src:指针,指向了源头数据。n:最多追加字符的个数。
返回值: 返回的是目标空间的起始地址。
代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[15] = "hello ";
char str[] = "world!";
strncat(arr, str, 6);
printf("%s", arr);
return 0;
}
strcat和strncat的比较:
- strcat函数在追加字符串的时候将源字符串的所有内容都追加到目标空间内,包含\0都追加过去,但是strncat函数只追加指定的长度。
- strncat函数中源字符串中不一定要有\0了。
- strncat更灵活安全。
strncmp
int strncmp(const char*str1 , const char* str2 , size_t n);
功能: 字符串比较;比较str1和str2指向的两个字符串的内容,最多比较n给字符
参数: str1:指针,指向第一个比较的字符串。str2:指针,指向第二个比较的字符串,n:最多比较字符的个数。
返回值:
- 若
str1<str2,返回负整数; - 若
str1==str2,返回0; - 若
str1>str2,返回正整数。
代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "abcdfr";
char str[] = "abcvfg";
int count = strncmp(arr, str,3);
if (count > 0)
{
printf("arr>str\n");
}
else if (count < 0)
{
printf("arr<str\n");
}
else {
printf("arr==str\n");
}
return 0;
}
strcmp和strncmp的比较
strncmp可以比较任意长度
strncmp函数灵活,更加安全
strstr
char * strstr(const char* str1,const char* str2);
功能: strstr函数,在字符串str1中查找str2指向的字符串第一次出现的位置。
参数: str1:指针,指向被查找的字符串。str2:指针,指向要查找的字符串
返回值:
- 如果str1指向的字符串中存在str2指向的字符串,那么就返回第一次出现位置的指针
- 如果str1指向的字符串中不存在str2指向的字符串,那么返回NULL
代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "hello world!";
char str[] = "ello";
char* s = strstr(arr, str);
printf("%s", s);
return 0;
}
strstr模拟
#include <stdio.h>
#include<assert.h>
char* my_strstr(const char* arr, const char* str)
{
assert(arr != NULL);
assert(str != NULL);
char* s1 =(char*) arr;
char* s2;
if (!*s2)
{
return (char*)str;
}
while (*s1)
{
s2 = (char*)str;
char* ptr = s1;
while ((*ptr) == (*s2))
{
ptr++;
s2++;
}
if (*s2 == '\0')
{
return s1;
}
s1++;
}
if (*s1 == '\0')
{
return NULL;
}
}
int main()
{
char arr[] = "abbbcd";
char str[] = "bbbc";
char* s = my_strstr(arr, str);
printf("%s", s);
return 0;
}
strtok
char* strtok(char* str , const char* delim)
功能:
- 分割字符串:根据delim参数中指定的分割符,将输入字符串str拆分成多个字符串。
- 修改原始字符串:strtok会直接在源字符串中插入\0终止符,替换分割符的位置,因此原始字符串会被修改。注意:strtok 会修改原字符串(把分隔符改成
\0来标记子串结束),如果原字符串需要保留,一定要先复制一份再分割!
参数:
str:首次调用时传入带入分割的字符串;后续调用传入NULL,表示继续分割同一个字符串。
delim:包含所有可能分隔的字符串(每个字符均视为独立的分隔符)。
返回值: - 成功:返回当前分割出的子串的首地址(可以直接用 printf(“%s”, 返回值) 打印子串);
- 失败 / 分割完毕:返回 NULL(以此作为循环结束的条件)。
核心原理
为什么 strtok 后续要传 NULL?因为它内部藏了一个静态指针(静态变量:函数调用结束后值不消失,记住上一次的状态),原理分 3 步:
- 第一次调用传原字符串时,strtok 会找到第一个不是分隔符的字符(子串开头),再往后找第一个分隔符,把这个分隔符改成字符串结束符
\0,然后返回子串开头地址,同时用静态指针记住分隔符的下一个位置; - 后续调用传
NULL时,strtok 会从静态指针记住的位置开始,重复第一步的操作,继续分割下一个子串; - 当找到字符串末尾,没有可分割的子串时,返回
NULL,分割结束。
关键提醒:因为用了静态指针,strtok 是线程不安全的(多线程同时用会乱),新手暂时不用管这个,单线程用完全没问题。
使用步骤:
1.首次调用:传入待分割字符串和分隔符。
2.后续调用:传入NULL和相同发分隔符,继续分割。
3.结束条件:当返回NULL时,表示分割完成。
简单示例:
#include <stdio.h>
#include <string.h> // 必须包含这个头文件
int main()
{
// 原字符串:注意这里用数组,不要用char *str = "张三,18,男";(常量字符串不能被修改,strtok会报错)
char str[] = "张三,18,男,北京";
char delim[] = ","; // 分隔符:逗号
char *p; // 接收strtok的返回值
// 第一步:第一次调用,传原字符串str和分隔符delim
p = strtok(str, delim);
// 循环分割:p不为NULL就继续,后续调用传NULL
while (p != NULL)
{
printf("分割出的子串:%s\n", p); // 打印当前子串
p = strtok(NULL, delim); // 后续调用,第一个参数必须是NULL
}
// 注意:原字符串已经被修改了(分隔符变成\0)
printf("修改后的原字符串:%s\n", str); // 只会打印第一个子串"张三"
return 0;
}
strerror
char* strerror(int errnum)
功能:
- 属于 C 语言标准库函数,头文件
<string.h>,核心是数字错误码转人类可读的文字错误描述,是 C 语言的错误码 “翻译官”; - 单独使用无意义,必须配合
errno,仅在库函数执行失败后调用才有有效结果; - 底层被
perror函数封装,perror是更简洁的错误打印方案(仅需头文件<stdio.h>)。
返回值
类型:char (字符串首地址);
内容:对应传入错误码的文字错误描述字符串,可直接通过printf(“%s”, 返回值)打印;
核心属性:返回的字符串存储在系统只读内存中,仅支持打印查看
代码演示:
演示场景:以fopen打开不存在的文件(库函数执行失败)为例,两种写法均可直接复制运行,跨 Windows/Linux 系统通用。
#include <stdio.h> // printf、fopen 头文件
#include <string.h> // strerror 头文件
#include <errno.h> // errno 头文件
int main() {
FILE *fp = fopen("test.txt", "r"); // 打开不存在的文件,失败返回NULL
if (fp == NULL) { // 核心:先判断函数失败,再使用strerror+errno
printf("数字错误码:%d\n", errno);
printf("文字错误原因:%s\n", strerror(errno));
} else {
printf("文件打开成功!\n");
fclose(fp); // 成功打开文件后必须关闭
}
return 0;
}
注意事项:
- 头文件按需引入:使用
strerror+errno时,<string.h>和<errno.h>必须同时引入;仅使用perror时,只需引入<stdio.h>; - errno 使用时机:仅在库函数执行失败后读取
errno才有意义,函数执行成功时,errno的值无有效含义(可能是随机值); - 返回字符串不可修改:
strerror的返回值是系统只读字符串,仅可打印查看,切勿尝试修改其内容,否则会导致程序崩溃; - 注意正确拼写:避免写成
strerro/strerr等错误形式,否则会编译报错 “未定义的标识符”; - 跨系统兼容性:核心错误码(如 2 = 文件不存在、13 = 权限不足)在 Windows/Linux 下通用,错误描述默认均为英文(跨平台开发无需额外适配)
更多推荐



所有评论(0)