前言
内存操作和字符串操作是C语言中很独特的地方
如果要控制内存里面的内容或者改变字符串的内容,能用哪些库函数来完成这些任务呢?
模板填写
调用名(小名): , 原名:
功能:
声明:
实现:
调用
注意
字符串
字符串不知道长度版本
最重要要的特性就是,字符串结尾是 ‘\0’
统计字符串长度
调用名(小名): strlen , 原名: string length
功能:统计字符串 0 前的字符个数
声明:
size_t strlen(const char *str);
实现:
size_t my_strlen(const char*str)
{
assert(str != NULL );
char * start = str;
char * end = str;
while(*end)
{
end++;
}
return end - str;
}
注意
- strlen 的返回值是 size_t ,是 unsigned int ,所以在运算时永远是正数
- 有多种实现方式,可以自己尝试
字符串复制
调用名(小名) strcpy , 原名 string copy
功能; 将字符串 A 拷贝到 字符串 B 中
声明:
char * strcpy(char* dest,const char* source);
实现:
char* my_strcpy(char* dest,const char* source)
{
assert(dest != NULL && source != NULL);
char * ret = dest;
while(*dest++ = *source++)
{
;
}
return ret;
}
使用
int main()
{
char a[20] = "I LOVE CHINA";
char b[20] = "###############";
printf("%s\n",strcpy(b,a));
return 0;
}
注意
- 目标空间要大
- source 数组要以 0 结尾
- 拷贝后dest 的 0 来源于 source
字符串比较
调用名(小名): , strcmp 原名:string compare
功能:
比较字符串中每一个字符的asc 码,找到第一个不同,如果第一个大,返回大于0 的数,如果第一个小,然后小于0的数,完全相等返回0
声明:
int strcmp(const char* first,const char* sec);
实现:
int strcmp(const char* first,const char* sec)
{
assert((first != NULL) && (sec != NULL));
//相等的略过
while(*first == *sec)
{
if(*sec == 0)
{
return 0;
}
else
{
first ++ ;
sec ++ ;
}
}
//不相等的比较
return *first - * sec;
}
注意
- 返回的不一定是 1 0 -1 ,是大于0 0 和小于0的数
- 实现逻辑就是 找到第一个不同的数 比较 asc码值
字符串追加
调用名(小名): strcat , 原名: string catch
功能:
在一个字符串数组后面追加另一个数组
声明:
(char*) strcat(char* dest,const char* source);
实现:
(char*) strcat(char* dest, const char* source)
{
assert(dest != NULL && source != NULL)
char* ret = dest;
while(*dest)
{
dest++;
}
while(*dest++ = *source++)
{
;
}
return ret ;
}
注意:
- 不能实现自追加,因为追加会影响数组本身的内容
字符串切割
调用名(小名):strtok , 原名:split str into tokens
功能:
按照一些符号切分数组,如果返回 NULL说明切分完毕
声明:
char * strtok ( char * str, const char * sep );
调用:
#define _CRT_SECURE_NO_WARNINGS 1
#include<string.h>
#include<stdio.h>
int main() {
char a[] = "carry@163.com";
char b[] = "@.";
char* str = NULL;
for (str = strtok(a, b); str != NULL; str = strtok(NULL, b))
{
printf("%s\n", str);
}
return 0;
}
结果:
注意
- 如果要对一个字符串数组切除多次,除了第一次传入要切割的数组 str ,之后形参位置 str 只要填入 NULL, strtok 会在当前的 str 一直切除下去
- strtok 有记忆性,形参 str 位置传入 NULL时启用
- 切除数组的方式就是加入 \0
字符串给了长度版本
复制
调用名(小名):strncpy
功能:
将 source 数组的 n 个字符串复制到 dest 上,如果 n > source 的长度,其余处补0
声明:
char * strncpy ( char * destination, const char * source, size_t num );
调用
int main()
{
char a[20] = "i love china";
char b[20] = "###########";
printf("%s\n",strncpy(b,a,6));
}
注意
- source 不足时 这个函数会一直补 0
追加
调用名(小名): strncat
功能:
在 dest 数组后追加 n 个字符
声明:
char * strncat ( char * destination, const char * source, size_t num );
调用:
#define _CRT_SECURE_NO_WARNINGS 1
#include<string.h>
#include<stdio.h>
int main() {
char a[20] = "i love china";
char b[20] = "###########";
//printf("%s\n", strncpy(b, a, 6));
printf("%s\n", strncat(b,a,6));
return 0;
}
注意
- 这个可以用来自追加,因为会自动补0
比较
调用名(小名): strncmp
功能:
比较两个字符串的前 n 项
声明:
int strncmp ( const char * str1, const char * str2, size_t num );
调用:
int main()
{
char a[] = "i am your father";
char b[] = "i am not your father";
int ret = strncmp(a,b,10);
if(ret == 0)
{
printf("两个字符串相同");
}
else
{
printf("两个字符串不同");
}
return 0;
}
寻找子串
调用名(小名): strstr
功能:
在字符串 a 中找到字符串 b
找到,返回第一次出现子字符串的地址
没找到返回 NULL 指针
声明:
const char * strstr ( const char * str1, const char * str2 );
实现:
思路:设立一个指针 str 记录子串在被寻字符串的起始位置,如果首字符和子串一样
就分离出一个新的地址,一直向下比,直到比到子串结束都一样,就返回 str
char* my_strstr(const char* str1,const char* str2)
{
char * p = NULL;
char* q = NULL;
while(*str1)
{
//第一个数相等
if(*str1 == *str2 && *str2 != 0 )
{
//储存子串开始的地方
p = str1;
q = str2;
while(*str1 == *str2)
{
str1++;
str2++;
}
if(*str2 == 0)
{
return p;
}
//找到了不同,而且不是0
else
{
p++;
str1 = p;
str2 = q;
}
}
else
{
str1++;
}
}
return NULL;
}
调用
int main()
{
char a[20] = "abbbcdef";
char b[20] = "bbb";
char* ret = my_strstr(a, b);
if (ret == NULL)
{
printf("没找到");
}
else
{
printf("%s",ret);
}
return 0;
}
对错误码的操作
调用名(小名): , 原名: strerror
功能:
每次出现错误时, 每种错误会储存在一个全局变量 errno 中
随后,用 strerror(errno) 可以直接打印出错误
声明:
char * strerror ( int errnum );
调用
int main()
{
dosomething();
//如果出现了错误
printf ("Error opening file unexist.ent: %s\n",strerror(errno));
//可以看到错误
}
注意
内存操作
内存复制
调用名(小名): memcpy,原名: memory copy
功能:
直接操作内存里头,然后把一个数组的 n 个字节复制到另一个数组
声明:
void * memcpy ( void * destination, const void * source, size_t num );
实现:
void* my_memcpy (void* dest,const void* source,size_t num)
{
char * ret = dest;
while(num--)
{
*(char*)dest = *(char*)source;
dest = (char*)dest+1;
source = (char*) source+1;
}
return ret;
}
调用
和 strcpy 是一样的
注意
- 实现的时候 dest ++ 和 source ++ 是容易出问题的
- 这种写法 是不能实现自 copy的,但是VS的写法不是这种写法,是 memmove的写法
内存移动
调用名(小名): memmove
功能:
不仅能够实现 memcpy 还能够实现 数组的一部分在数组内移动
声明:
void * memmove ( void * destination, const void * source, size_t num );
实现:
void* memmove(void* dest,const void* source,size_t num)
{
char * ret = dest;
while(num -- )
{
// source 在前,从后向前复制
if(dest > source)
{
*((char*)(dest)+num) = *((char*)(source)+num);
}
//source 在后,从前向后赋值
else
{
*(char*)dest = *(char*)source;
dest = (char*) dest + 1;
source = (char*) source + 1;
}
}
return ret;
}
调用
int main()
{
char a[20] = "abbbcdef";
char b[20] = "bbb";
char* ret = my_memmove(a+2, a, 4);
printf("%s", ret);
}
注意
- 部分编译器 memcpy 有 memmove的功能,但是有些是没有的,不要瞎用
- 实现过程要分一下情况
总结
- 在做字符串的题目时,可以尝试使用字符串函数
- 复制追加都需要保证 dest 函数足够大