C知识点总结静态动态存储_第1页
C知识点总结静态动态存储_第2页
C知识点总结静态动态存储_第3页
C知识点总结静态动态存储_第4页
C知识点总结静态动态存储_第5页
已阅读5页,还剩14页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、几个问题: 局部变量和静态变量的存储*133竈 下面的初始化有什久问题?堀译菽翼示"invalid initializers"就算他住息.char *p nialloc (LOH*:这个声明是静态或非局部变量吗?函数谓用只詭出現在自动变就(即局邯命静态斐钛)的初 始式中.即局部变量可以用类型相符的任意表达式来初始化,而全局变量和静态变量只能用常量表达为什么左边的会打印出乱码,而右边的 可以正常输出char * getmemory()char str=”hello world ” return str;int mai n()char *ptr=getmemory();prin

2、tf( %sn”,ptr);return 0;main栈帧ptrprintf栈帧printf会占用以前getmemory的栈空间, 改写这块内存。导致这里的字符串被改 写。printf打印时出现乱码。void fun( char *p) int a10;printf( %sn”,p);char * getmemory() ” ”char str= ” hello world ”; return str;int mai n()char *ptr=getmemory();fun( ptr);return 0;fun函数占用了 getmemory的栈空间, 但是fun的局部变量数组a因为没有初 始化

3、,所以只为a分配了内存,却没有 修改a占用的内存;该内存的内容还是 以前的内容。效果就是, a把字符串“ hello world ”保护起来。使得printf式(Constant Expression)初始化。因为对它们的初始化是在编译阶段完成的。为什么不应返回指向局部 变量的指针。char * itoa()char str20; return str;若多次调用函数,为什么b=3 都不再执行了?fun()static int b=3; b+;对b的初始化操作在编译时 已经完成了。递归函数的变量n,有几个?void f(int n)if (n <=0) return ;printf( %

4、dn” n);f(n -1);printf( &n %p n=%dn” &n, n);能打印出它。函数栈帧是什么时候分配的?执行函数体时分配的地址空间物理内存是从0号开始编址,为什么 0号地址不能访问,下面的代码总是段错误例:int *ptr=NULL; int a=*ptr;为什么不能修改常量区的数据,这是如何被保护的。例: int *ptr= "hello ” *ptr= 'm'讲这些之前,首先让我们了解虚拟存储器的概念虚拟存储器诡拟内基_如果它存在t而且你施看见它 它是具实的(real)如黑它不存在,但你能看见它它是虚拟(virtual)如黑它存

5、在、但你盾不见它它是透明的(transparent)如農它不存在,而且你也看不见它那肯定是你把它擦棹了.田M埒于解释虐拟内存的张贴画尢约是在年(理论参考存储管理 PPT,另参考深入理解计算机系统和C专家编程7.3节对虚拟内存的思考)硬盘、内存、CPU寄存器,还有本节要讲的Cache,这些都是存储器,计算机为什么要有这么多种存储器呢?这些存储器各自有什么特点?由于硬件技术的限制,我们可以制造出容量很小但很快的存储器,也可以制造出容量很大但很慢的存储器,但不可能两边的好处都占着,不可能制造出访问速度又快容量又大的存 储器。因此,现代计算机都把存储器分成若干级,称为Memory Hierarchy

6、(存储器层次结构),按照离CPU由近到远的顺序依次是 CPU寄存器、Cache内存、硬盘,越靠近CPU的存储器 容量越小但访问速度越快,下图给出了各种存储器的容量和访问速度的典型值。更小 更快和每字节) 成本更高的 存储设备LI高遼耀存(SRAM)12高邃袈存(SEIAM)离邃缓存 (SRAM)CPU寄存器探徉若从高連耀存押储器取出的宇,口高連總存帰祥莉从12 髙速煖存取岀的黑存行L2髙連規存棵存着从H 高速舉存取出的按存行更大 更慢和(每字节j 差别更低的 存储设备主存DRAM )IS本地二級存储(本地議盘)远程二S曲(分布式文件靈统、务誥)口离速製存保存着从主存 '奇連醍存取出的缓

