文档章节

c语言中几个常见的库函数strlen、strcmp、strcat、strcpy、strncpy、memset、memcpy、memmove、mmap

o
 osc_fmg49rzg
发布于 2019/03/22 08:16
字数 2530
阅读 5
收藏 0

「深度学习福利」大神带你进阶工程师,立即查看>>>

1.strlen()

1)计算给定字符串的长度,不包括’\0’在内

unsigned int strlen(const char *s)
{
    assert(NULL != s);//如果条件不满足,则终止程序
    unsigned int length = 0;
    while (*s++ != '\0')
        ++length;
    return length;
}

2.strcmp()

1)比较两个字符串,若str1、str2字符串相等,则返回零;若str1大于str2,则返回正数;否则,则返回负数

int strcmp(const char *str1, const char *str2)
{
    assert(NULL != str1 && NULL != str2);
    while(*str1 && *str2 && *str1 == *str2)
    {
        ++str1;
        ++str2;
    }
    return *str1 - *str2;
    //若相等,则*str1 - *str2 = '\0' - '\0' = 0;否则,*str1 - *str2 != 0;因为前面的位都相等,所以只需要比较当前位来确定返回值
}

3.strcat()

1)将src所指向的字符串添加到dest结尾处(会覆盖dest结尾处的'\0'

2)src和dest所指内存区域不可以重叠且dest必须有足够的空间(sizeof(dest)-strlen(dest)必须>=strlen(src)+1)来容纳src的字符串

char* strcat(char* dest, const char* src)
{
    assert(NULL != dest && NULL != src);//如果条件不满足,则终止程序
    char* temp = dest;
    while ('\0' != *temp)//while循环出来temp指向原字符串的NULL空字符的位置
        ++temp;
    while (*src != '\0')
        *temp++ = *src++;
    return dest;
}

4.strcpy()

1)把字符串src(包括'\0')复制到dest

2)src和dest所指内存区域不可以重叠且dest必须有足够的空间(sizeof(dest)>=strlen(src)+1)来容纳src的字符串

3)返回值char*而不是void,实现了链式表达(就是让调用strcpy的时候可以方便一些,在调用的时候可以一连串(链式)写下来)

char* strcpy(char* dest, const char *src)
{
    assert(NULL != dest && NULL != src);
    char * temp = dest;
    while (*src != '\0')
        *temp++ = *src++;
    return dest;
}

5.strncpy()

1)将以字符串src所指向的地址开始的前n个字节复制到dest中,并返回dest

2)如果src的前n个字符不含NULL结束符,则dest不会以NULL字符结束

3)如果n>strlen(src)+1,则以'\0'填充dest,直到复制完n个字节

4)src和dest所指内存区域不可以重叠且dest必须有足够的空间(sizeof(dest)>=strlen(src)+1)来容纳src的字符串

5)dest和n应该满足:sizeof(dest)>=n

char* strncpy(char* dest, const char* src, unsigned int n)
{
    assert(NULL != dest && NULL != src);
    char* temp = dest;
    while (n > 0 && *src != '\0')
    {
        *temp++ = *src++;
        --n;
    }
    if (n > 0)
    {
        *temp = '\0';
    }
    return dest;
}

6.memset()

1)将以s所指向的地址开始的前n个字节用ch替换,并返回s

2)常用于对较大的数组进行清零操作

void* memset(void* s, int ch, unsigned int n)
{
    assert(NULL != s);
    char* temp = s; //VS2017中这里会报错,gcc不会
    while (n > 0)
    {
        *temp++ = (char)ch; //将int转化为char,截去高24位,只保留低8位
        --n;
    }
    return s;
}

3)注意:不能把memset当作万能的初始化工具,应该只把memset当作清0的工具,因为memset是以字节(8bits)为单位用第二个参数ch进行赋值的

int main()
{
    int dest[2];//这里是int
    memset(dest, 1, sizeof(dest));
    for (int i = 0; i < sizeof(dest) / sizeof(int); ++i)
        printf("%d\n", dest[i]);

    return 0;
}
程序输出:
16843009
16843009

int main()
{
    int dest[2];//这里是int
    memset(dest, 0, sizeof(dest));
    for (int i = 0; i < sizeof(dest) / sizeof(int); ++i)
        printf("%d\n", dest[i]);

    return 0;
}
程序输出:
0
0

程序解读:

  1)1的二进制 : 00000000000000000000000000000001(32位),在memset源码中1将强制类型转换成char(1字节占8位)00000001(8位)

  2)memset函数传入的是sizeof(dest),也就是8(字节),所以n=8,进行8次while循环

  3)temp的类型是char*,所以会8位8位地去赋值

  4)所以while循环结束,从起始地址开始的值为:0000000100000001000000010000000100000001000000010000000100000001(64位)

  5)memset()调用结束后,dest的每个元素按照int类型读取(按32位读取),所以dest[0]=00000001000000010000000100000001(32位)=16843009,dest[1]=00000001000000010000000100000001(32位)=16843009

