C语言:几种字符串常用的API
字符串的常用操作
C 语言的标准库 <string.h>
提供了很多用于处理字符串的函数。
1. strlen
- 计算字符串长度
size_t strlen(const char *str);
- 功能:计算字符串
str
的长度,不包含字符串结束符'\0'
。
2.strcpy
- 复制字符串
char *strcpy(char *dest, const char *src);
- 功能:表示把src所指向的字符串中以src地址开始的前n个字节复制到dest所指的数组中,并返回被复制后的dest。
#include <stdio.h>
#include <string.h>
int main() {
char src[] = "Hello";
char dest[10];
strcpy(dest, src);
printf("复制后的字符串: %s\n", dest);
return 0;
}
#include <stdio.h>
#include <assert.h>
// 自定义的字符串复制函数
char* myStrcpy(char *des,char *src)
{
assert(des!=NULL&&src!=NULL); //断言
char *bak=des;
while(*src!='\0'){
*des=*src;
des++;
src++;
}
*des='\0';
return bak;
}
char* myStrcpy2(char *des,char *src)
{
if(des==NULL||src==NULL){
return NULL;
}
char *bak=des;
while(*src!='\0'){
*des++=*src++;
}
*des='\0';
return bak;
}
char* myStrcpy3(char *des,char *src)
{
if(des==NULL||src==NULL){
return NULL;
}
char *bak=des;
while((*des++=*src++)!='\0');
*des='\0';
return bak;
}
int main()
{
char str[128]={'\0'};
char *p="diqizhangzifuchuan";
myStrcpy(str,p);
puts(str);
return 0;
}
代码详细分析
1. myStrcpy
函数
char* myStrcpy(char *des, char *src)
{
assert(des!=NULL && src!=NULL); // 断言
char *bak = des;
while (*src != '\0') {
*des = *src;
des++;
src++;
}
*des = '\0';
return bak;
}
- 断言检查:使用
assert
函数检查des
和src
指针是否为NULL
。如果为NULL
,程序会触发断言错误并终止。 - 复制过程:通过
while
循环逐个字符地将src
指向的字符串复制到des
指向的内存中,直到遇到字符串结束符'\0'
。 - 添加结束符:复制完成后,手动在
des
末尾添加字符串结束符'\0'
。 - 返回值:返回指向目标字符串起始位置的指针。
2. myStrcpy2
函数
char* myStrcpy2(char *des, char *src)
{
if (des == NULL || src == NULL) {
return NULL;
}
char *bak = des;
while (*src != '\0') {
*des++ = *src++;
}
*des = '\0';
return bak;
}
- 空指针检查:使用
if
语句检查des
和src
指针是否为NULL
,如果是则返回NULL
。 - 复制过程:使用
*des++ = *src++;
语句同时完成字符复制和指针移动。 - 添加结束符:复制完成后,手动在
des
末尾添加字符串结束符'\0'
。 - 返回值:返回指向目标字符串起始位置的指针。
3. myStrcpy3
函数
char* myStrcpy3(char *des, char *src)
{
if (des == NULL || src == NULL) {
return NULL;
}
char *bak = des;
while ((*des++ = *src++) != '\0');
*des = '\0';
return bak;
}
- 空指针检查:使用
if
语句检查des
和src
指针是否为NULL
,如果是则返回NULL
。 - 复制过程:使用
while ((*des++ = *src++) != '\0');
语句在一个语句中完成字符复制、指针移动和循环条件判断。 - 添加结束符:由于
while
循环结束时,des
已经指向了字符串结束符的下一个位置,所以需要手动在des
末尾添加字符串结束符'\0'
。 - 返回值:返回指向目标字符串起始位置的指针。
myStrcpy
函数,myStrcpy2
函数,myStrcpy3
函数 实现功能一样
3. strncpy
- 复制指定长度的字符串
char *strncpy(char *dest, const char *src, size_t n);
- 功能:将字符串
src
的前n
个字符复制到dest
中。如果src
的长度小于n
,则在dest
后面填充'\0'
直到达到n
个字符;如果src
的长度大于等于n
,则不会自动添加'\0'
。 - 示例:
#include <stdio.h>
#include <string.h>
int main() {
char src[] = "Hello, World!";
char dest[5];
strncpy(dest, src, 4);
dest[4] = '\0'; // 手动添加字符串结束符
printf("复制后的字符串: %s\n", dest);
return 0;
}
原代码尝试实现一个自定义的 myStrncpy
函数,其功能类似标准库中的 strncpy
函数,用于将源字符串的最多 count
个字符复制到目标字符串中。若源字符串长度小于 count
,则用 '\0'
填充目标字符串剩余的空间。
#include <stdio.h>
char* myStrncpy(char *des, char *src, int count)
{
if (des == NULL || src == NULL) {
return NULL;
}
char *bak = des;
// 复制最多 count 个字符
while (*src != '\0' && count > 0) {
*des = *src;
des++;
src++;
count--;
}
// 如果 count 还有剩余,填充 '\0'
while (count > 0) {
*des = '\0';
des++;
count--;
}
return bak;
}
int main()
{
char str[128]={'\0'};
char *p="diqizhangzifuchuan";
myStrncpy(str,p,8);
puts(str);
return 0;
}
原代码逐行分析
#include <stdio.h>
char* myStrncpy(char *des, char *src, int count)
{
if(des == NULL || src == NULL) {
return NULL;
}
- 包含
<stdio.h>
头文件,为后续使用输入输出函数(如puts
)做准备。 - 定义
myStrncpy
函数,它接收三个参数:des
是指向目标字符串的指针,src
是指向源字符串的指针,count
表示要复制的最大字符数。 - 首先检查
des
和src
是否为NULL
,若其中任意一个为NULL
,则函数返回NULL
,避免对空指针进行操作。
char *bak = des;
while(*src != '\0' && count > 0) {
*des++ = *src++;
count--;
}
- 创建一个临时指针
bak
并初始化为des
,用于保存目标字符串的起始地址,方便后续返回。 - 使用
while
循环进行字符复制操作,条件是源字符串未结束(*src != '\0'
)且还未达到要复制的最大字符数(count > 0
)。在循环中,将源字符串当前字符复制到目标字符串当前位置,然后两个指针都向后移动一位,同时count
减 1。
if(count > 0) {
while(count > 0) {
count--;
*des = '\0';
}
return des;
}
- 当
while
循环结束后,检查count
是否大于 0。若大于 0,说明源字符串已经复制完,但还未达到要复制的最大字符数,此时需要用'\0'
填充目标字符串剩余的空间。 - 然而,原代码在这里存在逻辑错误。内层
while
循环将剩余的count
个位置填充为'\0'
后,返回的是des
指针,而des
此时已经指向填充完'\0'
后的位置,并非目标字符串的起始位置,这会导致调用者无法正确获取复制后的字符串。
*des = '\0';
return bak;
}
- 若
count
为 0,说明已经复制了count
个字符,此时在目标字符串末尾添加'\0'
作为字符串结束符,然后返回bak
指针,即目标字符串的起始位置。
4. strcat
- 连接字符串
char *strcat(char *dest, const char *src);
- 功能:把src所指向的字符串(包括“\0”)复制到dest所指向的字符串后面(删除*dest原来末尾的“\0”)。要保证*dest足够长,以容纳被复制进来的*src。*src中原有的字符不变。返回指向dest的指针。
- 示例:
#include <stdio.h>
#include <string.h>
int main() {
char dest[20] = "Hello";
char src[] = ", World!";
strcat(dest, src);
printf("连接后的字符串: %s\n", dest);
return 0;
}
#include <stdio.h>
#include <string.h>
#include <assert.h>
char* myStrcat(char *des,char *src)
{
assert(des!=NULL&&src!=NULL);//linux下的ANSI c的函数库
char *bak=des;
while(*des!='\0'){
des++;
}
while((*des++=*src++)!='\0'){
*des='\0';
return bak;
}
}
char* myStrcat2(char *des,char *src)
{
char *bak=des;
strcpy(des+strlen(des),src);//GNU C
return bak;
}
}
char* myStrcat3(char *des,char *src)
{
assert(des!=NULL&&src!=NULL);//OpenBSD
char *bak=des;
for(;*des!='\0';des++);
while((*des++=*src++)!='\0'){
*des='\0';
return bak;
}
}
int main()
{
char str[128]="staacthhhhh";
char *p="handsome";
myStrcat3(str,p);
puts(str);
return 0;
}
代码分析
myStrcat
函数
char* myStrcat(char *des, char *src)
{
assert(des!=NULL && src!=NULL); // linux下的ANSI c的函数库
char *bak = des;
while (*des != '\0') {
des++;
}
while ((*des++ = *src++) != '\0') {
*des = '\0';
return bak;
}
}
- 功能:尝试将源字符串
src
拼接到目标字符串des
的末尾。 - 参数:
des
是目标字符串的指针,src
是源字符串的指针。 - 代码流程:
assert(des!=NULL && src!=NULL);
:使用断言检查des
和src
指针是否为NULL
,若为NULL
则程序终止。char *bak = des;
:保存目标字符串的起始地址,方便最后返回。while (*des != '\0') { des++; }
:将指针des
移动到目标字符串的末尾。while ((*des++ = *src++) != '\0')
:把源字符串的字符逐个复制到目标字符串末尾。- 问题所在:
*des = '\0';
和return bak;
语句在循环内部,这会导致只复制源字符串的第一个字符就返回,并且会将该字符后面的位置置为'\0'
,无法完成整个字符串的拼接。
myStrcat2
函数
char* myStrcat2(char *des, char *src)
{
char *bak = des;
strcpy(des + strlen(des), src); // GNU C
return bak;
}
- 功能:借助标准库函数
strcpy
实现字符串拼接。 - 参数:
des
是目标字符串的指针,src
是源字符串的指针。 - 代码流程:
char *bak = des;
:保存目标字符串的起始地址。strcpy(des + strlen(des), src);
:先使用strlen(des)
找到目标字符串的末尾位置,再调用strcpy
函数将源字符串复制到该位置。return bak;
:返回目标字符串的起始地址。。
myStrcat3
函数
char* myStrcat3(char *des, char *src)
{
assert(des != NULL && src != NULL);
char *bak = des;
// 找到目标字符串的末尾
for (; *des != '\0'; des++);
// 连接源字符串
while ((*des++ = *src++) != '\0');
return bak;
}
- 功能:将源字符串
src
拼接到目标字符串des
的末尾。 - 参数:
des
是目标字符串的指针,src
是源字符串的指针。 - 代码流程:
assert(des != NULL && src != NULL);
:使用断言检查指针是否为NULL
。char *bak = des;
:保存目标字符串的起始地址。for (; *des != '\0'; des++);
:将指针des
移动到目标字符串的末尾。while ((*des++ = *src++) != '\0');
:逐个字符复制源字符串到目标字符串末尾,直到源字符串结束。return bak;
:返回目标字符串的起始地址。
myStrcpy
函数,myStrcpy2
函数,myStrcpy3
函数 实现功能一样
5. strncat
- 连接指定长度的字符串
char *strncat(char *dest, const char *src, size_t n);
- 功能:将字符串
src
的前n
个字符连接到dest
的末尾,并自动添加'\0'
。 - 示例:
#include <stdio.h>
#include <string.h>
int main() {
char dest[20] = "Hello";
char src[] = ", World!";
strncat(dest, src, 6);
printf("连接后的字符串: %s\n", dest);
return 0;
}
6. strcmp
- 比较字符串
int strcmp(const char *str1, const char *str2);
- 功能:比较两个字符串
str1
和str2
。如果str1
小于str2
,返回一个负数;如果str1
等于str2
,返回 0;如果str1
大于str2
,返回一个正数。 - 示例:
#include <stdio.h>
#include <string.h>
int main() {
char str1[] = "apple";
char str2[] = "banana";
int result = strcmp(str1, str2);
if (result < 0) {
printf("%s 小于 %s\n", str1, str2);
} else if (result == 0) {
printf("%s 等于 %s\n", str1, str2);
} else {
printf("%s 大于 %s\n", str1, str2);
}
return 0;
}
7. strncmp
- 比较指定长度的字符串
int strncmp(const char *str1, const char *str2, size_t n);
- 功能:比较两个字符串
str1
和str2
的前n
个字符。返回值规则与strcmp
相同。 - 示例:
#include <stdio.h>
#include <string.h>
int main() {
char str1[] = "apple";
char str2[] = "appetizer";
int result = strncmp(str1, str2, 3);
if (result < 0) {
printf("前 3 个字符中,%s 小于 %s\n", str1, str2);
} else if (result == 0) {
printf("前 3 个字符中,%s 等于 %s\n", str1, str2);
} else {
printf("前 3 个字符中,%s 大于 %s\n", str1, str2);
}
return 0;
自定义字符串比较函数 myStrcmp
// 自定义字符串比较函数
int myStrcmp(char *str1, char *str2) {
- 此函数的功能是比较两个字符串
str1
和str2
的大小,返回一个整数值来表示比较结果。 - 参数
str1
和str2
是指向字符的指针,分别指向要进行比较的两个字符串。
// 备份原始指针
char *bakStr1 = str1;
char *bakStr2 = str2;
- 把
str1
和str2
的原始指针备份到bakStr1
和bakStr2
中。在后续操作里,str1
和str2
指针会移动,备份原始指针是为了在需要时能够回到字符串的起始位置。不过在这段代码中,备份的指针并没有被实际使用。
// 逐个字符比较
while (*str1 && *str2 && (*str1 == *str2)) {
str1++;
str2++;
}
- 这是一个
while
循环,用于逐个字符地比较str1
和str2
。*str1
和*str2
分别表示str1
和str2
指针所指向的字符。*str1 && *str2
确保两个字符串都还未结束(即未遇到字符串结束符'\0'
)。(*str1 == *str2)
检查当前字符是否相等。- 若以上条件都满足,就将
str1
和str2
指针向后移动一位,继续比较下一个字符。
// 根据比较结果返回相应的值
if (*str1 < *str2) {
return -1;
} else if (*str1 > *str2) {
return 1;
} else {
return 0;
}
- 当
while
循环结束后,就根据当前str1
和str2
指针所指向的字符的大小关系来返回相应的值:- 若
*str1 < *str2
,表明str1
小于str2
,返回 -1。 - 若
*str1 > *str2
,表明str1
大于str2
,返回 1。 - 若
*str1
等于*str2
,意味着两个字符串相等或者同时结束,返回 0。
- 若
主函数 main
int main() {
char *p1 = "chenlichenaa";
char *p2 = "chenlichenab";
- 定义两个字符指针
p1
和p2
,分别指向两个字符串常量"chenlichenaa"
和"chenlichenab"
。
int ret = myStrcmp(p1, p2); // 1 0 -1
- 调用
myStrcmp
函数,将p1
和p2
作为参数传入,比较这两个字符串。 - 把比较结果存储在
ret
变量中。
if (ret == 0) {
puts("两个字符串一样");
} else if (ret < 0) {
puts("第一个字符串小于第二个字符串");
} else {
puts("第一个字符串大于第二个字符串");
}
- 根据
ret
的值输出相应的比较结果信息:- 若
ret
等于 0,使用puts
函数输出"两个字符串一样"
。 - 若
ret
小于 0,输出"第一个字符串小于第二个字符串"
。 - 若
ret
大于 0,输出"第一个字符串大于第二个字符串"
。
- 若
printf("RET=%d\n", ret);
return 0;
}
- 使用
printf
函数输出比较结果的具体数值。 return 0;
表示程序正常结束,返回值 0 通常表示程序执行成功。
8.strchr
查找子字符
是C 语言标准库 <string.h>
里的一个函数,其主要功能是在指定字符串里查找某个特定字符首次出现的位置。下面从多个方面详细介绍 strchr
函数:
函数原型
char *strchr(const char *str, int c);
参数说明
str
:这是一个指向要被搜索的字符串的指针,类型为const char *
,意味着在函数内部不会对该字符串进行修改。c
:需要查找的字符,虽然参数类型是int
,但实际上存储的是字符的 ASCII 码值。
返回值
- 若在字符串
str
中找到了字符c
,函数会返回一个指向该字符首次出现位置的指针。 - 若在字符串
str
中未找到字符c
,函数会返回NULL
。
工作原理
strchr
函数会从字符串 str
的起始位置开始,逐个字符进行比较,直至找到字符 c
或者到达字符串的结束符 '\0'
。一旦找到字符 c
,就会返回指向该字符的指针;若遍历完整个字符串都未找到,就返回 NULL
。
示例代码
#include <stdio.h>
#include <string.h>
int main() {
const char *str = "Hello, World!";
char target = 'o';
char *result = strchr(str, target);
if (result != NULL) {
printf("字符 '%c' 首次出现在字符串中的位置是: %ld\n", target, result - str);
} else {
printf("未在字符串中找到字符 '%c'\n", target);
}
return 0;
}
代码解释
- 定义字符串和目标字符:
const char *str = "Hello, World!";
char target = 'o';
这里定义了一个字符串 str
和要查找的目标字符 target
。
- 调用
strchr
函数:
char *result = strchr(str, target);
调用 strchr
函数在字符串 str
中查找字符 target
,并将返回的指针存储在 result
中。
- 处理返回结果:
if (result != NULL) {
printf("字符 '%c' 首次出现在字符串中的位置是: %ld\n", target, result - str);
} else {
printf("未在字符串中找到字符 '%c'\n", target);
}
- 若
result
不为NULL
,说明找到了目标字符,通过result - str
计算出目标字符在字符串中的位置(偏移量),并输出结果。 - 若
result
为NULL
,说明未找到目标字符,输出相应的提示信息。
9. strstr
- 查找子字符串
-
strstr
函数原型char *strstr(const char *str1, const char *str2);
参数说明
str1
:要进行搜索的主字符串,类型为const char *
,表示在函数内部不会修改该字符串。str2
:要查找的子字符串,同样类型为const char *
。- 若
str2
是str1
的子串,函数返回str2
在str1
中首次出现的地址。 - 若
str2
不是str1
的子串,函数返回NULL
。
返回值
- 若
str2
是str1
的子串,函数返回str2
在str1
中首次出现的地址。 - 若
str2
不是str1
的子串,函数返回NULL
。
标准库使用示例
#include <stdio.h>
#include <string.h>
int main() {
const char *str1 = "Hello, World!";
const char *str2 = "World";
char *result = strstr(str1, str2);
if (result != NULL) {
printf("子串 '%s' 在主串中首次出现的位置是: %ld\n", str2, result - str1);
} else {
printf("未在主串中找到子串 '%s'\n", str2);
}
return 0;
}
运行结果
D:\c语言\第七章字符串>a.exe
子串 'World' 在主串中首次出现的位置是: 7
char *strstr(const char *str1, const char *str2);
#include <stdio.h>
#include <string.h>
int main() {
char haystack[] = "Hello, World!";
char needle[] = "World";
char *result = strstr(haystack, needle);
if (result != NULL) {
printf("找到子字符串,位置: %ld\n", result - haystack);
} else {
printf("未找到子字符串\n");
}
return 0;
}
10.assert-断言
assert
是一个宏,定义在 <assert.h>
头文件中。它的主要作用是在程序运行时对某个条件进行检查,若条件为假(即条件表达式的值为 0),程序就会终止运行,并输出一条错误信息,其中包含断言失败的文件名、行号以及具体的断言表达式。
语法格式
#include <assert.h>
assert(expression);
expression
:这是一个需要被检查的条件表达式,它可以是任何合法的 C 语言表达式,例如变量比较、函数返回值判断等。
工作原理
-
当程序执行到
assert
语句时,会先计算expression
的值: - 若
expression
的值为真(非 0),assert
语句不会产生任何影响,程序会继续正常执行后续代码。 - 若
expression
的值为假(0),assert
会输出错误信息,并且调用abort()
函数终止程序的运行。
示例代码
#include <stdio.h>
#include <assert.h>
// 一个简单的除法函数
int divide(int a, int b) {
// 使用 assert 检查除数是否为 0
assert(b != 0);
return a / b;
}
int main() {
int result;
// 正常情况,除数不为 0
result = divide(10, 2);
printf("10 / 2 = %d\n", result);
// 异常情况,除数为 0
result = divide(10, 0);
printf("这行代码不会被执行\n");
return 0;
}
代码解释
- 在
divide
函数中,使用assert(b != 0);
来检查除数b
是否为 0。 - 当调用
divide(10, 2)
时,b
的值为 2,条件b != 0
为真,assert
不会产生任何影响,程序会继续执行并输出结果。 - 当调用
divide(10, 0)
时,b
的值为 0,条件b != 0
为假,assert
会输出错误信息,并且终止程序的运行,所以printf("这行代码不会被执行\n");
不会被执行。
11.strtok
-字符串分割
是 C 语言标准库 <string.h>
中一个非常实用的函数,用于将字符串按照指定的分隔符进行分割。下面将从函数原型、功能、使用示例、注意事项等方面详细介绍 strtok
函数。
函数原型
char *strtok(char *str, const char *delim);
参数解释
str
:指向要被分割的字符串的指针。在第一次调用strtok
时,需要传入要分割的字符串;后续调用时,应传入NULL
,表示继续对之前的字符串进行分割操作。需要注意的是,strtok
函数会修改原字符串,它会将找到的分隔符替换为字符串结束符'\0'
。delim
:指向包含分隔符字符的字符串的指针。strtok
函数会根据这个字符串中的字符来确定分割位置。例如,如果delim
为",;:"
,那么逗号、分号和冒号都会被当作分隔符。
返回值
- 如果找到了下一个标记(子字符串),则返回指向该标记的指针。
- 如果没有更多的标记可找,返回
NULL
。
功能描述
strtok
函数会在字符串 str
中搜索由 delim
中指定的分隔符分隔的标记(子字符串)。第一次调用时,它会从 str
的起始位置开始查找,将找到的第一个分隔符替换为 '\0'
,并返回指向该标记的指针。后续调用时,传入 NULL
作为第一个参数,strtok
会继续从上次停止的位置开始查找下一个标记,直到字符串结束。
使用示例
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "apple,banana;cherry:date";
const char delim[] = ",;: ";
// 第一次调用 strtok,传入要分割的字符串
char *token = strtok(str, delim);
// 循环获取分割后的每个子字符串
while (token != NULL) {
printf("%s\n", token);
// 后续调用 strtok 传入 NULL
token = strtok(NULL, delim);
}
return 0;
}
运行结果
D:\c语言\第七章字符串>a.exe
apple
banana
cherry
date
代码解释
- 定义字符串和分隔符:定义了一个待分割的字符串
str
和包含分隔符的字符串delim
。 - 第一次调用
strtok
:将str
和delim
作为参数传入strtok
函数,它会返回第一个分割后的子字符串(即"apple"
)的指针,并将原字符串中的逗号替换为'\0'
。 - 循环获取后续子字符串:在
while
循环中,不断调用strtok
函数,传入NULL
作为第一个参数,直到strtok
返回NULL
,表示字符串分割完毕。 - 输出结果:每次获取到子字符串后,使用
printf
函数将其输出。