目录
简介
C语言中对于字符和字符串的操作甚是频繁,经常把人给弄晕,今天我来总结一下。C语言中并没有定义字符串的关键字,但是字符串是存在的,字符串一般存放在常量字符串中或者字符数组中,也可以定义一个指向字符串的字符型指针,利用这个字符型指针可以访问该字符串。代码如下:
char arr1[] = {'a','b','c','d','e','f','\0'};//字符数组
char arr2[] = "abcdef";
char* parr = "abcdef";//指向字符串的字符型指针
以上代码中,“abcdef” 属于常量字符串,可以看到字符串保存在字符数组中的方式有两种(arr1和arr2),这两种方式结果是相同的。
1 字符串函数介绍与模拟实现
本章主要介绍字符串的库函数的使用和注意事项,并且尝试自己写代码实现。
1.1 长度不受限制的字符串函数
1.1.1 strlen
长度不受限制的的字符串函数,由于操作过程中以‘\0’作为字符串的结束标志,这就意味着当字符串缺少‘\0’时,会导致程序崩溃,从而使得程序的不安全性增高。在VS编译器中,当使用这一类函数是会报警告,一般在程序开头加上 #define _CRT_SECURE_NO_WARNINGS 1,可以忽略警告。
- 定义格式:
size_t strlen(const char* str);
-
函数功能:用于求str指向的字符串长度。
-
输入参数:
str:字符串的地址(类型:常字符型指针)。 -
输出参数:
字符串长度(类型:无符号整型) -
函数所在库:<string.h>
可以看到,strlen的参数是一个常字符型指针变量str,str内部存储的是字符串的首地址。返回值是一个无符号整型(size_t)数据,也即字符串的长度。strlen函数计算字符串长度时,是以’\0’为标志,‘\0’前面出现的字符个数算为字符串长度。
(1)使用例程
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
unsigned int length1 = 0, length2 = 0, length3 = 0;
char arr1[] = { 'a','b','c','d','e','f','\0' };
char arr2[] = "abcdef";
char* parr = "abcdef";
length1 = strlen(arr1);//将strlen的返回值赋值给length1
printf("%d\n", length1);
length2 = strlen(arr2);//将strlen的返回值赋值给length2
printf("%d\n", length2);
length3 = strlen(parr);//将strlen的返回值赋值给length3
printf("%d\n", length3);
system("pause");
return 0;
}
运行结果:
结果分析:
请注意:arr1和arr2是一样的。对于字符数组arr1,arr1内部保存的是字符串的首地址。arr1中,‘\0’前面的字符个数有6个,所以计算的结果为6,因为"abcdef" 在保存的时候会在末尾添加一个‘\0’,所以计算的出来的也是6。如果没有‘\0’计算得到的是一个随机值(在不同的编译器、不同的电脑得出的结果可能不同),错误示例代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
unsigned int length1 = 0;
char arr1[] = { 'a','b','c','d','e','f'};
length1 = strlen(arr1);//将strlen的返回值赋值给length1
printf("length1 = %d\n", length1);
system("pause");
return 0;
}
运行结果:
(2)模拟实现
第一种:计数器实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
size_t my_strlen(const char* arr)
{
size_t cnt = 0;//定义计数器cnt
while (*arr != '\0')
{
arr++;
cnt++;
}
return cnt;
}
int main()
{
char arr[] = "abcdef";
printf("%u\n", my_strlen(arr));
system("pause");
return 0;
}
第二种:递归实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
size_t my_strlen(const char* arr)
{
if (*arr != '\0')
{
arr++;
return 1 + my_strlen(arr);//递归
}
return 0;
}
int main()
{
char arr[] = "abcdef";
printf("%u\n", my_strlen(arr));
system("pause");
return 0;
}
第三种:利用地址实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
int my_strlen(const char* arr)
{
const char* ret = arr;//记录初始地址
while (*ret++);
return ret - arr-1;//地址相减
}
int main()
{
char arr[] = "abcdef";
printf("%d\n", my_strlen(arr));
system("pause");
return 0;
}
1.1.2 strcpy
定义格式:
char* strcpy(char* des, const char* src);
-
函数功能:将src指向的字符串复制到des指向的空间。
-
输入参数:
des:指向目标字符串的字符型指针,目标字符串存储的空间称为目标空间,des的值为目标空间的首地址;
src:指向源字符串的常字符型指针,源字符串存储的空间称为源空间,src的值为源空间的首地址。 -
输出参数:
输出目标空间的首地址(类型:字符型指针)。 -
注意事项:
源字符串必须以‘\0’结束;
源字符串中的’\0’也要拷贝到目标空间;
目标空间要足够大,以保证能够存放源字符串;
目标空间要保证可变。 -
函数所在库:<string.h>
(1)使用例程
① 正确用法
//将字符串“abcdef”复制到目标空间
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
char des[20];//目标空间
char src[]= { 'a','b','c','d','e','f','\0'};//源字符串
//char* src= "abcdef";//源字符串
printf("%s\n", strcpy(des, src));
system("pause");
return 0;
}
运行结果:
② 错误用法
错误1:源字符串没有以‘\0’结束
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
char des[20];//目标空间
char src[]= { 'a','b','c','d','e','f'};//字符串没有以'\0'结尾
printf("%s\n", strcpy(des, src));
system("pause");
return 0;
}
错误2:目标空间过小
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
char des[3];//目标空间过小
char src[]= { 'a','b','c','d','e','f','\0'};
printf("%s\n", strcpy(des, src));
system("pause");
return 0;
}
错误3:目标空间不可变
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
char* des = "123456789";//目标空间,des指向的是一个常量字符串
char src[]= { 'a','b','c','d','e','f','\0' };
printf("%s\n", strcpy(des, src));
system("pause");
return 0;
}
(2)模拟实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include<assert.h>
char* my_strcpy(char* des, const char* src)
{
char* ret = des;
assert(des != NULL);
assert(src != NULL);
while (*src != '\0')//拷贝字符串
{
*des = *src;
des++;
src++;
}
*des = *src;//将源字符串中的'\0'拷贝到目标空间
return ret;//返回目标空间地址
}
int main()
{
char des[20];//目标空间
printf("%s\n", my_strcpy(des, "abcdef"));//测试
system("pause");
return 0;
}
将以上代码简化:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include<assert.h>
char* my_strcpy(char* des, const char* src)
{
char* ret = des;
assert(des != NULL);
assert(src != NULL);
while (*des++ = *src++);//当*src为'\0'时,退出循环,也即完成复制
return ret;//返回目标空间地址
}
int main()
{
char des[20];//目标空间
printf("%s\n", my_strcpy(des, "abcdef"));//测试
system("pause");
return 0;
}
1.1.3 strcat
定义格式:
char* strcat(char* des, const char* src);
-
函数功能:将src指向的字符串粘接到des指向的字符串后面。
-
输入参数:
des:指向目标字符串的字符型指针,目标字符串存储的空间称为目标空间,des的值为目标空间的首地址;
src:指向源字符串的常字符型指针,源字符串存储的空间称为源空间,src的值为源空间的首地址。 -
输出参数:
输出目标空间的首地址(类型:常字符型指针)。 -
注意事项:
源字符串必须以‘\0’结束;
源字符串中的’\0’也要粘接到目标空间;
目标空间要足够大,以保证能够存放源字符串;
目标空间要保证可变。 -
函数所在库:<string.h>
(1)使用例程
① 正确用法
//在字符串"I love "后面粘接字符串"you"
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char arr[20] = "I love ";
printf("%s\n", strcat(arr, "you"));
system("pause");
return 0;
}
运行结果:
(2)模拟实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
char* my_strcat(char* des, const char* src)
{
assert(des != NULL);
assert(src != NULL);
char* ret = des;
while (*des != '\0')//找到des中字符'\0'的位置
{
des++;
}
while (*des++ = *src++);//将src指向的字符串粘接在des指向的字符串后面
return ret;
}
int main()
{
char arr[20] = "I love ";
printf("%s\n", my_strcat(arr, "you"));//测试
system("pause");
return 0;
}
1.1.4 strcmp
定义格式:
int strcmp(const char* str1, const char* str2);
-
函数功能:将str1指向的字符串与str2指向的字符串进行比较。
-
输入参数:
str1:指向第一个字符串的常字符型指针;
str2:指向第二个字符串的常字符型指针。 -
输出参数:
两个字符串的对比结果(类型:整型)。
第一个字符串大于第二个字符串,则返回数字 1 ;
第一个字符串等于第二个字符串,则返回数字 0 ;
第一个字符串小于第二个字符串,则返回数字 -1 。 -
C语言标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字 ;
第一个字符串等于第二个字符串,则返回数字 0 ;
第一个字符串小于第二个字符串,则返回小于0的数字 。 -
函数所在库:<string.h>
(1)使用例程
① 正确用法
//比较字符串"abc"和字符串"def"的大小
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char* str1 = "abc";
char* str2 = "def";
int n = strcmp(str1, str2);
printf("%d\n",n);
system("pause");
return 0;
}
运行结果:
字符串比较是先从第一个字符比较,如果相等则对比第二个字符,以此类推。显然由于’a’ < ‘b’ 故判定为字符串"abc"小于字符串"def",所以输出为-1。
(2)模拟实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 != NULL);
assert(str2 != NULL);
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
if (*str1 - *str2 > 0)
return 1;
else
return -1;
}
int main()
{
char* str1 = "abc";
char* str2 = "def";
int n = my_strcmp(str1, str2);
printf("%d\n",n);
system("pause");
return 0;
}
实现C语言标准规定的strcmp函数
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 != NULL);
assert(str2 != NULL);
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
return *str1 - *str2;
}
int main()
{
char* str1 = "abc";
char* str2 = "def";
int n = my_strcmp(str1, str2);
printf("%d\n",n);
system("pause");
return 0;
}
运行结果:
1.2 长度受限制的字符串函数
1.2.1 strncpy
定义格式:
char* strncpy(char* des, const char* src, size_t num);
-
函数功能:将src指向的字符串的前num个字符拷贝到des指向的空间。
-
输入参数:
des:指向目标字符串的字符型指针,目标字符串存储的空间称为目标空间,des的值为目标空间的首地址;
src:指向源字符串的常字符型指针,源字符串存储的空间称为源空间,src的值为源空间的首地址;
num:拷贝的字符个数(类型:无符号整型)。 -
输出参数:
输出目标空间的首地址(类型:字符型指针)。 -
注意事项:
目标空间要足够大,以保证能够存放num个字符;
如果源字符串的前num个字符包含‘\0’时,则不足的字符自动以字符’\0’代替,直到num个;
如果源字符串的前num个字符不包含‘\0’时,则要把目标空间下标为num的字符手动置为’\0,防止出现乱码;
目标空间要保证可变。
- 函数所在库:<string.h>
(1)使用例程
姑且先把des、src称为字符串,这样方便叙述。
① 正确用法
情况1:当源字符串src前num个字符包含‘\0’时
//将数组src前num个字符复制到目标空间
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
char des[20];//目标空间
char src[] = { 'a','b','\0','d','e','f','\0'};//源字符串
size_t num = 12;
char* pdes = strncpy(des, src, num);
printf("%s\n", pdes);
system("pause");
return 0;
}
运行结果:
des的内部存储情况如下
结果分析:因为字符串src的第3和第7个字符为‘\0’,而num = 12,所以说字符串src前12个字符包含‘\0’,可以看到des的从第3个字符到第12个字符,被自动置为‘\0’。这说明strncpy复制到‘\0’,即使没到num也会提前停止复制,这一点要特别注意。
情况2:当字符串src前num个字符不包含‘\0’时,要手动将第num个字符置为’\0’
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
char des[20];//目标空间
char src[] = { 'a','b','c','d','e','f','\0'};//源字符串
size_t num = 4;
char* pdes = strncpy(des, src, num);
des[num] = '\0';//复制完毕后,为保险起见,将最后一个字符置为'\0'
printf("%s\n", pdes);
system("pause");
return 0;
}
运行结果:
内部存储:
从程序里可以看到,把目标空间下标为num的字符手动置为’\0’,所以输出没有出现乱码。如果不手动置为‘\0’,输出时就会出现乱码,示范代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
char des[20];//目标空间
char src[] = { 'a','b','c','d','e','f','\0' };//源字符串
size_t num = 4;
char* pdes = strncpy(des, src, num);
printf("%s\n", pdes);
system("pause");
return 0;
}
运行结果:
(2)模拟实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include <assert.h>
char* my_strncpy(char* des, const char* src, size_t num)
{
char* ret = des;
assert(des != NULL);
assert(src != NULL);
while (num--)
{
if (*src == '\0')
{
*des++ = '\0';
}
else
{
*des++ = *src++;
}
}
return ret;
}
int main()
{
char des[20];//目标空间
char src[] = { 'a','b','c','d','e','f','\0' };//源字符串
size_t num = 4;
char* pdes = my_strncpy(des, src, num);
des[num] = '\0';//将最后一个字符置为'\0',防止输出时出现乱码
printf("%s\n", pdes);
system("pause");
return 0;
}
1.2.2 strncat
定义格式:
char* strncat(char* des, const char* src, size_t num);
-
函数功能:将src指向的字符串前num个字符粘接到des指向的字符串后面。
-
输入参数:
des:指向目标字符串的字符型指针,目标字符串存储的空间称为目标空间,des的值为目标空间的首地址;
src:指向源字符串的常字符型指针,源字符串存储的空间称为源空间,src的值为源空间的首地址。 -
输出参数:
输出目标空间的首地址(类型:常字符型指针)。 -
注意事项:
目标空间要进行初始化
目标空间要足够大,以保证能够存放源字符串;
目标空间要保证可变。 -
函数所在库:<string.h>
(1)使用例程
① 正确用法
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char arr[20] = "To be ";
printf("%s\n", strncat(arr, "or not to be", 2));
system("pause");
return 0;
}
运行结果:
②错误示范,目标空间未初始化
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char arr[20];
printf("%s\n", strncat(arr, "abc", 12));
system("pause");
return 0;
}
运行结果:
(2)模拟实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
char* my_strncat(char* des, const char* src, size_t num)
{
assert(des != NULL);
assert(src != NULL);
char* ret = des;
while (*des != '\0')//找到des中字符'\0'的位置
{
des++;
}
while(num--)
{
*des++ = *src++;//将src指向的字符串粘接在des指向的字符串后面
}
return ret;
}
int main()
{
char arr[20] = "To be ";
printf("%s\n", my_strncat(arr, "or not to be", 2));
system("pause");
return 0;
}
1.2.3 strncmp
定义格式:
int strncmp(const char* str1, const char* str2, size_t num);
-
函数功能:将str1指向的字符串前num个字符与str2指向的字符串的前num个字符进行比较。
-
输入参数:
str1:指向第一个字符串的常字符型指针;
str2:指向第二个字符串的常字符型指针。
num:需要比较的字符个数。 -
输出参数:
两个字符串的对比结果(类型:整型)。
第一个字符串大于第二个字符串,则返回数字 1 ;
第一个字符串等于第二个字符串,则返回数字 0 ;
第一个字符串小于第二个字符串,则返回数字 -1 。 -
函数所在库:<string.h>
(1)使用例程
① 正确用法
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char* str1 = "abcadflads";
char* str2 = "ablsdkjfal";
int n = strncmp(str1, str2, 2);
printf("%d\n", n);
system("pause");
return 0;
}
运行结果:
(2)模拟实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int my_strncmp(const char* str1, const char* str2, size_t num)
{
assert(str1 != NULL);
assert(str2 != NULL);
while (num--)
{
if (*str1 > * str2)// 第一个字符串大于第二个字符串,返回1
{
return 1;
}
else if (*str1 == *str2)// 第一个字符串等于第二个字符串,返回0
{
if (*str1 == '\0' || num == 0)
return 0;
str1++;
str2++;
}
}
return -1;// 第一个字符串小于第二个字符串,返回-1
}
int main()
{
char str1[] = { 'a','b','\0','f','e','f','\0' };
char str2[] = { 'a','b','\0','d','e','f','\0' };
int n = my_strncmp(str1, str2, 7);
printf("%d\n", n);
system("pause");
return 0;
}