字符串与内存库函数的模拟实现(新手)(C语言)
**运行通过编译器(VS2022) **
注意1:只有部分对除整形以外的类型乃至结构体做过测试
注意2:如果还有其他的字符串与内存操作函数,欢迎补充
注意3:因为本人不清楚专业命名,所以所有函数都以自己的理解代称
所用到的库函数以及宏:
#define _CRT_SECURE_NO_WARNING
#define cahr char
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
此处因为本人因输入习惯常将char打成cahr,所以专门定义了一个宏
*一、字节替换函数memset() *
void _cdecl my_memset(void* arr, cahr ch, size_t num)
{
assert(arr);
for (int i = 0; i < num; i++)
*((char*)arr + i) = ch;
}
1、assert(arr)相当于assert(arr != NULL) (下面有类似用法)
2、选择使用for循环而非while循环的原因为vs2022如果使用++(char*)arr会报出“表达式必须是可修改的左值”错误,因此没有直接对地址进行操作(下面有类似问题)
其他版本的编译器可能没有类似问题。
*二、字节比较函数memcmp() *
int _cdecl my_memcmp(const void* dest, const void* src, size_t num)
{
assert(dest && src);
for (int i = 0; i < num; i++)
if (*((char*)dest + i) > *((char*)src + i))
return 1;
else if (*((char*)dest + i) < *((char*)src + i))
return -1;
else;
return 0;
}
循环提前结束代表不等,否则相等
*三、内存重叠拷贝函数memmove() *
void* _cdecl my_memmove(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
if (dest < src)
//从前向后拷贝
for (int i = 0; i < num; i++)
*((char*)dest + i) = *((char*)src + i);
else
//从后向前拷贝
for (int i = num - 1; i >= 0; i--)
*((char*)dest + i) = *((char*)src + i);
return ret;
}
其实就是更具不同情况使用memcpy()函数,本质大差不离,多了一层if
*四、内存拷贝函数memcpy() *
void* _cdecl my_memcpy(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
for (int i = 0; i < num; i++)
*((char*)dest + i) = *((char*)src + i);
return ret;
}
*五、是否被包含判断函数strstr() *
char* _cdecl my_strstr(const char* dest, const char* src)
//只是比较而不改变内容,所以用const增加常属性
{
assert(dest && src);
char* ret = (char*)dest;
//如果src只有一个\0,则直接跳出函数减少运算
if (!*src)
return ret;
else
while (*dest++ != *src && *(dest - 1))
;
/*此处因为当*dest == *src && *dest == '\0'时,传出的dest多进行一次++,
所以判断对应*dest时要-1,但为了后续判断又不能改变dest的值*/
if (!(*--dest))//同上因为dest跳出循环时多进行一次++
return ret;
ret = (cahr*)dest;//已定义宏
while (*dest++ == *src++ && *dest)
;//*dest == '\0'时跳出循环,避免死循环
if (!(*src)) return ret;
//防止出现dest与src是交集的情况
else return NULL;
}
if(*src)表示判断其是否为'\0',如果是\0则返回0
*六、字符串比较函数strcmp() *
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1++ == *str2++)
if (!(*(str1 - 1) || *(str2 - 1)))
//等价于!(*(str1 - 1)) && !(*(str2 - 1))
return 0;
if (*--str1 > *--str2)
//此处因为str1与str2跳出循环时多进行一次++,所以要--
return 1;
else
return -1;
}
此处用到的逻辑运算: (!A && !B) == !!(!A && !B) == !(!!A || !!B) == !(A || B)
*七、字符串复制函数strcpy() *
char* my_strcpy(char* dest, const char* src)
{
assert(dest);
assert(src);
char* ret = dest;
while (*dest++ = *src++);
return ret;
}
*八、字符串追加函数strcat() *
char* my_strcat(char* dest, const char* src)
{
assert(dest != NULL);
assert(src);
//二者语法等价
char* ret = dest;
while (*dest++);
dest--;
while (*dest++ = *src++);
return ret;
}
*九、字符串分段函数strtok() *
尚在钻研
**其他问题: **
1、#define _CRT_SECURE_NO_WARNINGS
这是因为在vs2022中一些原本C语言的函数例如scanf()会被任务存在风险,在最开始加人该语句使得程序得以运行,当然,如果不加这句话也可以运行,可以使用自带的函数,例如scanf_s(),这种写法可以保证在该编译器中得以无风险运行,但是相同代码无法保证在其他编译器中运行。
另外,不止vs2022,其他的编译器可能也存在相关问题。
2、assert()
因为无法完全保证代码的正确性,所以加入断言函数防止传入空指针
3、_cdecl
因本人最近在研究该调用,所以部分函数用到该关键词,实际上该关键词即使不输入也默认是该类型,就像是auto一样,省略时默认该类型
4、size_t 库函数中将unsighed int类型重命名为size_t
5、对void类型的强制类型转换 因为void类型算是无类型指针,所以要先转换为字节大小为1的char*类型以方便操作
缺陷
1、部分代码尤其是strstr()函数过于繁杂,远远没有达到库函数应有的标准
2、注释多处可能存在不合理,例如strstr()函数就十分影响阅读,而且,很多函数可能也缺乏注释
3、调试不充分,只有小部分函数对包括结构体变量进行过测试
#C语言##库函数##初学者#