7、存行,主存保序着从半地機盘取出的盘盘块本地磁盘课萍蓉从通程网蜡服务器磁盘上取出的文件图金2d存储器层衣结构特点: 将整个存储系统看作一个大内存。一般而言,冲高层往底层走,存储设备变得更慢、更便宜和更大。对于每个k,位于k层的更快更小的存储设备作为位于k+1层的更大更慢的存储设备的缓存。换句话说,层次结构中的每一层都缓存来自较低一层的数据对象。目的,让程序不受系统物理内存大小的限制。 基于缓存的存储器层次结构行之有效,是因为较慢的存储设备比较快的存储设备更便宜,还因为程序往往展示局部性,具有良好性的程序通常比局部性差的程序运行得更快。()局部性原理(principle of locality):

8、是指程序在执行过程中的一个较短时期,所执行的指 令地址和指令的操作数地址,分别局限于一定区域。表现为:1. 时间局部性:即一条当前被执行的指令可能很快又被执行,或一个当前被访问的数据 可能很快又被访问到。那么他应当保存在缓存中。用在数据块淘汰算法。2. 空间局部性:即一条当前被执行的指令,它邻近的几条指令也可能要被执行;或一条当前被访问的数据, 他邻近的数据也可能要被访问。那么他应当保存在内存中。用在指令和数据的预取功能(数据总是以块大小为传送单元在第k层和第k+1层之间来回拷贝的; 不同层次之间可以有不同的块大小)。局部性原理的具体体现程序在执行时,大部分是顺序执行的指令,少部分是转移和过程

9、调用指令。 过程调用的嵌套深度一般不超过5层,因此执行的范围不超过这组嵌套的过程。程序中存在相当多的循环结构,它们由少量指令组成,而被多次执行。 程序中存在相当多对一定数据结构的操作,如数组操作,往往局限在较小范围内。虚拟存储的基本原理1在程序装入时,不必将其全部读入到内存,而只需将当前需要执行的部分页(这些页 面为 工作集”)或段读入到内存,就可让程序开始执行。2在程序执行过程中,如果需执行的指令或访问的数据尚未在内存(称为缺页或缺段) 则由处理器通知操作系统将相应的页或段从硬盘调入到内存,然后继续执行程序。虚拟地址空间由存储单元(字节或字)组成的一维连续的地址空间,简称内存空间。用来存放当

10、前正在运行程序的代码及数据。逻辑地址(相对地址,虚拟地址):用户的程序经过汇编或编译后形成目标代码,目标 代码通常采用相对地址的形式。CPU执行指令时看到的是逻辑地址。其首地址为0,其余指令中的地址都相对于首地址来编址。不能用逻辑地址在内存中读取信息。例:用objdump反汇编a.out,查看,编译时,给指令和数据分配的地址。见例题。物理地址(绝对地址,实地址):物理内存中存储单元的地址。物理地址可直接寻址。地址映射:将用户程序中的逻辑地址转换为运行时由机器直接寻址的物理地址。当程序运行,即需要装入内存时,操作系统要为该程序分配一个合适的内存空间,由于 程序的逻辑地址与分配到内存物理地址不一致

11、,所以当cpu访问内存时,先要进行地址转换。页表:记录逻辑地址和物理地址的对应关系。内存管理单元 MMU :该硬件读取页表,进行地址映射和内存保护。如果处理器启用了 MMU,CPU执行单元发出的内存地址将被MMU截获,从CPU到MMU的地址称为虚拟地址(Virtual Address,以下简称 VA),而MMU将这个地址翻译成另 一个物理地址(Physical Address,以下简称 PA)发到CPU芯片的外部地址引脚上,也就是 将VA映射成PA。如下图所示。CPU芯片主存数据字图42 个便用虚拟寻址的系统程序的虚拟地址范围与系统有关。这个范围的大小由CPU的地址总线位数决定,例如一个32位

