;一.字符函数和字符串函数
1.1strlen
字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包
含 '\0' )。
参数指向的字符串必须要以 '\0' 结束。
注意函数的返回值为size_t,是无符号的( 易错 )
学会strlen函数的模拟实现
//递归
//int my_strlen1(const char* str)
//{
// assert(str != NULL);
// if (*str != '\0')
// return 1 + my_strlen(str + 1);
// else
// return 0;
//}
//
指针-指针
//int my_strlen2(const char* str)
//{
// const char* start = str;
// assert(str != NULL);
// while (*str)
// {
// str++;
// }
// return str - start;
//}
//循环
//int my_strlen(const char* str)
//{
// assert(str != NULL);
// int count = 0;
// while (*str != '\0')
// {
// count++;
// str++;
// }
// return count;
//}
库里面实现的是size_t的
注意:
-3被当成无符号数也是大于0的,所以一直都输出大于号
1.2strcpy
Copies the C string pointed by source into the array pointed by destination, including the
terminating null character (and stopping at that point).
源字符串必须以 '\0' 结束。
会将源字符串中的 '\0' 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串。
目标空间必须可变。
学会模拟实现。
//char* my_strcpy(char* dest, const char* src)
//{
// char* ret = dest;
// assert(dest && src);
// while (*dest++ = *src++)
// {
// ;
// }
// return ret;
//}
1.3strcat
Appends a copy of the source string to the destination string. The terminating null character
in destination is overwritten by the first character of source, and a null-character is included
at the end of the new string formed by the concatenation of both in destination.
源字符串必须以 '\0' 结束。
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改。
字符串自己给自己追加,如何?
解决不了这种问题
//char* my_strcat(char* dest, const char* src)
//{
// assert(dest && src);
// char* ret = dest;
//
// //1. 找目标空间的\0
// while (*dest)
// {
// dest++;
// }
// //2. 追加
// while (*dest++ = *src++)
// {
// ;
// }
// return ret;
//}
1.4 strcmp
This function starts comparing the first character of each string. If they are equal to each
other, it continues with the following pairs until the characters differ or until a terminating
null-character is reached.
标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
那么如何判断两个字符串 ?
1.5 strncpy
如果n大于源字符串长度,则会补'\0'
//dest为目标数组,src为源数组,n为要复制的字符个数
char* My_strncpy(char* dest, const char* src, int n)
{
assert(dest != NULL);//保证dest非空
assert(src != NULL); //保证src非空
char* ret = dest; //将dest首地址储存在ret中,在之后dest++运算中,可以方便找到
while (n) //一次复制一个字符,要复制n次
{
*dest = *src; //复制
src++; //源地址往后+1
dest++; //目标地址往后+1
n--; //跳出循环条件
}
return ret; //返回目的数组的首地址
}
1.6 strncmp
//以null结尾的字符串 const修饰防止字符串被修改,进行保护
int My_strncmp(const char* str1, const char* str2, int n)
{
if (!n) //n=0时,无字符要比,直接return 0
return 0;
while (--n && *str1 && *str1 == *str2) //当字符相等且不为’\0‘时比较下个字符,知道n=0比完
{
str1++;
str2++;
}
return *str1 - *str2;//字符不相等时,(*str1 - *str2)可以满足返回值正负的需求
}
1.7 strstr
看我的另一篇博客,KMP算法
1.8strncat
//dest为前面的字符串,src为后面要加的字符串,n为要拷贝的字符个数
char* My_strncat(char* dest, const char* src, int n)
{
char* ret = dest; //将dest首地址储存在ret中,在之后dest++运算中,可以方便找到
assert(dest != NULL && src != NULL); //保证dest、src非空
while (*dest != '\0')//用指针往后一个个找,找到dest结尾的‘\0’
dest++;
while (n && (*dest++ = *src++) != '\0')//把src里的字符一个个放入dest后
//(*dest++ = *src++) 表示先把*src赋给*dest,再把两个指针同时往后移一位(方便下次循环)
n--; //循环跳出条件
*dest = '\0'; //字符追加完成后,再追加’\0’
return ret; //返回dest字符串起始地址
}
1.9strtok
sep参数是个字符串,定义了用作分隔符的字符集合
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标
记。strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:
strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容
并且可修改。)
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串
中的位置。
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标
记。
如果字符串中不存在更多的标记,则返回 NULL 指针。
strtok 函数大概的运行实现
#include <string.h>
#include <stdio.h>
int main () {
char str[80] = "aaa - bbb - ccc";
const char s[2] = "-"; //字符串里可以是一个分隔符,也可以是分隔符的集合
char *token;
/* 获取第一个子字符串 */
token = strtok(str, s);
/* 继续获取其他的子字符串 */
while( token != NULL ) {
printf( "%s\n", token );
token = strtok(NULL, s);
}
return(0);
}
实现代码:
char *strtok(char *str,const char *delim)
{
static char *next_start = NULL; //保存到静态存储区,函数返回后不会被销毁
if(str == NULL && (str = next_start) == NULL)
{
return NULL;
}
char *s = str;
const char *t = NULL;
while(*s)
{
t = delim;
while(*t)
{
if(*t == *s)
{
next_start = s + 1;
if(s == str) //第一个字符就是分隔符
{
str = next_start;
break;
}
else
{
*s = '\0';
return str;
}
}
else
{
t++;
}
}
s++;
}
return 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;
}
Edit & Run
perror=printf+strerror
1.***字符分类函数
#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![](https://img-blog.csdnimg.cn/direct/8c6dfe4cb0cb4be880c231c8f9a45e1e.png)
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 '\0' 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。
实现:
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);
}
使用:
#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.12 memmove
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理。
void * memmove ( void * dst, const void * src, size_t count)
{
void * ret = dst;
if (dst <= src || (char *)dst >= ((char *)src + count)) {
/*
* Non-Overlapping Buffers
* copy from lower addresses to higher addresses
*/
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
}
else {
/*
* Overlapping Buffers
* copy from higher addresses to lower addresses
*/
dst = (char *)dst + count - 1;
src = (char *)src + count - 1;
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst - 1;
src = (char *)src - 1;
}
}
return(ret);
}
1.13 memcmp
比较从ptr1和ptr2指针开始的num个字节
返回值如下:
int my_memcmp(const void *ptr1, const void *ptr2, int num)
{
//先强制类型转换
const char *tmp_ptr1 = (const char *)ptr1;
const char *tmp_ptr2 = (const char *)ptr2;
//循环num次,比较出就返回值
for (int i = 0; i < num; i++)
{
if (*(tmp_ptr1+i) > *(tmp_ptr2+i))
return 1;
else if (*(tmp_ptr1+i) < *(tmp_ptr2+i))
return -1;
}
//比较完num个字节了,仍没返回,完全相等返回值0。
return 0;
}
1.14memset
ptr是内存块首字节地址。
value是要赋的值,这个值应该不超过一个无符号字节大小即28-1。
num是需要赋值的字节数。
memset返回值是ptr首字节地址,并且是void*型,接收返回值需要强制类型转换。
void *my_memset(void *ptr, int value, int num)
{
//强制类型转换为无符号char*型
unsigned char *tmp_ptr = (unsigned char *)ptr;
//循环num次
for (int i = 0; i < num; i++)
{
//因为char是特殊的整型,所以可以直接赋值
*tmp_ptr++ = value;
}
return ptr;
}