目录
前言
一. 函数介绍
1.1 strlen
1.2 strcpy
1.3 strcat
1.4 strcmp
1.5 strncpy
1.6 strncat
1.7 strncmp
1.8 strstr
1.9 strtok
1.10 strerror
1.11 字符分类函数
1.12 memcpy
1.13 memmove
1.14 memcmp
二. 函数的模拟实现
2.1 模拟实现strlen
2.2 模拟实现strcpy
2.3 模拟实现strcat
2.4 模拟实现strstr
2.5 模拟实现strcmp
2.6 模拟实现memcpy
前言
在之前的函数中我们学习了很多的函数,今天我们就再来讲几个常见并且实用的函数并且模拟实现他们。
一. 函数介绍
1.1 strlen
size_t strlen(const char* str);
首先是咱们的老熟人strlen函数,求字符串长度,字符串是以“\0”作为结束标志,而strlen函数他的返回值也就是在字符串中“\0”之前出现过的字符个数(并不包括“\0”),参数指向的字符必须以“\0”结束,注意strlen函数的返回类型需要是无符号的类型即size_t。
那如何使用呢?如下:
#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;
}
非常的方便。
1.2 strcpy
char* strcpy(char * destination, const char * source );
这个函数我们之前也见过,这是拷贝字符串函数,将destination拷贝上source的内容,需要注意source必须以“\0”结尾,并且拷贝会将“\0”拷入destination中,并且destination的空间需要足够大并且destination的空间内容是允许改变的。
1.3 strcat
char * strcat ( char * destination, const char * source );
这个函数的作用在于在目标代码的地方追加上新的内容,需要的注意的地方是source依然需要为“\0”结尾,destination所指向的空间需要足够大且能够修改。
1.4 strcmp
int strcmp ( const char * str1, const char * str2 );
字符串比较函数,用于比较字符串的大小,比较的方式是一位一位比较,若第一位相同则比较第二位,如若一直相同,则直到“\0”为止,标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
1.5 strncpy
char * strncpy ( char * destination, const char * source, size_t num );
这个函数比起strcpy仅仅只是多了一个n,参数也就只是多了一个num,那两者之间是不是有什么关联,又为什么会出现这个strncpy,原因是strcpy只是复制字符串,但不限制复制的数量,很容易造成缓冲溢出。strncpy要安全一些。strncpy能够选择一段字符输出,strcpy则不能。也就是说strncpy是可以选择拷贝的长度的相比较而言造成越界的可能性会大大降低。拷贝num个字符从source字符串到目标空间。如果source字符串的长度小于num,则拷贝完source字符串之后,在destination的后边追加0,直到num个。
1.6 strncat
char * strncat ( char * destination, const char * source, size_t num );
这个函数也是与strcat函数是几乎没有区别的,只是在原来的追加上限制了追加字符的个数,所需要的注意也与strncpy一样。
// strncat example
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[20];
char str2[20];
strcpy (str1,"To be ");
strcpy (str2,"or not to be");
strncat (str1, str2, 6);
puts (str1);
return 0;
}
1.7 strncmp
int strncmp ( const char * str1, const char * str2, size_t num );
strncmp函数与上面差别并不大,结束的条件是比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。
// strncmp example
#include <stdio.h>
#include <string.h>
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;
}
1.8 strstr
char * strstr ( const char *str1, const char * str2);
这个函数是判断你的字符串str2中第一次出现str1的指针,也就是查看str1是否为str2的子字符串。
// strstr example
#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;
}
1.9 strtok
char * strtok ( char * str, const char * sep );
这个函数相对而言就是比较陌生了
sep参数是个字符串,定义了用作分隔符的字符集合。
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改)
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
如果字符串中不存在更多的标记,则返回 NULL 指针。
// strtok example
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="- This, a sample string.";
char * pch;
printf ("Splitting string \"%s\" into tokens:\n",str);
pch = strtok (str," ,.-");
while (pch != NULL)
{
printf ("%s\n",pch);
pch = strtok (NULL, " ,.-");
}
return 0;
}
又或:
#include <stdio.h>
int main()
{
char *p = "123456789@hello.world";
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);
}
}
1.10 strerror
char * strerror ( int errnum );
该函数可以返回对应错误的信息。
// 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;
}
Edit & Run
1.11 字符分类函数
函数 | 如果他的参数符合下列条件就返回真 |
iscntrl | 任何控制字符 |
isspace | 空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v' |
isdigit | 十进制数字 0~9 |
isxdigit | 十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F |
islower | 小写字母a~z |
isupper | 大写字母A~Z |
isalpha | 字母a~z或A~Z |
isalnum | 字母或者数字,a~z,A~Z,0~9 |
ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) |
isgraph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符和空白字符 |
字符转换
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.12 memcpy
void * memcpy ( void * destination, const void * source, size_t num );
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。这个函数在遇到 '\0' 的时候并不会停下来。如果source和destination有任何的重叠,复制的结果都是未定义的。
// memcpy example
#include <stdio.h>
#include <string.h>
struct {
char name[40];
int age;
} person, person_copy;
int main ()
{
char myname[] = "Pierre de Fermat";
// using memcpy to copy string:
memcpy ( person.name, myname, strlen(myname)+1 );
person.age = 46;
// using memcpy to copy structure:
memcpy ( &person_copy, &person, sizeof(person) );
printf ("person_copy: %s, %d \n", person_copy.name, person_copy.age );
return 0;
}
1.13 memmove
void * memmove ( void * destination, const void * source, size_t num );
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。如果源空间和目标空间出现重叠,就得使用memmove函数处理。
// memmove example
#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;
}
1.14 memcmp
int memcmp ( const void * ptr1,const void * ptr2,size_t num );
比较从ptr1和ptr2指针开始的num个字节.
// memcmp example
#include <stdio.h>
#include <string.h>
int main ()
{
char buffer1[] = "DWgaOtP12df0";
char buffer2[] = "DWGAOTP12DF0";
int n;
n=memcmp ( buffer1, buffer2, sizeof(buffer1) );
if (n>0) printf ("'%s' is greater than '%s'.\n",buffer1,buffer2);
else if (n<0) printf ("'%s' is less than '%s'.\n",buffer1,buffer2);
else printf ("'%s' is the same as '%s'.\n",buffer1,buffer2);
return 0;
}
二. 函数的模拟实现
2.1 模拟实现strlen
对于strlen的模拟实现我们是有三种方法的:
//计数器方式
int my_strlen(const char * str)
{
int count = 0;
while(*str)
{
count++;
str++;
}
return count;
}
//递归
int my_strlen(const char * str)
{
if(*str == '\0')
return 0;
else
return 1+my_strlen(str+1);
}
//指针-指针的方式
int my_strlen(char *s)
{
char *p = s;
while(*p != ‘\0’ )
p++;
return p-s;
}
2.2 模拟实现strcpy
//1.参数顺序
//2.函数的功能,停止条件
//3.assert 断言指针是否有效
//4.const修饰指针,指针不可改变
//5.函数返回值
char *my_strcpy(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while((*dest++ = *src++))
{
;
}
return ret;
}
2.3 模拟实现strcat
char *my_strcat(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while(*dest)
{
dest++;
}
while((*dest++ = *src++))
{
;
}
return ret;
}
2.4 模拟实现strstr
对于strstr函数的实现是有一些复杂的,首先将str2的指针需要不断移动,str1找到了之后也需要移动,如果错误,则需要返回str1的初始地址,所以我们需要创建三个指针变量,代码如下:
char * strstr (const char * str1, const char * str2)
{
char *cp = (char *) str1;
char *s1, *s2;
if ( !*str2 )
return((char *)str1);
while (*cp)
{
s1 = cp;
s2 = (char *) str2;
while ( *s1 && *s2 && !(*s1-*s2) )
s1++, s2++;
if (!*s2)
return(cp);
cp++;
}
return(NULL);
2.5 模拟实现strcmp
实现strcmp则需要将dest与src的指针不断往后移动直到比较出大小为止,并且返回值相等时返回0,大于时返回1,小于时返回-1,代码如下:
int my_strcmp (const char * src, const char * dst)
{
int ret = 0 ;
assert(src != NULL);
assert(dest != NULL);
while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst)
++src, ++dst;
if ( ret < 0 )
ret = -1 ;
else if ( ret > 0 )
ret = 1 ;
return( ret );
}
2.6 模拟实现memcpy
实质上与strcpy差异并不大,需要注意我们并不知道传入的类型,需要采用void*指针,在后续步骤中要记得将类型转换为char*类型。
void * memcpy ( void * dst, const void * src, size_t count)
{
void * ret = dst;
assert(dst);
assert(src);
/*
* copy from lower addresses to higher addresses
*/
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
return(ret);
}
这就是本章的内容,咱们下期再见!