杂乱知识总结(strcpy,memcpy.fork,)

本文详细解析了C程序执行时的启动过程、内存管理机制,包括内核如何加载并执行程序,如何处理命令行参数和环境变量,以及fork、vfork和exec函数的作用。同时,阐述了strcpy、memcpy、sprintf等字符串与内存复制函数的区别与使用场景,以及全局变量、文件锁和线程间互斥操作的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当内核执行C程序时,(使用一个exec函数),在调用main函数之前县调用一个特殊启动历程(通常用汇编语言编写,就是运行库)。可执行程序文件将此启动例程指定为程序的起始地址。启动例程从内核取得命令行参数环境变量值,然后为按上述方式调用main函数做好安排。

环境表:每个程序都会接受到一张环境表。历史上main函数曾经有三个参数,第三个参数就是环境表的地址。在exec的四个函数中有参数用来传递环境表。

进程地址空间:内核除了管理本身的内存外还必须管理用户空间中进程的内存,我们称这个内存为进程地址空间。

fork:fork产生的进程与子进程的正文代码段是共享的,但是其他的堆栈空闲区数据段是不共享的,子进程用的是父进程的一个副本(采用的是写时复制技术,进程地址空间部分相同)。

vfork:vfork在调用exec之前与父进程是完全共享的,包括正文段、堆栈以及数据段等。

exec:调用exec并不创建新进程,exec只是用一个全新的程序替换了当前进程的正文、数据、堆和栈。此时这个进程与父进程在正文、数据段、堆栈上已经完全不相关(即进程地址空间完全不同了)。


strcpy、memcpy和sprintf三个函数主要区别:

strcpy只能够执行字符串的复制。

memcpy能够执行内存上任意数据的复制。

sprintf主要用于格式化复制

sprintf 可以进行额外的格式化
strcpy 会复制直到出现 '' 为止,可能溢出
strncpy 会复制一个以 '' 结束的字符串,但是如果字符串长度超过指定数量则被截断,但结果可能不包含 '' 表示结束
memcpy 只负责复制指定数量的 bytes,不处理 '' 的情况
memmove 在 memcpy 的基础上对 overlap 的情况进行了处理。

这些函数的区别在于 实现功能 以及 操作对象 不同。

strcpy 函数操作的对象是 字符串,完成 从 源字符串 到 目的字符串 的 拷贝 功能。

snprintf 函数操作的对象 不限于字符串:虽然目的对象是字符串,但是源对象可以是字符 串、也可以是任意基本类型的数据。这个函数主要用来实现 (字符串或基本数据类型)向 字符串 的转换 功能。如果源对象是字符串,并且指定 %s 格式符,也可实现字符串拷贝功能。

memcpy 函数顾名思义就是 内存拷贝,实现 将一个 内存块 的内容复制到另一个 内存块 这一功能。内存块由其首地址以及长度确定。程序中出现的实体对象,不论是什么类型,其最终表现就是在内存中占据一席之地(一个内存区间或块)。因 此,memcpy 的操作对象不局限于某一类数据类型,或者说可 适用于任意数据类型,只要能给出 对象的起始地址和内存长度信息、并且对象具有可操作性即可。鉴于 memcpy 函数等长拷贝的特点以及数据类型代表的物理意义,memcpy 函数通常限于同种类型数据或对象之间的拷贝,其中当然也包括字符串拷贝以及基本数据类型的拷贝。

对于字符串拷贝来说,用上述三个函数都可以实现,但是其实现的效率和使用的方便程度不同:

  • strcpy 无疑是最合适的选择:效率高且调用方便。
  • snprintf 要额外指定格式符并且进行格式转化,麻烦且效率不高。
  • memcpy 虽然高效,但是需要额外提供拷贝的内存长度这一参数,易错且使用不便;并且如果长度指定过大的话(最优长度是源字符串长度 + 1),还会带来性能的下降。其实 strcpy 函数一般是在内部调用 memcpy 函数或者用汇编直接实现的,以达到高效的目的。因此,使用 memcpy 和 strcpy 拷贝字符串在性能上应该没有什么大的差别。

对于非字符串类型的数据的复制来说,strcpy 和 snprintf 一般就无能为力了,可是对 memcpy 却没有什么影响。但是,对于基本数据类型来说,尽管可以用 memcpy 进行拷贝,由于有赋值运算符可以方便且高效地进行同种或兼容类型的数据之间的拷贝,所以这种情况下 memcpy 几乎不被使用。memcpy 的长处是用来实现(通常是内部实现居多)对结构或者数组的拷贝,其目的是或者高效,或者使用方便,甚或两者兼有。

strcpy和memcpy功能上也有些差别:
比如:
const char *str1="abc\0def";
char str2[7];

首先用strcpy实现:
strcpy(str2,str1)
得到结果:str2="abc";也就是说,strcpy是以'\0'为结束标志的。


再用memcpy实现:
memset(str2,7);
memcpy(str2,str1,7);
得到结果:str2="abc\0def";

也就是说,memcpy是对内存区域的复制。当然,不仅能够复制字符串数组,而且能够复制整型数组等其他数组。


        fork出来的两个不同的进程运行在不同的地址空间,对全局变量的访问方式采用的是写时复制技术,所以一个进程A对全局变量a改写之后,进程B是不可见的。

但是这两个进程读写文件的时候就需要加fcntl文件锁对文件进行互斥操作,因为对文件的修改时彼此可见的。

       线程对全局变量的访问是彼此可见的所以需要使用互斥操作,就像内核中的全局变量的访问就需要进行互斥操作。

当文件A想要访问文件B的static全局变量的时候,可以在文件B中用一个函数封装这个全局变量,在文件A中来调用这个函数。

memcpystrcpy有以下几个区别: 1. 复制的内容不同:strcpy只能复制字符串,而memcpy可以复制任意内容,包括字符数组、整型、结构体、类等。\[2\] 2. 复制的方法不同:strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,如果空间不够,就会引起内存溢出。而memcpy则是根据其第三个参数决定复制的长度,可以精确控制复制的字节数。\[2\] 3. 用途不同:通常在复制字符串时使用strcpy,而需要复制其他类型的数据时则一般使用memcpy。由于字符串是以"\0"结尾的,所以对于在数据中包含"\0"的数据只能使用memcpy。\[2\] 此外,从效率上来看,根据源码分析,可以发现在拷贝相同字符串的情况下,memcpy函数中有3个变量在变化,而strcpy只有两个变量变化,因此可以认为strcpy的效率更高。\[3\] #### 引用[.reference_title] - *1* *3* [memcpystrcpy区别以及它们的效率分析](https://blog.csdn.net/vgxpm/article/details/47614901)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [strcpymemcpy的区别](https://blog.csdn.net/jiaoty19/article/details/125169454)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值