美文网首页
第七章 文件读写(OS&IO操作)

第七章 文件读写(OS&IO操作)

作者: 帅气的Lucky | 来源:发表于2021-08-23 19:09 被阅读0次

    IO操作

    一、读

    1、读文件

    • 过程

      a、找到文件

      b、打开文件

      c、读取文件的内容

      d、关闭文件

    • 找到文件

      绝对路径:从根目录开始链接的路径

      相对路径:不是从根目录开始链接的路径

      path = r"file.txt"
      
    • 打开文件

      • 原型

        def open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True)
        
      • 参数

        file:要打开的文件的路径

        mode:打开方式

        encoding:编码格式

        errors:错误处理方式(ignore表示直接忽略)

      • 返回值

        文件描述符,从当前的位置操作当前打开的文件

      • 打开方式

        方式 说明
        r 以只读的方式打开文件,文件的引用(描述符)将会被放在文件开头
        rb 以二进制格式打开只读文件,文件的引用(描述符)将会被放在文件开头
        r+ 以读写的方式打开文件,文件的引用(描述符)将会被放在文件开头
        w 以只写的方式打开文件,如果该文件存在,则将其内容覆盖,如果文件不存在则会创建该文件
        wb 以二进制格式打开只写文件,如果该文件存在,则将其内容覆盖,如果文件不存在则会创建该文件
        w+ 以读写的方式打开文件,如果该文件存在,则将其内容覆盖,如果文件不存在则会创建该文件
        a 打开一个文件用于追加内容,如果该文件存在,文件描述符会被放到文件的末尾,如果文件不存在则会创建该文件
        ab 打开一个文件用于追加写,如果文件存在,文件描述符将会放到文件末尾 不存在则创建
        a+ 打开一个文件用于读写,如果该文件存在,文件描述符会被放到文件的末尾,如果文件不存在则会创建该文件
      • 打开

        打开普通文件

        fp = open(path, "r")
        

        打开二进制文件

        fp = open(path, "rb")
        

        指定编码格式

        fp = open(path, "r", encoding="utf-8")
        

        指定错误处理方式

        fp = open(path, "r", encoding="utf-8", errors="ignore")
        
    • 读取文件的内容

      函数名称 函数说明
      read() 读取文件的全部内容
      readline() 读取整行内容
      readlines 读取所有行并返回一个列表
      seek(offset[,1whence]) 方法用于移动文件读取指针到指定位置whence:可选,默认值为 0。给offset参数一个定义,表示要从哪个位置开始偏移;0代表从文件开头开始算起,1代表从当前位置开始算起,2代表从文件末尾算起
      next(file) 返回文件下一行
      • 读取文件的全部内容

        str1 = fp.read()
        print(str1)
        
      • 读取指定字节数的内容

        str2 = fp.read(4)
        print(str2)
        
      • 读取整行内容(包括\n字符)

        str3 = fp.readline()
        print("*"+str3+"*")
        
      • 读取指定字节数内容

        str4 = fp.readline(13)
        print("*"+str4+"*")
        
      • 读取所有行并返回一个列表,列表中的元素是每行内容

        list5 = fp.readlines()
        print(list5)
        
    • 修改文件描述符的位置

      str6 = fp.read(4)
      print("*"+str6+"*")
      str7 = fp.read(4)
      print("*"+str7+"*")
      # 修改文件描述符的位置
      fp.seek(1)
      str8 = fp.read(4)
      print("*"+str8+"*")
      
    • 关闭文件

      • 注意

        文件使用过后必须关闭

      • 原因

        释放资源,系统能打开的文件个数是有限制的,所以需要释放相应文件的文件描述符

      • 关闭方式

        程序结束自动关闭:程序结束时会释放文件对象的空间,文件会关闭,但是不建议这样来做,最好手动关闭

        手动关闭:调用代码关闭

      • 示例

        fp.close()
        

    2、读文件完整过程

    try:
        fp = open("file.txt", "r")
        print(fp.read())
    finally:
        if fp:
            fp.close()
    

    3、读文件简写方式

    with open("file.txt", "r") as fp:
        print(fp.read())
    

    二、写

    1、写文件

    • 过程

      • 找到文件
      • 打开文件
      • 将内容写入缓冲区,此时内容没有写入文件
      • 刷新缓冲区,直接把缓存区中的数据立刻写入文件
      • 关闭文件
    • 刷新缓冲区方式

      • 程序结束
      • 关闭文件
      • 手动刷新
      • 缓冲区满了
      • 遇到\n
    • 函数

      函数名 函数说明
      file.write() 将字符串写入文件,没有返回值。
      file.writelines() 向文件写入一个序列字符串列表,如果需要换行则要自己加入每行的换行符。没有返回值
      file.flush() 刷新文件内部缓冲,直接把内部缓冲区的数据立刻写入文件, 而不是被动的等待输出缓冲区写入。
      file.tell() 返回文件当前位置。
    • 找打文件

      path = "file.txt"
      
    • 打开文件

      fp = open(path, "w")
      
    • 将内容写入缓冲区

      fp.write("lucky good")
      
    • 手动刷新缓冲区

      fp.flush()
      
    • 关闭文件

      fp.close()
      

    2、写文件完整过程

    try:
        fp = open("file.txt", "w")
        fp.write("cool man")
    finally:
        if fp:
            fp.close()
    

    3、文件关闭

    你可以反复调用write()来写入文件,但是务必要调用f.close()来关闭文件。当我们写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。只有调用close()方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用close()的后果是数据可能只写了一部分到磁盘,剩下的丢失了。所以,还是用with语句来得保险

    不使用with的情况

    file = open("test.txt","r")
    for line in file.readlines():
        print(line)
    file.close()
    

    这样直接打开文件,如果出现异常,如读取过程中文件不存在或异常,则直接出现错误,close方法无法执行,文件无法关闭

    使用with的情况

    file= open("test.txt","r")
    try:
        for line in file.readlines():
            print line
    except:
        print "error"
    finally:
        file.close()
    

    with语句作用效果相当于上面的try-except-finally

    4、写文件简写方式

    with open("file.txt", "w") as fp:
        fp.write("cool man")
    

    三、编码与解码

    Unicode:统一码,也叫万国码、单一码(Unicode)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。

    如果把各种文字编码形容为各地的方言,那么Unicode就是世界各国合作开发的一种语言。

    UTF-8是针对Unicode的一种可变长度字符编码;它可以用来表示Unicode标准中的任何字符,而且其编码中的第一个字节仍与ASCII相容,使得原来处理ASCII字符的软件无须或只进行少部份修改后,便可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。

    1、编码

    with open("file.txt", "wb") as fp:
        s = "lucky is a good man"
        s = s.encode("utf-8")
        fp.write(s)
    

    2、解码

    with open("file.txt", "rb") as fp:
        s = fp.read()
        s = s.decode("utf-8")
        print(s)
    

    编码与解码 :使用什么编码 就使用什么解码

    3、案例

    • 文件加密

      file = open('1.py','r')     
      myfile = open('2.py','a+')  
      x = file.read()  
      for i in x:              
          z = chr(ord(i)+10)   
          myfile.write(z)      
      
    • 文件解密

      file = open('4.py','r')       
      myfile = open('5.py','a+')
      x = file.read()  
      for i in x:            
          z = chr(ord(i)-10) 
          myfile.write(z)    
      

    4、chardet模块

    • 作用

      使用chardet检测编码,支持检测中文、日文、韩文等多种语言

    • 安装

      pip install chardet

    • 使用

      import chardet
      
      # 注意:数据量小,猜测的不准
      data = "lucky是一个好男人".encode("utf-8")
      # print(data)
      
      ret = chardet.detect(data)
      print(ret, type(ret))
      '''
      encoding:表示编码格式
      confidence:表示检测的正确的概率
      language:表示数据的语言
      '''
      
      '''
      GBK是GB2312的超集,两者是同一种编码
      '''
      info = data.decode(ret["encoding"])
      print(info)
      

    四、pickle 序列化的操作

    使用说明:可以将序列 序列化到 文件里 也就是 可以做到 原样写入 原样拿出 以二进制写进文件里 并以二进制的形式读取到内存里

    1、list、tuple、dict、set的文件操作

    • pickle模块

      持久化保存对象,将list、tuple、dict、set等数据序列化存储到文件

      import pickle
      
    • 函数

      函数名 函数说明
      dump() 将数据序列化后写入到 文件里
      load() 将数据反序列化 取出来
      dumps() 将数据序列化 直接返回
      loads() 将dumps序列化后的 进制 转换成 普通的数据类型
    • 存储

      import pickle
      user = {"account": "lucky", "passwd": "666"}
      with open("file.txt", "wb") as fp:
          pickle.dump(user, fp)
      
    • 读取

      with open("file.txt", "rb") as fp:
          user = pickle.load(fp)
      print(user, type(user))
      

    2、StringIO

    • 作用

      数据的读写不一定都是文件,也可以是内存中读写,StringIO可以在内存中读写字符串

      StringIO顾名思义就是在内存中读写str。

    • 导入

      from io import StringIO
      
    • 要把str写入StringIO,我们需要先创建一个StringIO,然后,像文件一样写入即可:

      getvalue()方法用于获得写入后的str。

      fp = StringIO()
      fp.write("lucky is\n")
      fp.write(" a good ")
      fp.write("man!")
      # 获取写入的内容
      print(fp.getvalue())
      
    • 要读取StringIO,可以用一个str初始化StringIO,然后,像读文件一样读取

      fp.seek(0)
      print(fp.read())
      fp.seek(0)
      print(fp.readline())
      fp.seek(0)
      print(fp.readlines())
      
      

      循环读

      from io import StringIO
      f = StringIO('Hello!\nHi!\nGoodbye!')
      while True:
          s = f.readline()
          if s == '':
              break
          print(s.strip())
      

    注意:文件使用后关闭文件

    3、BytesIO

    • 作用

      StringIO操作的只能是str,如果要操作二进制数据,就需要使用BytesIO。

    • 写入

      from io import BytesIO
      f = BytesIO()
      f.write('lucky老师'.encode('utf-8'))
      print(f.getvalue())
      

      请注意,写入的不是str,而是经过UTF-8编码的bytes。

    • 读取

      from io import BytesIO
      f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
      print(f.read())
      

    4、二进制文件

    原因大概有三个:

    第一是[二进制文件]比较节约空间,这两者储存[字符型数据]时并没有差别。但是在储存数字,特别是实型数字时,二进制更节省空间,比如储存 Real*4 的数据:3.1415927,文本文件需要 9 个字节,分别储存:3 . 1 4 1 5 9 2 7 这 9 个 ASCII 值,而[二进制文件]只需要 4 个字节(DB 0F 49 40)

    第二个原因是,内存中参加计算的数据都是用二进制无格式储存起来的,因此,使用二进制储存到文件就更快捷。如果储存为文本文件,则需要一个转换的过程。在数据量很大的时候,两者就会有明显的速度差别了。

    第三,就是一些比较精确的数据,使用二进制储存不会造成有效位的丢失。

    五、os模块

    1、os中常用属性和方法

    作用:包含了基本的操作系统功能,提供了非常丰富的方法用来处理文件和目录

    • 属性

      名称 说明
      name 操作系统的类型,nt表示windows,posix表示Linux、Unix
      uname 获取操作系统的信息,linux、Unix下使用
      environ 获取系统中的环境变量,environ.get()可以获取环境变量的值
      curdir 返回当前的目录
    • 方法

      名称 说明
      getcwd() 返回当前工作目录的绝对路径
      listdir() 返回指定目录下的所有文件和目录
      mkdir() 创建指定目录,注意目录已经存在时会报错,目录路径中存在不存在的层级时报错
      rmdir() 删除目录,注意目录不存在则报错
      rename() 重命名
      stat() 获取文件属性
      remove() 删除普通文件
      system() 运行shell命令 <br />运行shell命令 shutdown -s -f/ shutdown -a
    • 示例

      import os
      
      # 操作系统的类型
      # nt     windows
      # posix  Linux、Unix
      print(os.name)
      # 获取操作系统的信息,linux、Unix下使用
      print(os.uname())
      # 获取系统中的环境变量
      print(os.environ)
      # 获取指定环境变量的值
      print(os.environ.get("PATH"))
      
      # 返回当前的目录
      print(os.curdir)
      
      # 返回当前工作目录的绝对路径
      print(os.getcwd())
      
      # 返回指定目录下的所有文件和目录
      print(os.listdir(r"C:\Users\sunck\Desktop\code"))
      
      # 创建指定目录,注意目录已经存在时会报错,目录路径中存在不存在的层级时报错
      os.mkdir(r"C:\Users\sunck\Desktop\file")
      os.mkdir(r"C:\Users\sunck\Desktop\file\a\b")
      
      # 删除目录,注意目录不存在则报错
      os.rmdir(r"C:\Users\sunck\Desktop\file")
      
      # 重命名
      os.rename(r"C:\Users\sunck\Desktop\code\file1.txt", r"C:\Users\sunck\Desktop\code\file2.txt")
      
      # 获取文件属性
      print(os.stat(r"C:\Users\sunck\Desktop\code\file2.txt"))
      
      # 删除普通文件
      os.remove(r"C:\Users\sunck\Desktop\code\file2.txt")
      
      # 运行shell命令
      os.system("notepad")
      os.system("shutdown -s -t 10")
      os.system("shutdown -a")
      

    2、os.path中常用方法

    操作文件和目的函数一部分在os模块中,还有一部分在os.path中

    名称 说明
    abspath 返回指定路径的绝对路径
    join 拼接路径(不论是否存在)
    split 拆分路径(不论是否存在)
    splitdrive 以路径第一个'/'为分隔,分隔驱动器名与路径
    splitext 获取文件的扩展名(不论是否存在)
    basename 获取目录或文件名(不论是否存在)
    getsize 获取属性
    getctime 获取属性
    isdir 判断是否是目录
    isfile 判断是否是文件
    exists 判断目录和文件是否存在
    isabs 判断是否是绝对路径(不论是否存在)
    import os
    
    # 返回指定路径的绝对路径
    print(os.path.abspath("."))
    
    # 拼接路径(不论是否存在)
    print(os.path.join(r"C:\Users\sunck\Desktop\file", "a.txt"))
    
    # 拆分路径(不论是否存在)
    print(os.path.split(r"C:\Users\sunck\Desktop\file"))
    print(os.path.split(r"C:\Users\sunck\Desktop\file\sunck.txt"))
    # 以路径第一个'/'为分隔,分隔驱动器名与路径
    print(os.path.splitdrive(r"C:\Users\sunck\Desktop\file"))
    print(os.path.splitdrive(r"C:\Users\sunck\Desktop\file\sunck.txt"))
    # 获取文件的扩展名(不论是否存在)
    print(os.path.splitext(r"C:\Users\sunck\Desktop\file"))
    print(os.path.splitext(r"C:\Users\sunck\Desktop\file\sunck.txt"))
    
    # 获取目录名(不论是否存在)
    print(os.path.basename(r"C:\Users\sunck\Desktop\file"))
    # 获取文件名(不论是否存在)
    print(os.path.basename(r"C:\Users\sunck\Desktop\file\sunck.txt"))
    
    # 获取属性
    print(os.path.getsize(r"C:\Users\sunck\Desktop\code\test.py"))
    print(os.path.getctime(r"C:\Users\sunck\Desktop\code\test.py"))
    
    # 判断是否是目录
    print(os.path.isdir(r"C:\Users\sunck\Desktop\code"))
    print(os.path.isdir(r"C:\Users\sunck\Desktop\file"))
    print(os.path.isdir(r"C:\Users\sunck\Desktop\code\test.py"))
    
    # 判断是否是文件
    print(os.path.isfile(r"C:\Users\sunck\Desktop\code\test.py"))
    print(os.path.isfile(r"C:\Users\sunck\Desktop\code\1.txt"))
    print(os.path.isfile(r"C:\Users\sunck\Desktop\code"))
    
    # 判断目录和文件是否存在
    print(os.path.exists(r"C:\Users\sunck\Desktop\code\test.py"))
    print(os.path.exists(r"C:\Users\sunck\Desktop\code\2.py"))
    print(os.path.exists(r"C:\Users\sunck\Desktop\code"))
    print(os.path.exists(r"C:\Users\sunck\Desktop\file"))
    
    # 判断是否是绝对路径(不论是否存在)
    print(os.path.isabs(r"C:\Users\sunck\Desktop\code"))
    print(os.path.isabs(r"C:\Users\sunck\Desktop\file"))
    print(os.path.isabs(r".\code"))
    

    python删除一个非空文件夹

    import shutil
    shutil.rmtree('c:\\test')
    

    六、目录遍历

    1、递归遍历目录

    # 返回所有文件的绝对路径
    def traverseDir(dirPath):
        absPathList = []
        import os
    
        filesList = os.listdir(dirPath)
        for fileName in filesList:
            absPath = os.path.join(dirPath, fileName)
            if os.path.isdir(absPath):
                # 目录
                absPathList += traverseDir(absPath)
            else:
                # 文件
                # print(absPath)
                absPathList.append(absPath)
        return absPathList
    
    absPathList = traverseDir(r"C:\Users\lucky\Desktop\file")
    print(absPathList)
    print(len(absPathList))
    # for absPath in absPathList:
    #     print(absPath)
    

    2、递归遍历 统计大小

    import os
    # 递归 输出 所有的文件名
    path = r'C:\Users\xlg\Desktop\python安装文件'
    def myfile(path):
        sum = 0 # 文件大小初始化 为 0
        myfilelist = os.listdir(path) #返回当前文件夹下的所有的文件 和 目录
        for i in myfilelist: #遍历
            newpath = os.path.join(path,i) #将文件和路径拼凑在一起 形成一个新的完整的文件路径
            if os.path.isdir(newpath): #判断是否是 目录
                sum += myfile(newpath)  #如果是目录 就进去 继续统计
            if os.path.isfile(newpath):  #如果是文件 直接统计大小
                sum += os.path.getsize(newpath) #累加文件的大小
        return sum
    print(myfile(path))
    

    3、检索指定路径下后缀是 py 的所有文件

    获取文件后缀

    def getfile_fix(filename):
         return filename[filename.rfind('.')+1:]
    print(getfile_fix('lucky.txt'))
    
    
    方法二
    filename[-3:].upper()=='.PY'
    方法三
    #myList = x.split('.')
    #print(myList[len(myList)-1])
    

    完整示例

    import os
    import os.path
    
    #path = 'D:/UC/'
    ls = []
    
    def getAppointFile(path,ls):
        fileList = os.listdir(path)
        try:
            for tmp in fileList:
                pathTmp = os.path.join(path,tmp)
                if os.path.isdir(pathTmp):
                    getAppointFile(pathTmp,ls)
                elif pathTmp[pathTmp.rfind('.')+1:].upper()=='PY':
                    #相等filename[-3:].upper()=='.PY':   #不是目录,则比较后缀名
                    #myList = x.split('.')
                    #print(myList[len(myList)-1])
                    ls.append(pathTmp)
        except PermissionError:
            pass
    
    def main():
    
        while True:
            path = input('请输入路径:').strip()
            if os.path.isdir(path) == True:
                break
    
        getAppointFile(path,ls)
        #print(len(ls))
        print(ls)
        print(len(ls))
    
    main()
    

    进阶操作

    import os
    import time
    
    def getSuffix(path,suffixList,searchSuffix):
        fileList = os.listdir(path)
        for file in fileList:
            #拼接新的路径
            newPath = os.path.join(path,file)
            #判断是否是目录 是的话递归
            if os.path.isdir(newPath):
                getSuffix(newPath,suffixList,searchSuffix)
                #判断是否是py后缀的文件
            # elif newPath[newPath.rfind('.')+1:].upper() == "PY":
            elif newPath[newPath.rfind('.')+1:].upper() == searchSuffix.upper():
                suffixList.append(file)
    def suffixShow(path,searchSuffix='py'):
        myList = []
        #path 当前查找数据的目录
        #myList 存储后缀数据的名字
        getSuffix(path,myList,searchSuffix)
        # print(myList)
        #判断是否有数据
        length = len(myList)
        #有数据进行显示
        if length:
            print("你所查找py后缀文件在{}目录下的个数为{} 这些文件都为...".format(path,length))
            time.sleep(2)
            for file in myList:
                print(file)
        else:
            print("在当前目录下 没有找到你要寻找后缀的文件")
    
    while True:
        searchDir = input("请输入你要查找的目录")
        searchSuffix = input("请输入你要查找的后缀 不输入默认为py")
        #判断目录是否存在
        if os.path.exists(searchDir):
            if searchSuffix:
                suffixShow(searchDir,searchSuffix)
            else:
                suffixShow(searchDir)
        else:
            print(searchDir,"不存在 请重新输入")
            time.sleep(2)
    

    4、递归删除 文件

    path = './a'
    import os
    
    def d(path):
        List = os.listdir(path)
        for i in List:
            newPath = os.path.join(path,i)
            if os.path.isdir(newPath):
                d(newPath)
            if os.path.isfile(newPath):
                os.remove(newPath)
        os.rmdir(path)
    d(path)
    

    七、栈与队列

    1、栈结构

    • 栈和队列:两种数据存储格式

    • 特点:先进后出

      列表方法使得列表可以很方便的作为一个堆栈来使用,堆栈作为特定的数据结构,最先进入的元素最后一个被释放(后进先出)。用 append() 方法可以把一个元素添加到堆栈顶。用不指定索引的 pop() 方法可以把一个元素从堆栈顶释放出来。

    • 实例

      myStack = []
      # 压栈(往栈结构中存储数据)
      myStack.append(1)
      print(myStack)
      myStack.append(2)
      print(myStack)
      myStack.append(3)
      print(myStack)
      # 出栈(从栈结构中提取数据)
      myStack.pop()
      print(myStack)
      myStack.pop()
      print(myStack)
      myStack.pop()
      print(myStack)
      
    • 堆栈实现 获取文件下所有的文件名(深度优先

      import os
      path = r'C:\Users\xlg\Desktop\python安装文件'm
      
      mylist = [] #创建 一个存储 目录的空列表
      mylist.append(path) #把路径添加进去
      while len(mylist) != 0: #判断我的目录列表是否为空
          mypath = mylist.pop() #将目录弹出来
          mylistdir = os.listdir(mypath) #获取该目录下的所有文件
          for filename in mylistdir: #把该目录下的文件 进行 遍历 逐个获取
              newpath = os.path.join(mypath,filename) #把每个文件 或者 目录 拼凑成一个完整的 路径
              if os.path.isdir(newpath): #判断是否是目录
                  # print('目录名',filename)
                  mylist.append(newpath) #是目录 就添加到 目录列表里
              else:
                  print('文件名',filename) #是文件 就输出
      

    2、队列结构

    • 特点

      先进先出

      模块:collections中deque的使用

      deque是为了向list中删除和插入的效率更高,用法与list相似,而list自带的append和pop方法(尾部插入和删除)速度慢

    • 示例

      import collections #引入 其它的数据类型
      
      que = collections.deque() #使用队列
      mystr = 'abcdefg'
      for i in mystr:
          que.append(i)
      
      print(que.popleft())
      print(que)
      
    • 队列实现获取所有的文件名(广度优先

      import collections
      import os
      path = r'C:\Users\xlg\Desktop\python安装文件'
      que = collections.deque()
      que.append(path)
      while len(que) != 0:
          mypath = que.popleft()
          mylist = os.listdir(mypath)
      
          for filename in mylist:
              newpath = os.path.join(mypath,filename)
              if os.path.isdir(newpath):
                  que.append(newpath)
              else:
                  print("文件名为",filename)
      

      3、堆栈扩展知识

    内存.png
    栈区:由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈
    
    堆区:一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表
    
    全局区(静态区):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由系统释放
    
    文字常量区:常量字符串就是放在这里的,程序结束后由系统释放
    
    程序代码区:存放函数体的二进制代码
    
    • 堆栈对比

      • 申请方式

        stack:系统自动分配

        heap:需要程序员自己申请,并指明大小

      • 申请大小的限制

        stack:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小

        heap:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大

      • 申请后系统的响应

        stack:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出

        heap:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中

      • 申请效率的比较

        stack:系统自动分配,速度较快。但程序员是无法控制的

        heap:由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便

      • 堆和栈中的存储内容

        stack:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行

        heap:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排

      • 存取效率的比较

    堆栈效率-8665703.png
      stack:相对较高
    
      heap:相对较低
    

    相关文章

      网友评论

          本文标题:第七章 文件读写(OS&IO操作)

          本文链接:https://www.haomeiwen.com/subject/dhililtx.html