#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
/*字符函数和字符串函数
1、求字符串长度
strlen
2、长度不受限制的字符串函数
strcpy
strcat//字符串追加(不能给自己追加)
strcmp//返回值为无符号数!!
3、长度受限制的字符串函数
strncpy
strncat//可以给自己追加
strncmp
4、字符串查找
strstr//找到子串
strtok//取出带有分隔符的某一段的字符 例:将196.168.31.121 中的196取出
5、错误信息报告
strerror
6、字符操作
7、内存操作函数
memcpy
memmove
memset
memcmp
*/
c语言中对字符和字符串处理很频繁,但C语言本身没有字符串类型,因此,字符串通常放在常量字符串中或者字符数组中。
不引用临时变量实现strlen
#include<assert.h>
size_t my_strlen(const char* p)
{
assert(p != NULL);
if (*p != 0)
return 1 + my_strlen(p + 1);
else
return 0;
}
int main()
{
char arr[] = "abcdef";
int ret=my_strlen(arr);
printf("ret=%d", ret);
return 0;
}
创建一个strcpy函数
char* my_strcpy( char* dest, const char*src)
{
assert(dest != NULL);
assert(src != NULL);
char* temp = dest;
while (*dest++ = *src++)//先解引用将*src赋给*dest,再进行指针的++,最后进行判断*dest是否等于0(注:此处是指针向后挪了一位,但判断用的*dest还是原先那个)
{
;
}
return temp;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "bit";
my_strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
strcat(字符串末尾添加另一个字符串)函数的实现
char* my_strcat(char* dest,const char* src)
{
//断言,以防传入空指针
assert(dest&&src);
char* temp = dest;
//找到dest所指向的字符串上的\0
while (*dest)
{
dest++;
}
//字符串追加(将*src赋给*dest后进行后置++)
while (*dest++ = *src++)//先赋值,后dest,scr加加,最后判断*dest。(dest,scr指针++后没有进行解引用操作,所以判断时用的*dest是原先解引用的dest)
{
;
}
return temp;
}
int main()
{
char arr1[20] = "abcdef";
char arr2[] = "bit";
my_strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
实现strcmp函数
int my_strcmp(const char* dest,const char* src)
{
assert(dest&&src);
while (*dest == *src)//赋值可以简写,==判断不能!!!容易出错
{
if (*dest == 0)
return 0;
dest++;
src++;
}
return *dest - *src;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcad";
int ret=my_strcmp(arr1,arr2);
printf("%d\n", ret);
return 0;
}
长度受限制的字符串操作函数相对比长度不受限制的字符串操作长度函数更安全
/*strncpy有三个参数,最后一个参数是用来确定拷贝字符个数的。如果源字符串的长度小于我需要拷贝字符的个数,则拷贝完源字符串后,再目标后面追加0,直到目标个数个
若源字符串的长度大于我需要拷贝字符的个数,则拷贝完后不补\0*/
模拟实现strncpy
#include<string.h>
char* my_strncpy(char* dest,const char* src,int k)
{
assert(dest&&src);
char* temp = dest;
while (k&&(*dest++=*src++))
{
k--;
}
int ret=strlen(dest);
if (k)
{
while (--k)
{
*dest++ = '\0';
}
}
return temp;
}
int main()
{
char arr1[10] = "abcdef";
char arr2[] = "hello";
my_strncpy(arr1,arr2,8);
printf("%s\n", arr1);
return 0;
}
模拟实现strncat函数
char* my_strncat(char* dest,const char* src,int k)
{
assert(dest&&src);
char* temp = dest;
while (*dest)
{
dest++;
}
while (k&&(*dest++ = *src++))//先赋值后加加最后判断
{
k--;
}
if (k == 0)
*dest = '\0';
return temp;
}
int main()
{
char arr1[20] = "abcdef";
char arr2[] = "hello";
my_strncat(arr1, arr2, 5);
printf("%s\n", arr1);
return 0;
}
实现strstr函数
char* my_strstr(const char* p1,const char* p2)
{
assert(p1&&p2);//assert里面只能有一个参数,需要表示多个指针不为0时用&&并列
const char* s1 = p1;
const char* s2 = p2;
while (*s1)
{
char* temp = (char*)s1;//当*s1!=*s2时,追随s1一起动,直到找到相等的值
while ((*s2!=0)&&(*s1 == *s2))
{
s1++;
s2++;
if ((*s1 != *s2)&&(*s2!=0))//只有当两个值不等且s2没找完时才能进入此代码
{
s2 = p2;
temp = (char*)s1;//当遇到不想等的值时,将temp定位到该位置,以防该位置之后便找到子串
}
}
if(*s1!=0)//防止s1指向\0后依旧++导致的指针越界访问
s1++;
if (*s2 == 0)//判断是否是因为s2到了才退出循环的
return temp;
}
return NULL;
}
int main()
{
const char* p1 = "abcbcvbcdef";
const char* p2 = "bcd";
char* ret=my_strstr(p1,p2);
if (ret == NULL)
printf("没找到\n");
else
{
printf("找到了\n");
printf("%s\n", ret);
}
return 0;
}
/*strtok的用法:
声明:char* strtok(char* str,const char* sep);
1、sep参数是个字符串,定义了用作分隔符的字符的集合 eg:char sep[]=",.@"等
2、第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中的一个或者多个分隔符 eg:char arr[]="123.234,456@789"
3、strtok函数找到str中的分隔符标记,并将其改为\0,并返回一个指向这个标记的指针(注:strtok函数会改变被操作的字符串,所以在使用前应临时拷贝一份内容,并在
拷贝的内容上进行修改)
4、strtok中传入的第一个参数不是NULL时,函数将找到str中第一个标记,strtok将保存它在字符串中的位置
5、strtok函数的第一个参数为NULL时,函数将在同一个字符串中被保存的位置开始,查找下一个标记
6、如果在查找的过程中,字符串中不存在更多的标记,则返回NULL指针
*/
熟练掌握并运用strtok函数
int main()
{
char arr[] = "123,456.789@0";
char p[] = ",.@";
char buf[1024];
strcpy(buf, arr);//拷贝一个新的字符串
char* ret = NULL;//定义一个变量ret用来接收strtok返回的值
for (ret=strtok(buf, p);ret!=NULL;ret=strtok(NULL,p))//ret=strtok(buf, p)初始化ret(在for循环中初始化部分只会执行一次)。ret!=NULL 判断ret是否等于空指针,如果不等于,则进入循环。退出循环后执行ret=strtok(NULL,p),完成后继续循环,直到ret==NULL为止
{
printf("%s\n",ret);
}
return 0;
}
strerror——专门返回错误码所对应的错误信息的一种库函数
错误码——专门用来表示错误信息的一种整形数字,每个错误码都表示一个与其相对应的错误信息。
使用strerror函数时专门会用到参数errno——当库函数运行时发生错误,系统会将检测到的错误信息用错误码的形式返回,而errno是专门用来接收错误码的
#include<errno.h>
int main()
{
FILE* pfile=fopen("test.1", "r");//fopen是专门用来打开文件的操作符
if (pfile == NULL)
printf("%s\n", strerror(errno));//无该文件,返回No such file or directory
else
printf("打开成功\n");
printf("%s\n", strerror(0));
printf("%s\n", strerror(1));
printf("%s\n", strerror(2));
return 0;
}
字符转换函数——tolower,toupper
tolower——用来将大写字母转换成小写字母的函数
toupper——用来将小写字母转换成大写字母的函数
#include<ctype.h>
int main()
{
char arr[] = "I WAN TO A GIREFREND,qaq";
int i = 0;
while(arr[i])
{
arr[i]=tolower(arr[i]);//引用该库函数需引头文件ctype.h
i++;
}
printf("%s\n", arr);
return 0;
}
内存函数
memcpy——内存拷贝(无论什么类型的参数都可以拷贝。注:不能处理内存重叠的情况)
C语言标准说:memcpy可以拷贝不重叠的就可以了
熟练掌握memcpy的用法,并用函数实现它
void* my_memcpy(void* dest,const void* src,char count)
{
assert(dest&&src);
void* temp = dest;
while (count--)
{
*(char*)dest = *(char*)src;
dest=(char*)dest+1;//dest是不可修改的左值,因此+1后再赋给dest
src=(char*)src+1;//src是不可修改的左值,因此+1后再赋给src
}
return temp;
}
struct S
{
char name[20];
int age;
};
int main()
{
struct S s[3] = { {"张三",20},{"李四",25},{"王五",24} };
struct S buf[5] = { 0 };
void* ret = my_memcpy(buf,s,sizeof(s));
return 0;
}
memmove——专门用来处理内存重叠的情况,相当于memcpy的升级版
熟练掌握memmove并自己实现这个函数
void* my_memmove(void* dest,const void* src,char num)
{
void* temp = (void*)src;
if (dest > src)//判断dest是否在src的后面
{
while (num--)//先判断,num再--
{
*((char*)dest + num) = *((char*)src + num);//此时的num为--后的num
}
}
else
{
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return temp;
}
return dest;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] ={ 0,1,2,3,4};
int i = 0;
//void* ret=memmove(arr2+2, arr1, sizeof(arr1));
void* ret = my_memmove(arr1+3, arr1, 20);
for(i=0;i<10;i++)
printf("%d ", arr1[i]);
return 0;
}
memcmp——内存比较——可以比较任意类型变量的大小
熟练掌握memcmp的使用方法
int main()
{
int arr1[] = { 1,2,3,4,5,6 };
int arr2[] = { 1,2,3,6,5,5 };
int ret=memcmp(arr1, arr2, 12);//第三个参数仍然是需要比较的字节个数。所比较的结果取决于内存中每个字节的大小
printf("%d\n", ret);
return 0;
}
memset——内存设置
int main()
{
char arr[10] = {};
memset(arr, '*', 10);
int arr1[10] = {};
memset(arr1, 1, 10);//注:由于memset修改的是存在内存空间的字节数而非整形,所以此时所得到的结果不是10个1,而是10个字节空间为1的数。
return 0;
}