7.memcpy()

1)将以src所指向的地址开始的前n个字节的任意内容(不仅限于字符串)到拷贝到dest

2)src和dest所指内存区域不可以重叠,否则拷贝出来的数据将是错误的

void* memcpy(void* dest, const void* src, unsigned int n)
{
    assert(NULL != dest && NULL != src);
    void* temp = dest;
    for (int i = 0; i < n; ++i)
        *((char*)temp + i) = *((char*)src + i); //1个字节1个字节地拷贝,未知类型,不能++
    return dest;
}

8.memmove()

1)memmove也是将以src所指向的地址开始的前n个字节的任意内容(不仅限于字符串)到拷贝到dest,但是可以它可以处理src和dest所指内存区域存在重叠的情况(src<dest<src+n)

2)memmove这个函数名称中有"move"这个单词,但实际上并不是"移动"了。这个函数名称有它的历史原因:因为有了memcpy函数后,发现这个函数在使用时容易出现问题,于是又发明了一个能够处理重叠的memcpy函数,但为了保证兼容性依然保留了memcpy函数

void* memmove(void* dest, const void* src, unsigned int n)
{
    assert(NULL != dest && NULL != src);
    void* temp = dest;
    if (dest > src && dest < src + n)//内存重叠
    {
        for (int i = n - 1; i >= 0; --i)//从高地址向低地址拷贝
            *((char*)temp + i) = *((char*)src + i);
    }
    else//内存不重叠
    {
        for (int i = 0; i < n; ++i)
            *((char*)temp + i) = *((char*)src + i);
    }
    return dest;
}

9.mmap()

  转载自:https://blog.csdn.net/qq_36675830/article/details/79283113

9.1mmap()

1)mmap是一种内存映射的方法,即将一个磁盘文件映射到进程的虚拟地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对应

2)内存映射的好处:实现这样的映射关系后,进程一旦读写操作这一段内存,系统会自动回写到对应的磁盘文件上,这样一来,对文件的操作就不必再调用read、write等系统调用函数,效率会很高

3)mmap()的函数原型

void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
/*
其中:
addr:映射区起始地址,通常设为NULL,由系统指定。
length:将文件的多大长度映射到内存
prot:映射区的保护方式,可以是:
    PROT_EXEC:映射区可被执行
    PROT_READ:映射区可被读取
    PROT_WRITE:映射区可被写入
    PROT_NONE:映射区不能存取
flag:映射区的特性,可以是:
    MAP_SHARD:对映射区的写入数据会复制回文件,且允许其他映射该文件的进程共享
    MAP_PRIVATE:对映射区域的写入数据会产生一个映射的复制(copy - on - write),对此区域所做的修改不会写回到原文件
    其他标识这里就不再写出来了,可以通过man mmap查看
fd:由open返回的文件描述符,表示要映射的文件
offset:以文件开始处的偏移量,必须是分页大小的整数倍,通常为0, 表示从文件头开始映射
*/

4)vm_area_struct结构:linux内核使用vm_area_struct结构来表示一个独立的虚拟内存区域,由于每个虚拟内存区域功能和内部机制都不同,因此一个进程使用多个vm_area_struct结构来分别表示不同类型的虚拟内存区域。各个vm_area_struct结构使用链表或者树形结构链接,方便进程快速访问

5)在用户层创建虚拟地址空间中的映射区域

  • mmap()在内存映射段寻找一段空闲的满足要求的连续的区域,为此区域分配一个vm_area_struct结构,接着对这个结构进行初始化
  • 将新建的vm_area_struct结构插入进程的虚拟地址区域链表或树中 

6)从用户层进入内核层,调用内核函数mmap()(不同于用户空间库函数)完成文件物理地址和进程虚拟地址的一一映射关系

  • 内核函数mmap(),其原型为:
int mmap(struct file* filp, struct vm_area_struct* vma);

7)注意:mmap只是创建了建立文件磁盘地址和虚拟内存区域的映射关系,没有任何文件拷贝操作

9.2常规文件的读写和内存映射段读写的区别

1)进程对常规文件的读写过程:

  • 进程调用read()函数发起读文件的请求
  • 内核通过查找进程文件符表,从而找到此文件的inode
  • 通过inode检查次文件页是否已经缓存在物理页中,如果已缓存,则直接返回物理页上的内容
  • 如果未缓存,即发生缺页,于是进行页面置换,将数据从磁盘复制到物理页上缓存
  • 由于页缓存处在内核空间不能被用户进程直接寻址,所以还需要将页缓存中数据再次拷贝到内存对应的用户空间中。这样,通过了两次数据拷贝过程,才能完成进程对文件内容的获取

