0、前言
c语言中对字符和字符串的处理都很频繁,但c语言本身是没有字符串类型的,字符串通常放在常量字符串和字符数组中。
字符串常量适用于那些对它不做修改的字符函数中。
1、函数介绍
1.1 strlen
- strlen函数是以'\0'作为结束标志,它返回的是在字符串中'\0'前面的字符个数(不包括字符'\0')
- 参数指向的字符串必须要以'\0'作为结束标志
- 函数返回的类型是size_t类型,是无符号的(这是易错的)
- strlen函数的模拟实现很重要
代码如下
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abc";
char arr2[] = "abcdef";
if ((strlen(arr1) - strlen(arr2))<0)
{
printf("<");
}
else
{
printf(">");
}
return 0;
}
显然易知的是arr1的字符数是应该小于arr2的字符数的,但为什么结果却是大于呢?
由于strlen函数的返回类型是size_t无符号类型,所以当算出的是一个负数将被按着无符号计算,结果就将是一个很大的正数。
1.2 strcpy
- strcpy函数是将源字符串的内容拷贝到目的地数组空间中,包含字符‘\0’
- 源字符串必须以‘\0’结束
- 拷贝时会将‘\0’一起拷贝的目的地空间中
- 目的地空间必须足够大,以确保能存放源字符串
- 目标空间必须可变
- 同样strcpy的模拟实现是很重要的
1.3 strcat
- 字符串追加strcat函数,可以将源字符串追加到目的地字符串的后面。
- 源字符串必须以‘\0’结束。
- 目的地字符串必须足够大,以能够容得下源字符串。
- 目标空间必须可以被修改。
- 不可以用来字符串给自己追加
1.4 strcmp
- 字符串比较函数,可以用来比较两个字符串的大小,规则是从第一个字符开始比较其对应的ASCII码大小。
- 标准规则:
- 第一个字符串大于第二个字符串,返回大于0的数。
- 第一个字符串等于第二个字符串,则返回0。
- 第一个字符串小于第二个字符串,则返回小于0的数。
1.5 strncpy
- 拷贝num个字符从源字符串到目的地空间中
- 如果源字符串的字符的长度小于num,则拷贝完源字符串后在其后追加一个‘\0’,直到num个。
1.6 strncast
- 字符串追加函数,可以在目的地字符串后追加长度为num的字符串
- 规则是从源字符串num的长度追加到目的地字符串之后,并在最后追加‘\0’。
1.7 strncmp
- 比较出一个字符不一样或者字符串结束或者num个字符全部比较完。
1.8 strstr
- 这是一个在字符串中找子字符串的函数。
- 即在str1指向的主字符串中去找str2指向的字符串。
- 找到将返回匹配到的字符串的首地址
- 主字符串和子字符串都不可被修改
- 都需要有‘\0’结束
1.9 strtok
- delimiters参数是个字符串,定义了用作分隔符的字符集合。
- 第一个str参数是字符串,它包含0个或多个由delimiters中的0个或多个分隔符分割的标记。
- strtok函数找到str中下一个标记,并将其用‘\0’结尾,返回指向这个标记的指针(注:因strtok使用会改变字符串的内容,故一般在使用时都是切割的字符串都是临时拷贝的内容。)
- strtok函数第一个参数不为NUL,函数将找到str指向字符串中的第一个标记,并保存该标记的位置。
- strtok函数第一个参数为NULL,函数在同一个字符串中被保存的标记位置查找下一个标记。
- 如果字符串中不存在更多标记,返回NULL。
1.10 strerror
- 返回错误码所对应的信息
/* strerror example : error list */
#include <stdio.h>
#include <string.h>
#include <errno.h>//必须包含的头文件
int main ()
{
FILE * pFile;
pFile = fopen ("unexist.ent","r");
if (pFile == NULL)
printf ("Error opening file unexist.ent: %s\n",strerror(errno));
//errno: Last error number
return 0;
}
字符分类函数:
字符转换函数:
int tolower(int c);
int toupper(int c);
/* isupper example */
#include <stdio.h>
#include <ctype.h>
int main ()
{
int i=0;
char str[]="Test String.\n";
char c;
while (str[i])
{
c=str[i];
if (isupper(c))
c=tolower(c);
putchar (c);
i++;
}
return 0;
}
1.11 memcpy
- 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
- 这个函数在遇到 '\0' 的时候并不会停下来。
- 如果source和destination有任何的重叠,复制的结果都是未定义的。
1.12 memmove
- 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
- 如果源空间和目标空间出现重叠,就得使用memmove函数处理。
1.13 memcmp
- 比较从ptr1和ptr2指针开始的num个字节。
- 返回值如下:
2、库函数的模拟实现
2.1 模拟实现strlen
//模拟实现strlen()
//1.计数器
// 2.递归
// 3.指针-指针
#include<stdio.h>
//计数器实现
size_t my_strlen(const char* str)
{
int count = 0;
while (*str++ != '\0')
{
count++;
}
return count;
}
//递归实现
size_t my_strlen(const char* str)
{
if (*str != '\0')
{
return 1 + my_strlen(str + 1);
}
else
{
return 0;
}
}
//指针-指针实现
size_t my_strlen(const char* str)
{
const char* start = str;
while (*str != '\0')
{
str++;
}
return str - start;
}
int main()
{
char arr[] = "abcd";
size_t ret = my_strlen(arr);
printf("%zd\n", ret);
return 0;
}
2.2 模拟实现strcpy
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest,const char* src)
{
char* start = dest;
assert(dest && src);
while (*dest ++ = *src ++)
{
;
}
return start;
}
int main()
{
char arr1[] = "xxxxxxxxxxxx";
char arr2[] = "abcdef";
my_strcpy(arr1,arr2);
printf("%s\n", arr1);
return 0;
}
2.3 模拟实现strcat
#include<stdio.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
while (*dest !='\0')
{
dest++;
}
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[100] = "abcd";
char arr2[] = "abc";
my_strcat(arr1,arr2);
printf("%s\n", arr1);
return 0;
}
2.4 模拟实现strcmp
#include<stdio.h>
#include<assert.h>
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;
}
else
{
return -1;
}*/
return *str1 - *str2;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcq";
int ret = my_strcmp(arr1,arr2);
if (my_strcmp(arr1, arr2) > 0)
{
printf(">\n");
}
else if(my_strcmp(arr1,arr2)==0)
{
printf("=\n");
}
else
{
printf("<\n");
}
return 0;
}
2.5 模拟实现strstr
#include<stdio.h>
#include<assert.h>
const char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
const char* cp;//记录开始匹配的位置
const char* s1;//遍历str1指向的字符串
const char* s2;//遍历str2指向的字符串
cp = str1;
while (*cp)
{
s1 = cp;
s2 = str2;
while (*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");
}
else
{
printf("%s\n", ret);
}
return 0;
}
2.6 模拟实现memcpy
#include<string.h>
#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[] = { 3,4,5,6,7 };
my_memcpy(arr1,arr2,20);
return 0;
}
2.7 模拟实现memmove
#include<stdio.h>
#include<assert.h>
void* my_memmove(void* dest, void* src, size_t sz)
{
assert(dest && src);
void* ret = dest;
if (dest < src)
{
while (sz--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1; src = (char*)src + 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, arr + 2, 20);
return 0;
}