今天我学习了C语言常见的几种库函数,我们来模拟实现一下!
字符串函数:
strlen()
strcpy()
strcat()
strcmp()
strncpy()
strncat()
strstr()
内存函数:
memcpy()
memmove()
memcmp()
memset()
字符串函数
函数细节代码内有详细的注释,望垂阅!
这是下面函数的接口代码,可以参考运行:
int main()
{
//char str1[] = "hello world!";
//char str2[] = "lo ";
strstr返回char*,%c返回char,如果要得到一个字符,就需要对char解引用
//printf("%c\n", *mystrstr(str1, str2)); mystrstr
//printf("%s\n", mystrstr(str1, str2));
///
//char str1[] = "hello bit!";
//char str2[] = "hello world!"; mystrncmp
//printf("%d\n",mystrncmp(str1,str2,7));
///
//char str1[1024] = "hello bit!";
//char str2[] = "hello World!"; mystrncat
//printf("%s\n", mystrncat(str1, str2, 15));
///
//char str1[1024] = { 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x','x','x'};
//char str2[] = "hello world!"; mystrncpy
//printf("%s\n",mystrncpy(str1,str2,7));
///
//char str1[] = "abcd";
//char str2[] = "aecd"; mystrcmp
//printf("%d\n", mystrcmp(str1, str2));
///
//char dest[1024] = "abcdefg";
//char src[] = "hijklmn"; mystrcat
//printf("%s\n", mystrcat(dest, src));
///
//char dest[] = "xxxxxxxxxxx";
//char src[] = "hijklmn"; mystrcpy
//printf("%s\n", mystrcpy(dest, src);t);
/
}
strlen()
//strlen()
// size_t strlen ( const char * str );
//计算‘\0’之前的字符个数
//注意条件: ①返回值是 size_t ②参数指向的字符串必须以‘\0’结束 ③参数是const char* 类型
//返回值: size_t %d / %u
size_t mystrlen(const char* str)
{
//检验不为空
if (str == NULL)
{
return 0;
}
size_t size = 0;
while (str[size] != '\0')
{
size++;
}
return size;
}
strcpy()
//strcpy()
// char* strcpy(char * destination, const char * source );
//把 src 所指向的字符串复制到 dest。 如果目标数组 dest 不够大,而源字符串的长度又太长,可能会造成缓冲溢出的情况。
//返回一个指向最终的目标字符串 dest 的指针 char*
char* mystrcpy(char * dest, const char*src)
{
//判断合法性
assert(dest != NULL);
assert(src != NULL);
//赋值
int i = 0;
while (src[i] != '\0')
{
dest[i] = src[i];
i++;
}
dest[i] = '\0 ';
return dest;
strcat()
//strcat()
// char * strcat ( char * destination, const char * source );
//把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。与strcpy一样,也需要dest的空间足够大,能够容纳两个字符串加在一起的长度
//注意:两个dest和src空间不能重叠 例如:
// dest: h e l l o \0 src: n i h a o \0
// 0x100 0x102
//返回一个指向最终的目标字符串 dest 的指针 char*
char* mystrcat(char* dest, const char* src)
{
//判断合法性
assert(dest != NULL);
assert(src != NULL);
//找到dest的结束位置'\0'
int destTail = 0;
while (dest[destTail] != '\0')
{
destTail++;
}
//拷贝直接使用mystrcpy(dest+destTail,src)即可,我们来写一下
int i = 0;
while (src[i] != '\0')
{
dest[destTail + i] = src[i];
i++;
}
//把dest最后的位置设为'\0'
dest[destTail + i] = '\0';
//返回
return dest;
}
strcmp()
//strcmp()
// int strcmp(const char *str1, const char *str2)
//比较规则:按照字典序进行比较,谁在字典前面谁就小
//返回: str1 < str2 ==> < 0 的整数
// str1 > str2 ==> > 0 的整数 返回 int
// str1 = str2 ==> = 0 的整数
int mystrcmp(const char* str1, const char* str2)
{
//先检验合法性
assert(str1 != NULL);
assert(str2 != NULL);
//进行比较
//没遇到'\0'的情况
const char* p1 = str1;
const char* p2 = str2;
while (*p1 != '\0'&&*p2 != '\0')
{
if (*p1 < *p2){
return -1;
}else if (*p1>*p2){
return 1;
}else{
//相等就比较下一个字符
p1++;
p2++;
}
}
//遇到'\0'的情况
//如果两个字符串数量不一样,比如“abcd”和“ab”,就会出现有一方先到'\0',此时它的值就是0
if (*p1 < *p2){
//p1先结束了(p1到'\0'时,*p1=0)
return -1;
}
else if (*p1>*p2){
//p2先结束了
return 1;
}
else{
//两个同时遇到'\0'了
return 0;
}
//上面这一串代码可以用一句代码概况: return *p1-*p2; 实际的strcmp()也是这么做的
}
strncpy()
//strncpy()
// char *strncpy(char *dest, const char *src, size_t n)
//拷贝num个字符从源字符串到目标空间,
// 当num比src长时,会在多余部分补上'\0'
// 当num比src短时,就只拷贝src的前num个字符,不会拷贝'\0'
// num的值,必须保证dest能够容纳下,且要考虑'\0'的情况
// 该函数返回最终复制的字符串dest char*
char* mystrncpy(char* dest, char* src, size_t num)
{
//检验合法性
assert(dest != NULL);
assert(dest != NULL);
assert(dest != 0);
//开始拷贝
size_t i = 0; //后面会进行i和num的比较,需要是同一类型
while (src[i] != '\0'&&i < num)
{
dest[i] = src[i];
i++;
}
//有两种情况会打断上面循环
// a. i == num ,直接return即可
if (i == num)
return dest;
// b. src遇到'\0',把后面的dest剩余部分设为'\0'
while (i < num)
{
dest[i] = '\0';
i++;
}
return dest;
}
strncat()
//strncat()
// char *strncat(char *dest, const char *src, size_t n)
//把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。
//返回dest char*
char* mystrncat(char* dest, const char* src, size_t num)
{
//检验合法性
assert(dest != NULL);
assert(src != NULL);
assert(num != 0);
//找到dest的末尾
size_t destTail = 0;
while (dest[destTail] != '\0')
{
destTail++;
}
//循环结束destTail指向dest的'\0'
//把src从destTail的位置开始拷贝给dest
size_t i = 0;
while (src[i] != '\0' && i < num)
{
dest[destTail] = src[i];
i++;
destTail++;
}
//两种可能导致循环结束:
// a. src遇到'\0',此时需要将'\0'拷贝上去
// b. i = num,说明src比num长,我们只需要拷贝前面的就可以了,后面的用'\0'填充就好了
// a b 两种情况都要添加'\0',那么我们直接加上就可以了
dest[destTail + 1] = '\0';
return dest;
}
strncmp()
//strncmp()
// int strncmp(const char *str1, const char *str2, size_t n)
//把 str1 和 str2 进行比较,最多比较前 n 个字节 比较和返回规则跟strcmp一样
int mystrncmp(const char* str1, const char*str2, size_t num)
{
//检验合法性
assert(str1 != NULL);
assert(str2 != NULL);
assert(num != 0);
//一个一个进行比较
size_t i = 0;
while (str1 != '\0'&&str2 != '\0'&&i < num)
{
if (str1[i] < str2[i])
return -1;
else if (str1[i]>str2[i])
return 1;
else
i++;
}
//有三种情况会导致上面的循环结束
//str1 遇到'\0',
//str2 遇到'\0', 这两种认为是一种,谁先遇到谁小
//i == num, 此时说明前面的str1[i],str2[i]都相等,且比较了前num个字符,完成比较,认为str1 = str2
if (i == num)
return 0;
return str1[i] - str2[i];
}
strstr()
//strstr()
// char *strstr(const char *haystack, const char *needle)
//在字符串 haystack 中查找第一次出现字符串 needle 的位置,
//找到返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。 char*
const char* mystrstr(const char* str1, const char* str2)
{
//检验合法性
assert(str1 != NULL);
assert(str2 != NULL);
//两个字符串也不能为空字符串str != "";
assert(str1 != '\0');
assert(str2 != '\0');
//算法
const char* black = str1;
//外层循环控制black从str1找子字符串的起始位置
while (*black!='\0')
{
const char* red = black;
const char* sub = str2;
//里层循环从black开始,判断当前字符串与str2是否相等
while (*red != '\0'&&*sub != '\0'&&*red == *sub)
{
red++;
sub++;
}
//上面这个循环有三种可能会终止
// *red = '\0' ,就是从black当前的位置找没找到,可以直接进入下次循环.或者说找不到了,直接返回NULL
// *sub = '\0' ,没找到,跳出进入下次循环。
// *red == *sub ,找到了,直接返回black。
if (*red != '\0'&&*sub != '\0')
{
black++;
continue;
}
return black;
}
return NULL;
}
内存函数
这是下面函数的接口代码,可以参考运行:
typedef struct Student
{
int id;
char name[1024];
}student;
int main()
{
//int a = 10;
//int b = 0;
//mymemcpy(&b,&a, sizeof(a));
//printf("b = %d\n", b);
//int arr[] = { 1, 2, 3, 4, 5 };
//int arr1[100] = { 0 };
//mymemcpy(arr1, arr, sizeof(arr));
//for (int i = 0; i < 5; i++)
//{
// printf("%d\n",arr1[i]);
//}
//student s1 = { 1, "lihan" };
//student s2;
//mymemcpy(&s2, &s1, sizeof(student));
//printf("%d,%s",s2.id,s2.name);
//char str1[] = "hello";
//char str2[1024];
//mymemcpy(str2, str1, sizeof(str1));
//printf("%s\n",str2);
}
memcpy()
//memcpy()
//void *memcpy(void *str1, const void *str2, size_t n)
// 从存储区 str2 复制 n 个字节到存储区 str1。 (内存块拷贝)
//返回值: 返回一个指向目标存储区 str1 的指针 void*
//注意: 这里的字符串是 void* 类型,意味着它再也不能解引用操作。因为void*只知道地址,不知道大小。
// 我们就只有对他进行强制类型转换,为了便于操作,我们转为char*。 其他类型并不方便
void* mymemcpy(void* str1, const void* str2, size_t num)
{
//检验合法性
assert(str1 != NULL);
assert(str2 != NULL);
assert(num != 0);
//void*不好操作,进行强制类型转换 按照字节为单位进行转换
char* pstr1 = (char*)str1;
const char* pstr2 = (const char*)str2;
size_t i = 0;
while (i < num)
{
pstr1[i] = pstr2[i];
i++;
}
return str1;
}
memmove()
//memmove()
//void *memmove(void *str1, const void *str2, size_t n)
//比memcpy增加了考虑内存重叠,如果str1和str2内存重叠,那么memmove比memcpy更安全,如果不重叠,两者相同
//如果重叠方式是这样的:
// int a[100]: 1 2 3 4
// src dest
// 从1开始 从3开始
//把src考入dest,如果按照正常手法,就会将3覆盖,怎么办呢?
//我们可以反着拷贝,把4拷贝到最后面,再拷贝3 ……
// ④ 1 ③ 2 ② 3 ① 1
//但如果重叠方式是这样的:
// int a[100]: 1 2 3 4
// dest src
// 从src前面开始 从2开始
//按照正常的手法一个字节一个字节拷贝,并没有什么影响
//所以我们考虑内存重叠只需要考虑dest起始位置在src和src+num就好了
void* mymemmove(void* dest, void* src, size_t num)
{
//检验合法性
assert(dest != NULL);
assert(src != NULL);
assert(num != 0);
//void*不好操作,进行强制类型转换 按照字节为单位进行转换
char* pdest = (char*)dest;
const char* psrc = (const char*)src;
size_t i = 0;
//判断重叠
if (pdest>psrc && pdest<psrc+num)
{
//如果重叠,反着拷贝
size_t i = num;
while (i>0)
{
pdest[i] = psrc[i];
i--;
}
return dest;
}
else
{
//如果不重叠,正着拷贝
size_t i = 0;
while (i < num)
{
pdest[i] = psrc[i];
i++;
}
return dest;
}
}
memcmp()
// memcmp()
// int memcmp(const void *str1, const void *str2, size_t n))
//把存储区 str1 和存储区 str2 的前 n 个字节进行比较 比较内存块
// 返回值和strcmp类似,返回0、大于0/小于0的数字 int
int mymemcmp(const void* str1, const void* str2, size_t num)
{
//检验合法性
assert(str1 != NULL);
assert(str2 != NULL);
assert(num != 0);
//void*无法比较,强制类型转换
const char* pstr1 = (char*)str1;
const char* pstr2 = (char*)str2;
//一个字节一个字节进行比较
for (size_t i = 0; i < num; i++)
{
if (pstr1[i] < pstr2[i])
{
return -1;
}
else if (pstr1[i]>pstr2[i])
{
return 1;
}
else
continue;
}
return 0;
}
memset()
//memset()
//void *memset(void *str, int c, size_t n)
//复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。 填充
// 返回一个指向存储区 str 的指针。 void*
void* mymemset(void* str, int value, size_t num)
{
//检验合法性
assert(str != NULL);
assert(num != 0);
//类型转换
char* sptr = (char*)str;
//一个字节一个字节填充
for (size_t i = 0; i < num; i++)
{
sptr[i] = (char)value;
}
//循环结束,返回值
return str;
}