C语言之库函数的模拟与使用
C语言之库函数的模拟与使用
在我们学习C语言的过程中,难免会遇到这样的一种情况:
我们通常实现一个功能的时候,费尽心血的写出来,却有着满满的错,这时却有人来告诉你说:这个功能可以用相应的库函数来实现。
这时你的心里充满着***。但这并不算坏事,至少加深了你对它的认识与记忆。
所以,今天来漫谈一下 某些库函数的模拟与实现。
而这篇我们主要来介绍一些处理字符和字符串的库函数的使用和注意事项
内容大致如下:
1.求字符串长度 strlen
2.长度不受限制的字符串函数 strcpy strcat strcmp
3.长度受限制的字符串函数介绍 strncpy strncat strncmp
4.字符串查找 strstr strtok
5.错误信息报告 strerror perror
6.字符操作
7.内存操作函数 memcpy memmove memset memcmp
1.求字符串长度--strlen
正如所介绍那样,此函数的功能就是求一个字符串的长度。而我们所需要做的就是传一个字符指针进去。
示例:
#include <stdio.h>
#include <string.h> int main ()
{
char szInput[256];
printf ("Enter a sentence: ");
gets (szInput);
printf ("The sentence entered is %u characters long.\n",(unsigned)strlen(szInput));
return 0;
}
结果:
在此函数的使用过程中我们需要注意以下几点:
1.字符串已经 '\0' 作为结束标志
2.strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )。
3.参数指向的字符串必须要以 '\0' 结束。
4.注意函数的返回值为size_t,是无符号的( 易错 )
我们来看一个对于第四点的例子:
#include <stdio.h>
#include<string.h>
int main()
{
const char* str1 = "abcdef";
const char* str2 = "bbb";
if (strlen(str2) - strlen(str1) > 0)
{
printf("str2>str1\n");
}
else
{
printf("srt1>str2\n");
}
return 0;
}
这个结果会是什么呢?
相信会有人给出srt1>str2的结果,可编译器给出的结果却是:
为什么呢?
我们原以为 3-6=-3 这个式子没错啊,-3<0 执行 else 的结果
但 因为strlen的返回类型是一个unsigned形,所以相减之后的那个数会变为一个很大很大的数。
我们来模拟实现一下此函数
1.计数器方式
int my_strlen(const char* str)
{
int count = 0;
while (*str)
{
count++;
str++;
}
return count;
}
2.递归实现
int my_strlen(const char* str)
{
if (*str == '\0')
return 0;
else
return 1 + my_strlen(str + 1);
}
3.指针-指针 实现
int my_strlen(char* s)
{
char* p = s;
while (*p != '\0')
p++;
return p - s;
}
2.长度不受限制的字符串函数
1.strcpy
这个函数可谓是见名知意,字符串的拷贝函数,我们来看看介绍:
实例:
#include <stdio.h>
#include <string.h> int main()
{
char str1[] = "Sample string";
char str2[40];
char str3[40];
strcpy(str2, str1);
strcpy(str3, "copy successful");
printf("str1: %s\nstr2: %s\nstr3: %s\n", str1, str2, str3);
return 0;
}
上述代码干了一个什么事呢?
将str1中的内容拷贝到str2中
又将 copy successful 拷贝到str3中
结果:
同样注意以下几点:
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' 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串。目标空间必须可变。
模拟实现strcpy
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest;
assert(dest != NULL); //断言保证dest不为空指针
assert(src != NULL); //断言保证src不为空指针 while ((*dest++ = *src++))
{
;
}
return ret;
}
2.strcat-连接两个字符串
介绍如下:
实例:
#include <stdio.h>
#include <string.h> int main()
{
char str[80];
strcpy(str, "these ");
strcat(str, "strings ");
strcat(str, "are ");
strcat(str, "concatenated.");
puts(str);
return 0;
}
作用:
先将 these 复制到 str 中,在逐次将 strings 、are、concatenated 连接到str上
结果:
注意:
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. 1.源字符串必须以 '\0' 结束。 2.目标空间必须有足够的大,能容纳下源字符串的内容。目标空间必须可修改。
还值得注意的一点就是:若自己追加自己将会“永无休止”的循环下去,为什么呢?
模拟实现
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{
char* ret = dest;
assert(dest != NULL);
assert(src != NULL);
while (*dest) //找到\0的位置
{
dest++;
}
while ((*dest++ = *src++))//在此\0位置上覆盖新的内容
{
;
}
return ret;
}
3.strcmp-字符串比较函数
切记:不能用 == 来判断两个字符串是否相等
介绍:
实例:
#include <stdio.h>
#include <string.h> int main()
{
char key[] = "apple";
char buffer[80];
do {
printf("Guess my favorite fruit? ");
fflush(stdout);
scanf("%79s", buffer);
} while (strcmp(key, buffer) != 0);
puts("Correct answer!");
return 0;
}
作用:
定义一个key来表示自己喜欢的水果,
让用户不断来输入,
直到用户输入所指定内容
最后输出 correct answer
结果:
注意:
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的数字
比较是比的相应字符的ASCII码值,如果相等,则比较下一个以此类推.....
模拟实现:
int my_strcmp(const char* src, const char* dest)
{
int ret = 0;
assert(src != NULL);
assert(dest != NULL);
while (!(ret = *(unsigned char*)src - *(unsigned char*)dest) && *dest)//dest不为空,且如果相减结果为0,就继续循环
{
++src, ++dest; if (ret < 0)
ret = -1;
else if (ret > 0)
ret = 1; } return(ret);
}
#include<assert.h>
int my_strcmp(const char*s1, const char*s2)
{
assert(s1 && s2);
while (*s1 == *s2)
{
if (*s1 == '\0')
return 0;
s1++;
s2++;
}
return *s1 - *s2;
}
3.长度受限制的字符串函数介绍
1.strncpy-从原始串复制自定义个字符到新串中
介绍:
实例:
#include <stdio.h>
#include <string.h> int main ()
{
char str1[]= "To be or not to be";
char str2[40];
char str3[40]; /* copy to sized buffer (overflow safe): */
strncpy ( str2, str1, sizeof(str2) ); /* partial copy (only 5 chars): */
strncpy ( str3, str2, 5 );
str3[5] = '\0'; /* null character manually added */ puts (str1);
puts (str2);
puts (str3); return 0;
}
作用:
将str1中所有的字符复制到str2中
将str1中的前五个字符复制到str3中
结果:
注意:
Copies the first num characters of source to destination. If the end of the source C string (which is
signaled by a null-character) is found before num characters have been copied, destination is padded
with zeros until a total of num characters have been written to it.
拷贝num个字符从源字符串到目标空间。 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
模拟实现:
char* my_strncpy(char* destination, const char* source, size_t num)
{
assert(destination && source);
char* ret = destination;
destination[num] = '\0';
while (num--)
{
if (*source == '\0')
{
*destination = '\0';
break;
}
*destination++ = *source++;
}
return ret;
}
2.strncat--从原始串取自定义个字符连接到新串中
介绍:
示例:
#include <stdio.h>
#include <string.h> int main()
{
char str1[20];
char str2[20];
strcpy(str1, "To be ");
strcpy(str2, "or not to be");
strncat(str1, str2, 6);
puts(str1);
return 0;
}
作用:
将str2中的前6个字符连接到str1后面
结果:
注意:
Appends the first num characters of source to destination, plus a terminating null-character.
If the length of the C string in source is less than num, only the content up to the terminating nullcharacter is copied.
模拟实现:
char* my_strncat(char* destination, const char* source, size_t num)
{
assert(destination && source);
char* ret = destination;
while (*destination);//找到\0
destination[num] = '\0';//放置\0
while (num--)
{
*destination++ = *source++;
if (*source == '\0')
{
*destination = '\0';
break;
}
}
return ret;
}
3.strncmp-自定义个字符比较
比较方式与strcmp相同
介绍:
示例:
#include <stdio.h>
#include <string.h> int main()
{
char str[][5] = { "R2D2" , "C3PO" , "R2A6" };
int n;
puts("Looking for R2 astromech droids...");
for (n = 0; n < 3; n++)
if (strncmp(str[n], "R2xx", 2) == 0)
{
printf("found %s\n", str[n]);
}
return 0;
}
作用:
比较各字符串的前两个字符是否为R2,如果相同,则输出
结果:
注意:
比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。
模拟实现:
#include <stdio.h>
#include <string.h> int my_strncmp(const char* str1, const char* str2, size_t num)
{
assert(str1 && str2);
while (num--)
{
if ((*str1 != *str2))
{
return *str1 - *str2;
}
str1++;
str2++;
}
return 0;
}
4.字符串查找函数
1.strstr-在一个字符串中查找另一个字符串是否存在并返回相应的地址
介绍:
示例:
#include <stdio.h>
#include <string.h> int main()
{
char str[] = "This is a simple string";
char* pch;
pch = strstr(str, "simple");
if (pch != NULL)
strncpy(pch, "sample", 6);
puts(pch);
return 0;
}
作用:
在str中查找simple是否存在
结果:
注意:
Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1.
模拟实现:
#include <stdio.h>
#include <string.h> const char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
const char* start = str1;
while (*start)
{
const char* e1 = start;
const char* e2 = str2;
while ((*e1 == *e2) && (*e1) && (*e2))
{
e1++;
e2++;
}
if (*e2 == '\0')
{
return (char*)start;
}
start++;
}
return NULL;
}
2.strtok-字符串分割函数
介绍:
实例:
#include <stdio.h>
#include <string.h> int main()
{
char str[] = "EG:- This, a sample string.";
char* pch;
printf("Splitting string \"%s\" into tokens:\n", str);
pch = strtok(str, " ,.-");
while (pch != NULL)
{
printf("%s\n", pch);
pch = strtok(NULL, " ,.-");
}
return 0;
}
作用:
将所给字符串用所给分隔符分割开;
结果:
注意:
char * strtok ( char * str, const char * sep );
sep参数是个字符串,定义了用作分隔符的字符集合
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改
变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
如果字符串中不存在更多的标记,则返回 NULL 指针。
5.错误信息报告
1.strerror-返回错误码,所对应的错误信息。
介绍:
示例:
#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));
return 0;
}
结果:
注意:
使用时必须包含头文件:errno.h
2.perror-打印对应的错误信息
示例:
#include <stdio.h> int main()
{
FILE* pFile;
pFile = fopen("unexist.ent", "rb");
if (pFile == NULL)
perror("The following error occurred");
else
fclose(pFile);
return 0;
}
结果:
6.字符分类函数:
注意:
比较时,是一个一个字符的去比较
示例及结果:
#include <stdio.h>
#include <ctype.h>
int main ()
{
int i;
char str[]="c3po...";
i=0;
while (isalnum(str[i])) i++;
printf ("The first %d characters are alphanumeric.\n",i);
return 0;
}
#include <stdio.h>
#include <ctype.h>
int main()
{
int i = 0;
char str[] = "C++";
while (str[i])
{
if (isalpha(str[i]))
printf("character %c is alphabetic\n", str[i]);
else
printf("character %c is not alphabetic\n", str[i]);
i++;
}
return 0;
}
7.内存操作函数
内存操作函数不仅可以处理字符串,还可以处理结构体,整形数组等。
1.memcpy--以字节为单位复制
介绍:
示例:
#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;
}
结果:
注意:
void * memcpy ( void * destination, const void * source, size_t num );
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 '\0' 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的
模拟实现:
void* my_memcpy(void* destination, const void* source, size_t num)
{
assert(destination && source);
void* ret = destination;
while (num--)
{
*((char*)destination)++ = *((char*)source)++;
}
return ret;
}
2.memmove
介绍:
示例:
#include <stdio.h>
#include <string.h> int main ()
{
char str[] = "memmove can be very useful......";
memmove (str+20,str+15,11);
puts (str);
return 0;
}
结果:
注意:
memmove和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。 如果源空间和目标空间出现重叠,就得使用memmove函数处理。
模拟实现:
void* my_memmove(void* dest, const void*src, size_t count)
{
void* ret = dest;
assert(dest && src);
if (dest < src)
{
//前->后
while (count--)
{
*(char*)dest = *(char*)src;
++(char*)dest;
++(char*)src;
}
}
else
{
//后->前
while (count--)
{
*((char*)dest + count) = *((char*)src + count);
}
}
return ret;
}
3.memset
介绍:
示例:
#include <stdio.h>
#include <string.h> int main ()
{
char str[] = "almost every programmer should know memset!";
memset (str,'-',6);
puts (str);
return 0;
}
结果:
模拟实现:
void * Memset(void* src, int ch, size_t count)
{
assert(src != NULL);
void* start = src;
while (count--)
{
*(char*)src = (char)ch;
src = (char*)src + 1;
}
return start;
}
4.memcmp
介绍:
示例:
#include <stdio.h>
#include <string.h> int main ()
{
char buffer1[] = "DWgaOtP12df0";
char buffer2[] = "DWGAOTP12DF0"; int n; n=memcmp ( buffer1, buffer2, sizeof(buffer1) ); if (n>0) printf ("'%s' is greater than '%s'.\n",buffer1,buffer2);
else if (n<0) printf ("'%s' is less than '%s'.\n",buffer1,buffer2);
else printf ("'%s' is the same as '%s'.\n",buffer1,buffer2); return 0;
}
结果:
注意:
比较从ptr1和ptr2指针开始的num个字节
返回值同strcmp
模拟实现:
int my_memcmp(const void* p1, const void* p2, size_t count)//方法1
{
assert(p1);
assert(p2);
char* dest = (char*)p1;
char* src = (char*)p2;
while (count-- && (*dest++ == *src++))
{
;
}
if (count == 0)
return 0;
return *dest - *src;
}
若有有错之处,还望大家多多指点!!!
本次讲解就到这了,
如果大家对于函数的模拟实现都能掌握,那么大家肯定对于上述函数有了深刻的理解。
C语言之库函数的模拟与使用的更多相关文章
- C语言中库函数strstr的实现
在C语言中库函数strstr()函数表示在一个字符串str1中查找另一个字符串str2,如果查到则返回str2在str1中首次出现的位置,如果找不到则返回null. char* strstr(char ...
- 通过调用C语言的库函数与在C代码中使用内联汇编两种方式来使用同一个系统调用来分析系统调用的工作机制
通过调用C语言的库函数与在C代码中使用内联汇编两种方式来使用同一个系统调用来分析系统调用的工作机制 前言说明 本篇为网易云课堂Linux内核分析课程的第四周作业,我将通过调用C语言的库函数与在C代码中 ...
- C语言之通讯录的模拟实现
C语言之通讯录的模拟实现 在C语言学习结束之际,谨以此篇文章来对C语言的学习告一段落. 纲要: 通讯录的静态版本 通讯录的动态版本 通讯录的带文件版本 因为三种实现方法除了储存形式不同,其他都基本相同 ...
- 问题解决:补充安装c语言的库函数和系统调用man手册
问题解决:补充安装c语言的库函数和系统调用man手册 今日份麻麻~上课时大家的Ubuntu都可以通过man查到关于stat的库函数,但是我的Kali查出来是这样: 询问老师之后得知需要去安装相 ...
- c语言部分库函数,代码实现,以及细节理解
代码来自: http://blog.csdn.net/v_JULY_v //得9 分 //为了实现链式操作,将目的地址返回,加2 分! char * strcpy( char *strDest, co ...
- C++调用C语言的库函数
在项目中,使用C语言编写了一个socket后台程序tkcofferd,并且为方便客户端的使用,提供了动态库,其中包含socket接口. 现在的需求是使用qt做一个前端界面,用来展示tkcofferd的 ...
- ubuntu 安装 c语言的库函数man手册
安装 1.C语言库函数基本的帮助文档 sudo apt-get install manpages sudo apt-get install manpages-de sudo apt-get insta ...
- 大二作业——操作系统实验——C语言用双向链表,模拟实现动态分区式存储管理
实验:动态分区式存储管理 实验内容: 编写程序模拟完成动态分区存储管理方式的内存分配和回收.实验具体包括:首先确定内存空闲分配表:然后采用最佳适应算法完成内存空间的分配和回收:最后编写主函数对所做工作 ...
- C语言常用库函数
一.数学函数 调用数学函数时,要求在源文件中包下以下命令行: #include <math.h> 函数原型说明 功能 返回值 说明 int abs( int x) 求整数x的绝对值 计算结 ...
随机推荐
- 纯原生javascript下拉框表单美化实例教程
html的表单有很强大的功能,在web早期的时候,表单是页面向服务器发起通信的主要渠道.但有些表单元素的样式没办法通过添加css样式来达到满意的效果,而且不同的浏览器之间设置的样式还存在兼容问题,比如 ...
- 【Oracle】10g rac如何开启归档和关闭归档
开启归档: 1.设置想设置的归档的位置,我们这里归档的位置为ASM磁盘组,磁盘组的名称为DATA alter system set log_archive_dest_1='location=+DATA ...
- 在recover database时,如何决定该从哪一个SCN开始恢复
使用备份恢复的方法搭建DG库,还原数据文件后,打开数据库时报错 SQL> ALTER DATABASE OPEN READ ONLY; ALTER DATABASE OPEN READ ONLY ...
- ElasticSearch Python 基本操作
创建索引 from elasticsearch import Elasticsearch es = Elasticsearch('192.168.149.96:9200') mappings = { ...
- java虚拟机入门(四)-垃圾回收的故事
谈到垃圾回收器,java程序员骄傲了起来,c语言你是够快,但是你有管家帮你打扫吗,还不是得靠自己的一双手,有钱就是任性.既然如此令java程序员骄傲的垃圾回收器,怎能让人不想去一探究竟呢! 垃圾回收器 ...
- 用CSS制做一个三角形!
用CSS制做一个三角形! <style> .outer { width: 0; height: 0; border-left: 10px solid transparent; border ...
- NULL-safe equal null 索引 空字符串
小结 1. mysql> INSERT INTO my_table (phone) VALUES (NULL); 有手机号但是不知道 mysql> INSERT INTO my_table ...
- 轻型目录访问协议 ldap 公司内部网站的登录 单点登录
https://zh.wikipedia.org/wiki/轻型目录访问协议 轻型目录访问协议(英文:Lightweight Directory Access Protocol,缩写:LDAP,/ˈɛ ...
- sql多行合并
例一 SELECT qqo.questionID '题目id', qqo.quesOption '选项' FROM qz_question_option qqo, qz_question qq WHE ...
- docker镜像加速,docker更换为国内镜像
docker镜像加速,docker更换为国内镜像 一.使用官方镜像 二.Docker守护进程配置加速器 相关博文原文地址: CSDN:让我思考一下 :docker更换为国内镜像 一.使用官方镜像 Do ...