字符函数,重点字符串函数及内存函数模拟实现及总结
前言
求字符串长度
strlen
长度不受限制的字符串函数
strcpy
strcat
strcmp
长度受限制的字符串函数介绍
strncpy
strncat
strncmp
字符串查找
strstr
strtok
错误信息报告
strerror
字符操作
内存操作函数
memcpy
memmove
memset
memcmp
一,字符函数,重点字符串函数
1,求字符串长度strlen
size_t strlen ( const char * str );
(1)字符串已经 ‘\0’ 作为结束标志。
(2)strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )。
(3)size_t strlen ( const char * str );
参数指向的字符串必须要以 ‘\0’ 结束。
注意:函数的返回值为size_t,是无符号的( 易错 ),同时不要和sizeof()搞混,sizeof是操作符,它计算的是内存大小,单位是字节,sizeof计算的对象不仅仅是字符,比如结构体等
例题
1234长度明显比123456小应该打印小于号但是结果是大于号。为什么呢?因为无符号-无符号数的结果会被当成无符号数,无符号数的补码原码反码相同
4-6=-2
原码 10000000 00000000 00000000 00000010
反码 11111111 11111111 11111111 11111101
补码 11111111 11111111 11111111 11111111
计算机内存中存的是补码直接当成无符号处理无符号原码与补码相同,符号位为有效位,打印出来就是一个很大的正数
strlen()模拟实现
在int my_strlen( const char * q)//const关键词修饰防止修改字符
{
int count = 0;
while (*q != '\0')
{
count++;//记录个数
q++;//指针指向
}
return count;
2,拷贝字符函数strcpy()
char* strcpy(char * destination, const char * source );
(1)源字符串必须以 ‘\0’ 结束。
(2)会将源字符串中的 ‘\0’ 拷贝到目标空间。
(3)目标空间必须足够大,以确保能存放源字符串。
目标空间必须可变。
(1)模拟实现`
char* my_strcpy(char* p, const char* q)
{
char* s = p;
/*while (*q != '\0') 写法一
{
*p = *q;
p++;
q++;
}//循环条件将'\0'作为判断条件,结果并没有将字符'\0'拷贝,循环结束要重新将字符0拷贝
*p = *q;//把字符'\0'拷贝 */
//写法二循环条件即将字符拷贝到目的地址的同时又拷贝了字符'\0'
while (*p++ = *q++)
{
;
}
return s;
}
3,追加字符函数strcat()
char * strcat ( char * destination, const char * source );
(1)源字符串必须以 ‘\0’ 结束。
(2)目标空间必须有足够的大,能容纳下源字符串的内容。
(3)目标空间必须可修改。
(1)模拟实现`
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
//1. 找目标空间中的\0
while (*dest)
{
dest++;//循环结束这时dest是指向'\0'的位置
}
while (*dest++ = *src++)
{
;
}
return ret;
注意:字符串自己给自己追加,如何?
//如果对自己追加的话,会把自己的字符'\0'替换,而这个函数遇到'\0' 停止追加,没有了字符'\0',就不会停止追加导致程序会
}
4,字符比较函数strcmp()
int strcmp ( const char * str1, const char * str2 );
标准规定:
(1)第一个字符串大于第二个字符串,则返回大于0的数字
(2)第一个字符串等于第二个字符串,则返回0
(3)第一个字符串小于第二个字符串,则返回小于0的数字
那么如何判断两个字符串?
(1)模拟实现
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);//断言,防止指针为空
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
return (*str1 - *str2);
}
5,strncpy()
char * strncpy ( char * destination, const char * source, size_t num );
(1)拷贝num个字符从源字符串到目标空间。
如果源字符串的长度小于num,则拷贝完源字符串之后, 在目标的后边追加0,直到num个。
int main()
{
char arr1[20] = "abcdef";
char arr2[] = "xxx";
strncpy(arr1, arr2, 1);//arr1 xbcdef
//strncpy(arr1,arr2,4) 源字符串的长度小于
// printf("%s %d", arr1,strlen(arr1));
return 0;
}
6,strncat()
char * strncat ( char * destination, const char * source, size_t num );
(1)模拟实现
int main()
{
char arr1[20] = "abcd\0efyyyyyyyy";
char arr2[] = "xxxxxxxxx";
strncat(arr1, arr2, 3);
printf("%s", arr1);
}
只将arr2的三个字节拷贝到arr1中,遇到\0就开始拷贝最后拷贝完添加\0
7,strncmp()
int strncmp ( const char * str1, const char * str2, size_t num );
int main()
{
char arr1[] = "abcqwertyuiop";
char arr2[] = "abcdef";
printf("%d\n", strncmp(arr1, arr2, 3)); //vs 中字符(可以根据ASCII值比较)想等返回值为0,大于返回1,小于返回-1
return 0;
}
前三个字符都想等就返回0
8,求子串函数strstr()
char * strstr ( const char *str1, const char * str2);
(1)strstr函数如果找到匹配的字符串就会返回指向目标字符匹配成功的字符第一个元素地址(也就是str数组中s地址)
通过strstr()函数找到"This is a simple string"的子串simple,函数的返回值是str数组中simple的首位字符‘s’的地址,然后拷贝sample替换掉simple
(1)模拟实现
char* my_strstr(char* str1, char* str2)
{
char* cp = str1;//记录要返回的指针
char* s1 = cp;//遍历
char* s2 = str2;//遍历
if (*str2 == '\0')
return str1;
while (*cp)//遍历
{
//开始匹配
s1 = cp;
s2 = str2;
while (*s1 && *s2 && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
return cp;
cp++;
}
return NULL;
}
9,strtok()函数
strtok
char * strtok ( char * str, const char * sep );
(1)sep参数是个字符串,定义了用作分隔符的字符集合。第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记
(2)strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:
strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容
并且可修改。)
(3)strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
(4)strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标
记。
(5)如果字符串中不存在更多的标记,则返回 NULL 指针。
int main()
{
char arr[] = "zpengwei@yeah.net@666#777";
char copy[30];//拷贝
strcpy(copy, arr);
char sep[] = "@.#";
char* ret = NULL;
/*char* ret = strtok(copy, sep);
printf("%s\n", ret);
ret = strtok(NULL, sep);
printf("%s\n", ret);*/
// //ret = strtok(NULL, sep);
// //printf("%s\n", ret);
//
//简化,我们可以根据它的返回值,作为循环条件,同时因为它每找到一个标记,就会用'\0'
结尾,直接打印字符串就可以了
for (ret = strtok(copy, sep); ret != NULL; ret=strtok(NULL, sep))
{
printf("%s\n", ret);
}
return 0;
}
10,函数strerror()
char * strerror ( int errnum );
相比较perror()函数直接一步到位
库函数在执行时如果出现错位,就会将错误信息存放入错误码中
errnum 是一个全局变量
如果我们当前路径下没有这个test.txt 文件,fopen()函数返回为空指针
二,字符分类函数
函数 如果他的参数符合下列条件就返回真
一,
(1)iscntrl
任何控制字符
(3)isspace 空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’
(3)isdigit 十进制数字 0~9
(4)isxdigit 十六进制数字,包括所有十进制数字,小写字母af,大写字母AF
(5)islower 小写字母a~z
(6)isupper 大写字母A~Z
(7)isalpha 字母az或AZ
(8)isalnum 字母或者数字,az,AZ,0~9
(9)ispunct 标点符号,任何不属于数字或者字母的图形字符(可打印)
(10)isgraph 任何图形字符
(11)isprint 任何可打印字符,包括图像字符和空白字符
二,字符转换
int tolower(int c);
int toupper(int c);
举例:把大写字母转换成小写为例
int main()
{
/*printf("%d", isupper('q'));
printf("%d", islower('q'));
printf("%d", isdigit('8'));*/
char arr[40] ={0};
gets(arr); 键盘输入字符存入数组,可输入空格
char* p = arr;
while (*p)//把输入的字符转换成小写
{
if (isupper(*pp))//判断是大写字母
{
*p= tolower(*p);//通过指针直接修改
}
p++;
}
printf("%s", arr);
}
转换的结果
二,内存函数
(1)memcpy
void * memcpy ( void * destination, const void * source, size_t num );
memcpy函数从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 ‘\0’ 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。
(注意在vs中memcpy函数重叠复制结果是可行的但是在另一些编译器并不一定重叠复制结果是正确的)
1,模拟实现
//实现memcpy()
void* my_memcpy(void* src, const void* dest, unsigned int size)
{
void* ret = src;
while (size--)//参数是拷贝多少个字节
{
*((char*)src) = *((char*)dest);//因为是void*类型同时要实现一个一个字节拷贝所以要转char*类型
src = (char*)src + 1;//虽然前面前强转成char*类型但是都是暂时的,并不能永久改变,在次需要它的还得转
dest = (char*)dest + 1;
}
return ret;
}
(2)memmove
void * memmove ( void * destination, const void * source, size_t num );
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理。
1,模拟实现
void* my_memmove(void* dest, const void* src, size_t num)
{
assert(dest&&src);
void* ret = dest;
if (dest < src)
{
while (num--)
{
*((char*)dest) = *((char*)src);
src = (char*)src + 1;
dest = (char*)dest + 1;
}
}
else
{
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
return ret;
}
}
(3)memset
memset是计算机中C/C++语言初始化函数。作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作。
arr1中前两个值已经赋值成0
(4)memcmp
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
比较的值是以字节为单位的,就是看每一位是否相等,如果相等返回0,不相等返回-1;