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

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命令
    运行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操作))