目录
前言
一、求字符串长度
strlen
注意要点
strlen的模拟实现
二、长度不受限制的字符串函数
strcpy
注意要点
模拟实现
strcat
注意要点
模拟实现
strcmp
注意要点
模拟实现
三、长度受限制的字符串函数
strncpy
注意要点
模拟实现
strncat
注意要点
模拟实现
strncmp
注意要点
四、字符串查找
strstr
注意要点
模拟实现
strtok
注意要点
在c语言的使用中,要经常对字符串和内存进行各种各样的操作,但是c语言并没有字符串类型,也不能直接对内存进行复杂操作。我们通常用常量字符串或者字符数组来存储并操作字符串,用指针来对内存空间进行操作。这时,就需要有一些函数,通过将指针传入而对字符串和内存进行各种操作。而字符串常量只适用于对它不做修改的字符串函数。下面就是一些可以对字符和内存进行较复杂操作的函数。
size_t strlen ( const char * str );
相信大家对这个函数已经很熟悉了,现在主要对该函数的要点进行总结:
1.字符串以’\0‘作为结束的标志,strlen函数返回的是在字符串中’\0‘前面出现的字符个数(不包含’、0‘)。
2.参数指向的字符串必须以’\0‘结束,否则会越界访问。
3.注意函数返回值时size_t,默认无符号数。
例如:
#include
#include
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;
}
两个无符号数相减,结果会被看做是无符号数,所以不论是什么情况,结果都是str2>str1。
strlen实现有三种方式:
1.计数器
size_t my_strlen(const char* str)
{
int count = 0;
while (*str)
{
count++;
str++;
}
return count;
}
2.递归(不创建临时变量的计数器)
size_t my_strlen(const char* str)
{
if (*str)
{
return 1 + my_strlen(str + 1);
}
else
{
return 0;
}
}
3.指针-指针
size_t my_strlen(const char* str)
{
char* tmp = str;
while (*tmp)
{
tmp++;
}
return tmp - str;
}
char* strcpy ( char * destination , const char * source );
strcpy函数就是把source指针指向的字符串拷贝到destination指针指向的数组中,包括结束字符。
1.源字符串必须以 '\0' 结束。
2.会将源字符串中的 '\0' 拷贝到目标空间。
3.目标空间必须足够大,以确保能存放源字符串。
4.目标空间必须可变,不能是常量或常变量。
5.返回值是目标空间起始地址。
例如:
#include
#include
int main()
{
char arr1[10] = "abcdef";
char arr2[3] = "ab";
printf("%s",strcpy(arr1, arr2));
return 0;
}
函数执行前:
函数执行后:
原来的abc被ab\0覆盖掉了。
#include
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest;//先用指针临时存储,以便最后返回目的地址
assert(dest && src);//断言,防止指针是空指针
while (*dest++ = *src++)//巧妙的思路,根据操作符优先级依次解引用、赋值、判断、自增
{
;
}
return ret;
}
char * strcat ( char * destination , const char * source );
将源字符串的副本追加到目标字符串。目标中的终止空字符被源字符串的第一个字符覆盖,并且在目标字符串中由两者串联形成的新字符串的末尾包含一个空字符。
1.源字符串必须以 '\0' 结束。
2.目标空间必须有足够的大,能容纳下源字符串的内容。
3.目标空间必须可修改。
4.字符串自己给自己追加,会出错。
前面三个不用多讲,但最后一个自己跟自己追加,会怎样呢?我们来试验一下:
通过调试,我们清楚地看到,在执行这个函数时发生了错误,而错误原因正是arr数组后面的’\0‘被覆盖掉了,函数没有办法结束,无限地在后面追加,陷入死循环。
#include
#include
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);
char* tmp = dest;
while (*dest)
{
dest++;
}
while (*dest++ = *src++)
{
;
}
return tmp;
}
int strcmp ( const char * str1, const char * str2 );
将字符串 str1 与字符串 str2 进行比较。
1.此函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续以下对,直到字符不同或达到终止空字符。
2.此函数执行字符的二进制比较。
3.标准规定:
例如:
#include
#include
int main()
{
char arr1[10] = "abcdef";
char arr2[10] = "abcde";
if (0 == strcmp(arr1, arr2))
{
printf("arr1=arr2");
}
else if (strcmp(arr1, arr2) > 0)
{
printf("arr1>arr2");
}
else if (strcmp(arr1, arr2) < 0)
{
printf("arr1
输出结果为arr1>arr2
#include
int my_strcmp(const char* p1,const char* p2)
{
assert(p1 && p2);
while (*p1 == *p2)
{
if (*p1 == "\0")
return 0;
p1++;
p2++;
}
return *p1 - *p2;
}
char * strncpy ( char * destination , const char * source , size_t num );
从字符串中复制字符
1.将源字符串开头的num个字符复制到目标字符串。
2.如果在复制 num 个字符之前找到源字符串的末尾,则目标字符串将填充零,直到总共写入 num 个字符为止。
例如:
int main()
{
char arr1[10] = "abcdefghi";
char arr2[12] = "hijklm";
printf("%s",strncpy(arr1, arr2, 5));
return 0;
}
输出结果hijklfghi
#include
char* my_strncpy(char* dest,const char* str, size_t num)
{
char* tmp = dest;
assert(dest && str);
while (num--)
{
if (*str)
{
*dest++ = *str++;
}
else
{
*dest++ = '\0';
}
}
return tmp;
}
记得如果给的个数过多需要’\0‘来凑
char * strncat ( char * destination, const char * source, size_t num );
为字符串追加字符
1.将源字符串的开头的num个字符追加到目标字符串,外加一个终止空字符。
2.如果源字符串中字符串的长度小于 num,则仅追加终止空字符之前的内容。
3.长度限制,可以自己跟自己追加。
例如:
int main()
{
char arr[20] = "abcdefghi";
printf("%s", strncat(arr, arr, 9));
return 0;
}
结果是两个重复的abcdefghi
#include
char* my_strncat(char* dest, const char* src, size_t n)
{
assert(dest);
assert(src);
char* ret = dest;
while (*dest)
{
dest++;
}
while (n--)
{
*dest++ = *src++;
}
*dest = '\0';
return ret;
}
int strncmp ( const char * str1, const char * str2, size_t num );
比较两个字符串的字符
1.此函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续比较,直到字符不同,或达到终止的空字符,或者直到两个字符串中的 num 字符匹配。
2.
例如:
#include
#include
int main ()
{
char str[][5] = { "R2D2" , "C3PO" , "R2A6" };
int n;
puts ("Looking for R2 astromech droids...");
for (n=0 ; n<3 ; n++)
if (strncmp (str[n],"R2xx",2) == 0)
{
printf ("found %s\n",str[n]);
}
return 0;
}
char * strstr ( const char * str1 , const char * str2 );
1.返回str2在str1中第一次出现的位置,如果没有,返回NULL。
2.以’\0‘作为结束标志。
例如:
int main()
{
char arr1[20] = "abbbcdefbbc";
char arr2[10] = "bbc";
if (strstr(arr1, arr2))
{
printf("%s\n", strstr(arr1, arr2));
}
else
{
printf("没找到\n");
}
return 0;
}
结果是从bbc开始向后打印。
char* my_strstr(const char* dest, const char* str)
{
char* cp = (char*)dest;//把一个安全的指针交给一个不太安全的指针,权限放大,有警告,因此强制类型转换
char* s1, * s2;
while (*cp)
{
s1 = cp;//以cp作为标记,在匹配失败后能重新返回到这个点
s2 = (char*)str;
while (*s1 == *s2 && *s1 && *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return cp;
}
cp++;
}
return NULL;
}
char * strtok ( char * str , const char * sep );
1.sep参数是个字符串,定义了用作分隔符的字符集合
2.第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
#include
#include
int main()
{
char* p = "[email protected]";
const char* sep = ".@";
char arr[30];
char* str = NULL;
strcpy(arr, p);//将数据拷贝一份,处理arr数组的内容
for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
{
printf("%s\n", str);
}
}
To Be Continued...