C语言本身没有字符串类型,字符串通常放在常量字符串或者字符数组中,字符串常量适用于那些对它不做修改的字符串函数。
目录
2. 字符串函数strncpy strncat strncmp
3. 字符串函数strstr strtok strerror
1. 字符串函数strlen strcpy strcmp
1.1 strlen函数介绍
使用需引用头文件<string.h>
size_t strlen(const char*string);
功能:字符串以'\0'作为结束标志,strlen函数返回的值为在字符串'\0'之前出现的字符个数
返回类型为size_t,无符号整型
1.1.1 strlen使用与模拟
一、使用
在使用strlen时,只需要把地址传入即可
#include <stdio.h>
int main()
{
const char*str1 = "abcdef";
const char*str2 = "bbb";
if(strlen(str2)-strlen(str1)>0)
{
printf("str2>str1\n");
}
else
{
printf("srt1>str2\n");
}
return 0;
}
"abcdef"和"bbb"都为常量字符串,相当于把a,b的地址分布存入到str1和str2中,其中的strlen(str1)为6,strlen(str2)为3。
3-6原本应为-3,因为都为无符号数,所以其结果大于0。输出"str2>str1"。
所以在比较长度大小时要避免使用这种相减的方式,直接进行比较大小或强制转换为int型后进行相减。
二、三种模拟实现
计数器
int istrlen(const char*string)
{
int count=0;
while(*string != '\0')
{
count++;
string++;
}
return count;
}
递归
int istrlen1(const char* string)
{
if (*string != '\0')
{
return 1 + istrlen1(++string);//string++错,后置++
}
else
return 0;
}
指针-指针
int istrlen2(const char* string)
{
const char* p = string;
while (*string != '\0')
{
string++;
}
return string - p;
}
1.2 strcpy函数介绍
使用需引用头文件<string.h>
char* strcpy(char* dest,const char* src);
功能:源字符串必须以'\0'结束;目标空间必须足够大,以确保能存放源字符串;目标空间必须可 变;拷贝时会将源字符串中的'\0'拷贝到目标空间。
1.2.1 strcpy使用与模拟
一、使用
使用时strcpy(add1,add2);add1传入目标字符串地址,add2传入源字符串元素地址。
#include<stdio.h>
#include<string.h>
int main()
{
char arr[20]="asdfg";
char arr1[]="mmm";
strcpy(arr,arr1);
printf("%s\n",arr);
printf(arr);
return 0;
}
运行结果如下图,strcpy会将目标空间覆盖拷贝。
二、模拟
char* strcpy1(char* arr, const char* arr1)
{
char* p = arr;
assert(arr != NULL);//不满足条件报错
assert(arr1 != NULL);
while (*arr++ = *arr1++)//后置++,先赋值后++
{
;
}
return p;
}
1.3 strcat函数介绍
使用需引用头文件<string.h>
char* strcat(char* dest,const char* src);
功能:源字符串必须以'\0'结束;目标空间必须足够大,以确保能存放源字符串;目标空间必须可修改。
1.3.1 strcat使用与模拟
一、使用
使用时strcat(add1,add2);add1传入目标字符串地址,add2传入源字符串元素地址。
#include<stdio.h>
#include<string.h>
int main()
{
char arr[20]="wfghjkl";
char arr1[]="Zxcvbn";
strcat(arr,arr1);
printf("%s",arr);
return 0;
}
strcat会在目标字符串的'\0'处把源字符串内容拼接上去。
自己接自己,由于'\0'被覆盖,将会发生一直循环放入,直至这个数组空间存满,例如上述的arr数组
二、模拟
char* strcat1(char* str,const char* str1)
{
assert(str);
assert(str1);
char* p=str;
while (*str)//*str++多加一次超过\0
{
str++;
}
while ((*str++=*str1++))
{
;
}
return p;
}
1.4 strcmp函数介绍
使用需引用头文件<string.h>
int strcmp(char* str,const char* str1);
功能:第一个字符串大于第二个字符串,返回大于0的数字;
第一个字符串等于第二个字符串,返回等于0的数字;
第一个字符串小于第二个字符串,返回小于0的数字;
注意返回的具体数字每个编译器有不同的结果。
1.4.1 strcmp函数使用与模拟
一、使用
使用时strcmp(add1,add2);add1传入需比较字符串地址,add2传入要比较字符串元素地址。
#include<stdio.h>
#include<string.h>
int main()
{
char arr[20]="wfghjkl";
char arr1[]="Zxcvbn";
printf("%d",strcmp(arr,arr1));
return 0;
}
strcmp在进行比较是会从第一个字符开始,逐个进行其ASCII码值的大小比较,如果前几个字符相同,会一直比较到不同字符得出结果。
二、模拟
int strcmp1(const char* string, const char* string1)
{
assert(string != NULL);
assert(string1 != NULL);
while (*string||*string1)//有1个到了'\0'就该出结果了
{
if (*string > *string1)
{
return 1;
}
else if (*string == *string1)
{
string++;
string1++;
}
else
return -1;
}
return 0;
}
2. 字符串函数strncpy strncat strncmp
从前面的strcpy,strcat,strcmp中我们可以发现要进行拷贝,追加,比较时,无法指定源字符串中字符的具体个数,而这几个函数可以帮我们实现这个功能。
2.1 strncpy
其中的size_t num单位是字节。
char* strncpy(char* dest,const char* source,size_t num);
一、使用
#include<stdio.h>
#include<string.h>
int main()
{
char arr[]="qwerttyr";
char arr1[]="wasd";
strncpy(arr,arr1,2);
printf(arr);
return 0;
}
其模拟实现,以下两个函数的模拟介绍与strcpy,strcat,strcmp类似就不在做详细描述。
2.2 strncat
char* strncpy(char* dest,const char* source,size_t num);
2.3 strncmp
int* strncpy(const char* dest,const char* source,size_t num);
3. 字符串函数strstr strtok strerror
3.1 strstr函数介绍
使用需引用头文件<string.h>
char* strstr(const char* str,const char* str1);
功能:寻找在str字符串中是否含有str1字符串相同内容
找到后返回被查找字符串的首元素地址,失败返回NULL,
如果查的是char arr[]={0},也返回被查找字符串首元素地址。
3.1.1strstr函数使用与模拟
一、使用
使用时strstr(add1,add2);add1传入需要在被查找的字符串地址,add2传入需要查找的字符串地址
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="This is a simple string";
char * pch;
pch = strstr (str,"simple");
strncpy (pch,"sample",6);
puts (str);
return 0;
}
二、模拟
我们需要创建一个指针来存放被查找字符串首元素地址,和一个依次往后用于移动查找的指针
char* strstr1(const char*s1,const char*s2)
{
char* p = (char*)s1;//用于输出打印
char* ss2 = (char*)s2;//存放查找数组首地址
if (!*s2)//s2空把s1全打出来
{
return p;
}
while (*s1)//s1遍历完没结果就没找到
{
s2 = ss2;//找一次,找不到返回首地址
s1 = p;//怕bbbcsf,bbc这种情况一次加一个找
while (*s2 && *s1 && !(*s1 - *s2))//一直往后找s1,s2,,!(*s1-*s2)相等继续
{
s1++;
s2++;
}//s1和s2完全相同,会越界
if(!*s2)//s2到0说明找到了,越界了也行
{
return p;
}
p++;//没找到指标后移
}
return NULL;
}
3.2 strtok函数介绍
使用需引用头文件<string.h>
char* strtok(char* str,const char* set);
功能:切割函数
set参数是个字符串,定义了用作分隔符的字符集合
第一个参数指定一个字符串它包含了0个或者多个由set字符串中一个或者多个分隔符分割的标记。
3.2.1strtok函数使用
一、使用
strtok函数找到str中的下一个标记,并将标记变成\0 ,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "- Me, a handsome boy.";
printf("%p\n", str);
printf("M的地址%p\n", str+2);
printf("a的地址%p\n", str+6);
printf(".的地址%p\n", str+20);
char* pp;
printf("%s\n", str);
pp = strtok(str, " ,.-");
printf("%p\n", pp);
printf("%s\n", pp);
printf("\n\n\n", pp);
while (pp != NULL)
{
printf("%s\n", pp);
pp = strtok(NULL, " ,.-");
printf("%p\n", pp);
}
return 0;
}
简单来说strtok会把遇到字符前的所有分隔符切掉,并返回遇到第一个字符的地址,并将字符后的分隔符变为'\0',再次进行传参时可以直接传NULL,函数会记住上一次分割符的位置,接着往后找,循环这个过程,直到找到字符串结束位置'\0'。
3.3 strerror函数介绍
使用需引用头文件<string.h>
char* strerror(int errnum);
功能:返回错误码所对应的错误信息。
3.3.1strerror函数使用
一、使用
比如我们搜索网页时出现404,这个404就是一个错误码,用strerror用来查看其对应的错误信息
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
int main ()
{
int* p=(int*)malloc(INT_MAX);
if (p == NULL)
{
printf (strerror(errno));//errno为错误码,会自动放入,使用需引用头文件erron.h
}
perror("Malloc");//不需要使用printf可以直接打印出,Malloc:对应错误信息,字母大小写不影响
return 0;
}
4. 内存操作函数
上面介绍的函数只能对字符串进行操作,但是如果我们对整型数组或者其它类型数组操作就不适用,这个时候就轮到我们的内存操作函数登场了。
4.1 memcpy函数介绍
使用需引用头文件<string.h>或<memory.h>都可
void* memcpy (void* dest,const void* src,size_t num);
功能:函数从src的位置开始向后复制num个字节到dest的内存位置
此函数进行复制时遇到'\0'时并不会停下来
如果src和dest有任何的重叠,复制的结果是未定义的
提示:现在的编译器已经将memcpy和memmove的功能实现的一样,不会出现上述情况,但我们仍需了解一下原本二者的区别所在。
一、使用
#include <stdio.h>
#include <string.h>
int main()
{
int arr[] = { 1,1,1,1,1,1 };
int arr1[] = { 22,55,44 };
int i;
for (i = 0;i < 6;i++)
{
printf("%d ", *((int*)(memcpy(arr, arr1, 4))+i));
}
return 0;
}
当自己对自己复制产生空间重叠又会发生什么,我们在模拟的函数中可以进行观察
#include <stdio.h>
#include <string.h>
int main()
{
int arr[] = { 1,2,3,4,5,6 };
int i;
for (i = 0;i < 6;i++)
{
printf("%d ", *((int*)(memcpy(arr, arr+1, 8))+i));
}
return 0;
}
二、模拟
void* f_memcpy(void* dest, const void* src, int count)
{
assert(dest && src);
void* ret = dest;
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
当自己对自己进行复制发生空间重叠时
可以看到并没有像我们预想的那样变成asddf,因为f在往后复制时已经被d给覆盖掉了。
4.2 memmove函数介绍
使用需引用头文件<string.h>
void* memmove (void* dest,const void* src,size_t num);
功能:和memcpy的区别就是memmove函数处理时,源内存块和目标内存块是可以重叠的,也就是说我们可以在同一个数组内部进行操作,不需要两个数组。
一、使用
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] = "memmove can be very useful......";
memmove (str+20,str+15,11);
puts (str);
return 0;
}
二、模拟
分两种情况进行考虑
void* f_memmove(void* dest, const void* src, int count)//一个数组内部操作
{
assert(dest&&src);
void *mm= dest;
if (dest < src)//从前往后拷
{
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else
{
while (count--)
{
*((char*)dest + count) = *((char*)src + count);
}
}
return mm;
}
4.3 memset函数介绍
使用需引用头文件<string.h>或<memory.h>
void* memset (void* dest,int c,size_t num);
功能:以字节为单位初始化内存单元;
c为要每个字节要设置的数字;
num为要操作的字节数。
一、使用
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
int arr[] = { 1,2,8,7,9,2 };
memset(arr, 10, 4);
int i;
for (i = 0; i < 6; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
arr数组中的1,补码为00000000000000000000000000000001
转换为16进制为00 00 00 01 4个字节
每个字节变为10,变为16进制为A,0A 0A 0A 0A
计算得出168430090
4.4 memcmp函数介绍
使用需引用头文件<string.h>或<memory.h>
void* memcmp (const void* dest,const void* dest1,size_t num);
功能:比较dest和dest1指针开始的num个字节;
第一个字符串大于第二个字符串,返回大于0的数字;
第一个字符串等于第二个字符串,返回等于0的数字;
第一个字符串小于第二个字符串,返回小于0的数字;
比较过程和strcmp函数类似就不再做具体介绍了。