12、地址总线的 CPU,它可寻址的地址范围是00xFFFFFFFF(4G),而对于一个64位的CPU,它的地址范围为 00xFFFFFFFFFFFFFFFF (64T这个范围就是我们的程序能够使用的地 址范围,我们把这个地址范围称为虚拟地址空间,该空间中的某一个地址我们称之为虚拟地址。对于一台内存为 256M的32bit x86主机来说,它的虚拟地址空间范围是00xFFFFFFFF(4G),而物理地址空间范围是 0x0000000000x0FFFFFFF( 256M)。现代的操作系统使用一种 分页(paging)的内存管理机制。虚拟地址空间划分成称为页 (page)的单位,而相应的物理地址空间也被

13、进行划分,单位是页桢(frame)。页和页桢的大小必须相同。例如,若页的大小为4K,则页桢大小也为 4K这点是必须保证的,而且内存和外围存储器(磁盘等)之间的传输总是以页为单位的。linux使用了段页式 的内存管理,吸收了段式和页式的优点。(见ppt的解释)对于4G的虚拟地址和256M的物理存储器,他们分别包含了1M个页和64K个页桢。页表就是记录页与物理内存的页帧的对应关系的。注意:并不是每个页都对应一个页帧。若一个页不对应任何页帧,则该页不可访问,试图去访问就会出现常见的段错误。例如,linux系统保证对0号页及附近的一些虚拟页不对应任何物理页帧,即虚地址0不对应任何物理地址,因此对虚地址

14、 0的访问会出现段错误。空指针NULL每个进程都有独立的虚拟地址空间。 以32bit x86主机来说,每个进程都有4G的虚拟地 址空间。这个功能是通过页表(将不同进程相同的虚拟地址映射到不同的物理地址上)来实现的。例如,进程 A在地址0X8049000写一个字符''进程B同样在地址0X8049000写字 符''如果 A进程读地址 0X8049000,读到的是进程 A写入的''而进程 B读取地址 0X8049000时,则读到的是 B写入的''两者不会冲突。这里,进程A的虚拟地址0X8049000 和进程B的虚拟地址0X8049000

15、对应的物理地址不同,因此他们之间没有干扰。例如:例题演示,两个进程,变量的地址相同,在两个进程里修改这个变量,他们彼 此不干扰。地址映射机制的产生,使得只有对应有物理页帧的虚拟页才是可访问的;实现不同进 程的虚拟页对应同一个物理页帧,达到内存共享的目的。进程地址空间是独立的MMU将VA映射到PA是以页(Page)为单位的,32位处理器的页尺寸通常是4KB。例如,MMU 可以通过一个映射项将VA的一页 0xb70010000xb7001fff映射到 PA的一页0x20000x2fff,如果CPU执行单元要访问虚拟地址0xb7001008,则实际访问到的物理地址是0x2008。物理内存中的页称为物

16、理页面或者页帧( Page Frame)。虚拟内存的哪个页面映 射到物理内存的哪个页帧是通过页表(Page Table)来描述的,页表保存在物理内存中,MMU会查找页表来确定一个VA应该映射到什么 PA。操作系统和 MMU是这样配合的: 操作系统在初始化或分配、释放内存时会执行一些指令在物理内存中填写页表,然后用指令设置MMU,告诉MMU页表在物理内存中的什么位置。 设置好之后,CPU每次执行访问内存的指令都会自动引发MMU做查表和地址转换操作,地址转换操作由硬件自动完成,不需要用指令控制MMU去做。我们在程序中使用的变量和函数都有各自的地址,程序被编译后,这些地址就成了指令中的地址,指令中的

17、地址被 CPU解释执行,就成了 CPU执行单元发出的内存地址,所以在 启用MMU的情况下,程序中使用的地址都是虚拟地址,都会引发MMU做查表和地址转换操作。那为什么要设计这么复杂的内存管理机制呢?多了一层VA到PA的转换到底换来了什么好处?All problems in computer science can be solved by another level of indirection. 计算机科学的所有问题都可以通过增加一个抽象层来解决。在32位系统上的进程执行于 32位地址空间。操作系统负责具体细节。使得每个进程都以为自己拥有整个地址空间的独家访问权。这个幻觉是通过“虚拟内存”实现

18、的。所有的进程共享同一个物理内存。当内存用完时就用磁盘保存数据。在进程运行时,数据在磁盘和内存之间来回移动。内存管理硬件 MMU负责把虚拟地址翻译为物理地址,并让一个进程运行于系统的真正内存中。 应用程序员只看到虚拟内存, 并不知道自己的进程在磁盘和内存之间 来回切换。除非他们诸如“ ps “之类的系统命令。命令ps aux有两列分别显示虚拟内存和物理内存的使用大小。MMU除了做地址转换之外,还提供内存保护机制。各种体系结构都有特权模式(Privileged Mode,也称内核态)和用户模式( User Mode,也称用户态)之分。intel有四种工作模式(0-3),linux只使用0和3两种

19、模式,分别对应 特权模式,和用户模式。操作系统可以在页表中设置每个内存页面的访问权限,有些页面不允许访问, 有些页面只有在CPU处于特权模式时才允许访问,有些页面在用户模式和特权模式都可以访问,访 问权限又分为可读、可写和可执行三种。例如:用命令 pmap显示一个进程内存映像。这样设定好之后,当 CPU要访问一个 VA时,MMU会检查CPU当前处于用户模式还是 特权模式,访问内存的目的是读数据、写数据还是取指令,如果和操作系统设定的页面权限 相符,就允许访问,把它转换成PA否则不允许访问,产生一个异常(Exception)。异常的处理过程和中断类似,不同的是中断由外部设备产生而异常由CPU内部

20、产生,中断产生的原因和CPU当前执行的指令无关,而异常的产生就是由于CPU当前执行的指令出了问题,例如访问内存的指令被 MMU检查出权限错误,除法指令的除数为0等都会产生异常。处理器模式Interrupt orInterrupt or exception32位x86平台下,进程的 4G虚拟地址空间又分为用户空间和系统空间。用户空间:00Xbfffff的地址空间,用于存放用户的程序和数据。系统空间:0xc00000000xffffffff的地址空间,用于存放内核和内核数据。(每个进程的系统空间的内容基本相同,通过地址映射来实现物理内存共享的。)用户程序加载到用户空间,在用户模式下执行,不能访问内

21、核中的数据,也不能跳转到内核代码中执行。这样可以保护内核,如果一个进程访问了非法地址,顶多这一个进程崩溃,而不会影响到内核和整个系统的稳定性。CPU在产生中断或异常时不仅会跳转到中断或异常服务程序,还会自动切换模式, 从用户模式切换到特权模式,因此从中断或异常服务程序可以跳转到内核代码中执行。事实上,整个内核就是由各种中断和异常处理程序组成的。总结一下:在正常情况下处理器在用户模式执行用户程序,在中断或异常情况下处理器切换到特权模式执行内核程序,处理完中断或异常之后再返回用户模式继续执行用户程序。段错误我们已经遇到过很多次了,它是这样产生的:1. 用户程序要访问的一个 VA经MMU佥查无权访问

22、。2. MMI产生一个异常,CPU从用户模式切换到特权模式,跳转到内核代码中执行异常服务程序。3. 内核把这个异常解释为段错误,向该进程发送一个信号(sig nal),把引发异常的进程终止掉。虚拟内存管理的作用,可以从以下四个方面来理解。第一,虚拟内存管理可以 控制物理内存的访问权限 。物理内存本身是不限制访问的,任何地址都可以读写,而操作系统要求不同的页面具有不同的访问权限,这是利用CPU模式和MMU的内存保护机制实现的。例如,Text Segment代码段被只读保护起来,防止被错误的指令意外改写,内核地址空间也被保护起来,防止在用户模式下执行错误的指令意外改写 内核数据。这样,执行错误指令

23、或恶意代码的破坏能力受到了限制,顶多使当前进程因段错误终止,而不会影响整个系统的稳定性。第二,虚拟内存管理最主要的作用是让 每个进程有独立的地址空间 。所谓独立的地址空 间是指,不同进程中的同一个 VA被MMU映射到不同的PA并且在某一个进程中访问任何 地址都不可能访问到另外一个进程的数据,这样使得任何一个进程由于执行错误指令或恶意代码导致的非法内存访问都不会意外改写其它进程的数据,不会影响其它进程的运行,从而保证整个系统的稳定性。 另一方面,每个进程都认为自己独占整个虚拟地址空间,这样链接器和加载器的实现会比较容易,不必考虑各进程的地址范围是否冲突。动态库.so shared object

24、共享目标使用共享库可以大大节省内存。比如libc,系统中几乎所有的进程都映射libc到自己的进程地址空间,而libc的只读部分在物理内存中只需要存在一份,就可以被所有进程共享, 这就是“共享库”这个名称的由来了。现在我们也可以理解为什么共享库必须是位置无关代码了。比如libc,不同的进程虽然共享libc所在的物理页面,但这些物理页面被映射到各进程的虚拟地址空间时却位于不同的 地址,所以要求libc的代码不管加载到什么地址都能正确执行。第三,VA到PA的映射会给分配和释放内存带来方便,物理地址不连续的几块内存可以映射成虚拟地址连续的一块内存。即虚拟地址连续,物理地址不一定连续。比如要用mallo

25、c分配一块很大的内存空间,虽然有足够多的空闲物理内存,却没有足够大的连续空闲内存, 这时就可以分配多个不连续的物理页面而映射到连续的虚拟地址范围。如下图所示。不连续的PA可臥映射为连续的VA094000000940100005402000RAM第四,一个系统如果同时运行着很多进程,为各进程分配的内存之和可能会大于实际 可用的物理内存,虚拟内存管理使得这种情况下各进程仍然能够正常运行。因为各进程分配的只不过是虚拟内存的页面,这些页面的数据可以映射到物理页面,也可以临时保存到磁盘上而不占用物理页面,在磁盘上临时保存虚拟内存页面的可能是一个磁盘分区,也可能是一个磁盘文件,称为交换设备(Swap De

26、vice)。当物理内存不够用时,将一些不常用的物理页面中的数据临时保存到交换设备,然后这个物理页面就认为是空闲的了,可以重新分配给进程使用,这个过程称为换出 (Page out)。如果进程要用到被换出的页面,就从交换设备再加载回物理内存,这称为换入(Page in)。换出和换入操作统称为换页(Paging)。CPU迸程虚拟 地址空何系统中的係 个进秤都肓 白己的地址 空问OLGF面是一个linux的4G空间的典型布局。其中阴影部分代表未被使用。(深入理解计算机系统第九章)r与进程相关的数据结枸 (钊虬页表* Ek和rum结构.内核战)物理存储雕r内核代码和数据Si堀户栈共卓库的存傭褂映附医域-

27、丄运行时堆(運过malloa并配的)未初始化的数据(b&a)已初始化歎据(ata)程序文件t«t0对每个逊程 都不相同对每牛进程 都一样%esp0x08044000(31)0x40000000(4)卜内械虚拟存储眾进程虚拟存储器图4笳 一牛Linux迸程的虚拟存储器下面对各个段介绍。linux系统对不同的段赋予不同的权限。.text文本段:包含程序的指令,这个段是可读和可执行的。在 定存放在虚拟地址 0X8048000开始的地方。.data已初始化数据段:存放已经初始化的全局变量和静态变量。32位系统中,文本段固.bss未初始化数据段:存放未初始化的全局变量和静态变量。.da

28、ta段和.bss段统称为 数据段,数据段在文本段的下一个页开始的地方。数据段的下 个页开始是 堆,堆向上生长,即向高地址方向延伸。栈(又称为堆栈段):用于函数调用,存放局部变量,传递参数,返回地址等。在接近 系统空间的地方,栈向下生长,栈的大小是有限制的,一般12M大小。例如:写代码估计linux下堆的最大值。并用ps命令查看进程的内存使用情况,命令free查看物理内存的使用情况。下面是C源文件和可执行程序的对应关系。6运行时创建c诸引的各部分公出现4:啣些段'V可执行文件和内存中的进程各段的对应关系。c专家编程第六章)进程的地址空间址嵐内力地址堆找段(局祁F晦啟的散獣a out的神惜

29、読亍1 rLout的扎他丙牡assetBssHfiS需的大小数陽段经过初齡化杓住局Sl按段町曲订文件的指令h魄射区域|3.OUC文書空衍赋址内依地如卜图6 2可执行文件中的段在内存中如何布局t木初输化的竝却:;蚌过初鴉化的紘側】size例子:用命令size查看一个a.out文件的各段大小。修改你的源文件,增加一个未初始 化的int a100数组,编译后用size看看。给数组a初始化一些非零值,编译后再用查看各段的大小变化。若把该数组放到main函数里呢?由上可知,全局变量和静态变量的值,地址和占用空间的大小,在编译时就已经确定; 根据是否初始化,分别存储在 .data段和.bss段。栈的典型分

30、布(深入理解计算机系统8.4.5节)name=value图821当一个新的程序开始时;用户栈的典型组织结构(实际上,一个进程的栈段的栈底不在Oxbfffffff,出于安全的考虑,在进程创建时,系统在Oxbfxxxxxx的虚拟地址范围内取一个地址作为栈的栈底。)bfOOOOOO bfffffffchar *argvarffvt*la"II,/user/lttcludar,图&19参数列表的组织结构envpU图费20环境变量列表的组织结构栈的特点:一块连续的虚拟地址空间,实现了 “后进先出”的结构。类似于叠在一起的 盘子,只能从顶部取出。栈段的用途:1)栈为函数内部声明的 局部变

31、量 提供存储空间。2)进行一次函数调用时, 系统在栈顶分配一段空间(通常大小为16字节的倍数),存储与该函数调用相关的一些维护性信息。这些信息称为栈结构或栈帧(即是上面例子里的盘子)。这些信息包括 函数调用地址(即当被调用的函数结束后,调用函数接下来需要执行的语句的地址,这个入栈操作由call指令完成)、任何不适合装入寄存器的参数(比如参数需要的存储空间大或需要对参数取地址。因为寄存器的访问速度快,并不是参 数都要放到栈中) 以及一些寄存器值的保存(例如,上一个栈帧空间的起始地址,即 寄存器ebp的值),为了重用寄存器;以及 1和3里提到的用途。3)栈可被用作暂时存储区。有时候程序需要一些临时