2)进程对建立了内存映射的文件的读写过程:

  • 进程的读或写操作访问虚拟地址空间的映射地址,通过查询页表,发现这虚拟页并未被缓存,引发缺页异常。因为目前只建立了地址映射,真正的硬盘数据还没有拷贝到物理内存中,即没有被缓存,因此引发缺页异常
  • 然后内核利用已经建立好的映射关系,将磁盘文件直接拷贝至位于用户空间的内存映射段上,于是进程就可以直接读取文件内容了
  • 如果进程的写操作改变了其内容,一定延迟后系统会自动回写对应磁盘地址,也即完成了写入到文件的过程,可以调用msync()来强制同步, 这样所写的内容就能立即保存到文件里了

3)区别:常规文件操作需要从磁盘到内核空间的页缓存再到用户空间的两次数据拷贝;而读写进行了内存映射的文件,只需要从磁盘到用户空间的一次数据拷贝,因此内存映射段效率很高

上一篇: Spring之Spel表达式
下一篇: 字典树
o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。
访问安全控制解决方案

本文是《轻量级 Java Web 框架架构设计》的系列博文。 今天想和大家简单的分享一下,在 Smart 中是如何做到访问安全控制的。也就是说,当没有登录或 Session 过期时所做的操作,会自动退回到...

黄勇
2013/11/03
3.6K
8
beego API开发以及自动化文档

beego API开发以及自动化文档 beego1.3版本已经在上个星期发布了,但是还是有很多人不了解如何来进行开发,也是在一步一步的测试中开发,期间QQ群里面很多人都问我如何开发,我的业余时间实在...

astaxie
2014/06/25
2.7W
22
XLSX读写库--EPPlus

EPPlus 是使用Open Office XML格式(xlsx)读写Excel 2007 / 2010文件的.net开发库。 EPPlus 支持: 单元格范围 单元格样式(Border, Color, Fill, Font, Number, Alignments) Charts 图片 形状...

匿名
2013/02/01
1W
2
C++模板库--C++ B-tree

这是一个google开源的C++模板库,实现了基于B-tree数据结构的有序内存容器。类似于STL的map、set、multimap和multiset模板,C++ B-tree也提供了btreemap、btreeset、btreemultimap和btreemu...

匿名
2013/02/05
3.4K
1
Javascript图元绘制库--ternlight

基于HTML CANVAS API的Javascript库,提供在HTML页面上绘制图元——如流程图的能力。 目前已支持简单的矩形图元和图元间的连线(直线、直角连线两种),拖拽图元等能力。 该javascript librar...

fancimage1
2013/02/07
6.3K
1

没有更多内容

加载失败,请刷新页面

加载更多

功能之前,感叹号有什么作用? - What does the exclamation mark do before the function?

问题: !function () {}(); 解决方案: 参考一: https://stackoom.com/question/Fl0I/功能之前-感叹号有什么作用 参考二: https://oldbug.net/q/Fl0I/What-does-the-exclamation-mark-do-......

富含淀粉
19分钟前
3
0
微服务分布式构架开发实战 附下载地址

微服务是一种软件架构风格,目标是将一个复杂的应用拆分成多个服务模块,每个模块专注单一业务功能对外提供服务,并可以独立编译及部署,同时各模块间互相通信彼此协作,组合为整体对外提供完...

Idea
35分钟前
6
0
AppEmit v0.9.5 新功能 chrome 浏览器调用本地应用程序 支持打开 编辑 金山 wps office,  excel  ppt

AppEmit v0.9.5 新功能 浏览器调用本地应用程序 支持打开 编辑 金山 office 更多主页 能在以及几乎所有支持websock浏览器,包括chrome各个版本,支持打开 编辑 金山 wps office, excel ppt ...

AppEmit
38分钟前
5
0
Git(学习二)----->Tortoise Git(图形化工具) 安装

下载: 本人使用版本为2.4.0.2有需要的可以从网盘下载,也可以百度找资源 链接:https://pan.baidu.com/s/13Vu0VNUQMZNFKLFhl6VzDw 提取码:y27p 一、安装 步骤1:安装向导 步骤2:许可介绍 步...

安然_oschina
42分钟前
7
0
TarsGo 服务获取自定义模版(配置)值

作者:amc 导语:之前的文章中我们介绍了如何使用TarsGo创建HTTP服务和TARS RPC服务,本篇文章中,我们将介绍如何在TarsGo服务中获得自定义模板的配置值。 TARS 模版是什么? TARS 框架中,有...

TARS基金会
58分钟前
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部