32、存储,比如计算一个很长的算术表达式时,可以把中间结果暂时存储到栈中,用的时候再取出。由上可知,栈主要用于函数调用,因此又称函数栈。对函数调用的分析,需要知道系统如何利用栈,来跟踪函数之间的调用关系;当函数执行return语句后,控制将返回到哪里;当产生函数调用时(例如调用函数 fun),系统给被调用的函数(fun)分配一个栈帧, 在这里可以记录上面提到三方面的信息。梶W1給构(牲用来传递掺数、存储返回惜克、保存奇存器.以及市地存储)几个重要寄存器:eip:指令寄存器,记录将要执行的下一条指令的地址。也称为 ebp:记录当前栈帧的起始地址esp:记录当前栈的栈顶地址eax:在函数返回时,可存放返

33、回值(前提是,返回值能放到 值需要放在栈里。)pc寄存器4个字节里。否则,返回对于下面的代码,当函数 caller调用swap_add时,caller的函数栈空间如右图所示。int caller0-int argl 534; int 1067;Int sum = 3wap_add(£argl,血虹骅); Int diff - argl - arg2;return sun * diff;.这里演示了一个典型的栈帧的分布。保存寄存器局部变量空间区未使用空间(出于对齐的考虑)参数构造区(被调用函数的形参空间)返回地址例题:验证保存的返回地址就是调用函数的下一条指令的地址分析寄存器eax和返回值有关。该分布图解释了执行下面这条语句为什么会出现段错误。void fun()int a10;a10=1;进入一个函数体前,保存以前用过的ebp,和某些通用寄存器例如ecx。紧挨着的是局部变量的存储空间。若该函数内进行了函数调用,在栈顶,把所有不适合放到寄存器的实参值按照从右向左顺序,依次入栈(这其实就是个形参初始化的操作),这些存储空间就是被调用函数形参的存储空间。为何不能从函数中返回一个指向该函数局部变量的指针?函数返回后,给它分配的栈帧空间的访问合法性就

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论