C笔记19

堆内存管理&字符串

2019.5.25

堆内存管理

什么是堆内存

程序在内存是分段:
  • 代码段+只读段:二进制指令,常量
  • 全局段:初始化过和的全局变量,静态变量
  • 静态数据段:没有初始化过和的迁居变量,静态变量
  • 栈:局部变量,块变量(采用栈的方式进行管理)
  • 堆:数据无序的存储到这一块内存中,受物理内存的限制

为什么使用堆内存

    1. 栈内存的大小有限
    2. 栈内存中的数据释放不受程序员控制(函数结束后,属于它的内存就会被系统自动释放,它不适合长期存放数据)

如何使用堆内存

  1. 堆内存无法与标识符建立对应关系(必须使用指针来指向堆内存)
  2. C语言中没有管理堆内存的语句,但标准库提供一套函数来管理堆内存
  3. 堆内存的管理和释放由程序员手动操作(显示调用函数来管理)
void *malloc(size_t size);
  • 功能:从栈内存申请size个字节
  • size:要申请的字节数
  • 返回值:所申请到的内存首字节地址需要使用指针来接收
    注意:使用malloc所申请的堆内存的内容默认情况下不确定(但首次使用时是0),如果size的值为0,返回值是NULL

练习1:计算100-1000所有的素数,存储在堆内存中

void bzero(void *s,size_t n);
  • 功能:所一块内存的所有字节,设置为0(清理内存)
  • s:要清理的内存首地址
  • n:清理的字节数

void* 是一种无类型的指针

  1. 不能直接使用,不能通过这种指针变量解引用(必须转换为其他类型)
  2. 可以与任意类型的指针进行自动转换
    void* = 任意类型
    任意类型 = void*
  3. void* 也叫万能指针,解决函数之间传递指针参数时类型不确定问题
void *menset(void *s, int c, size_t n);
  • 功能:把内存的每一个字节都设置为c
  • s:内存首地址
  • c:字节中要设置的数据,-128~127
  • n:要设置的字节数
  • 返回值:初始化的内存首地址,也就是s的值(链式调用)
void free(void *ptr);
  • 功能:释放内存,只是把内存的使用权收回,内容还在。
  • ptr:malloc的返回值,之前所申请的内存的首地址。
    注意:
    内存释放后,还能继续访问(不产生段,可能会造成脏数据),但是在非法访问。
    内存在释放后,指针要立即置NULL,否则指针就会变成野指针。
    一块内存不能释放两次,否则会出现堆崩溃。
void *calloc(size_t nmemb, size_t size);
  • 功能:从堆内存申请nmemb*size个字节(内存已初始化为0)
  • size:每次申请的字节数
  • nmemb:申请的次数
  • 返回值:所申请的内存的首地址(size或nmemb只要有一个0,返回值为NULL)
    注意:calloc的速度会比malloc慢,因此绝大多数情况下只使用malloc
void *realloc(void *ptr, size_t size);
  • 功能:调整已申请到的内存的大小。
  • ptr:内存的首地址,malloc或calloc的返回值,如果ptr为NULL而size大于0,相当于申请内存。
  • size:把内存调整为size个字节,可以调大,也可以调小,如果ptr的值合法,而size的值为0,则相当于释放内存。
  • 返回值:调整后的内存的首地址,一定要** 重新接收 ** 。

练习2:修改练习1,不浪费内存。

使用内存时要注意的问题

  • 当程序结束后,所有属于它的所有资源都会被操作系统回收
内存泄漏:
  • 由于失误而忘记或无法释放堆内存,导致堆内存无法循环利用,从而每次重新申请内存使可用内存越来越少。
  1. 保护指针不被修改

    类型* const p = malloc(size);
    
  2. mallocfree成对出现

     谁申请谁释放
    
定位内存泄漏
  1. 当程序运行后观察内存的使用情况,内存暴涨检查循环中的申请内存
    cat /proc/meminfo linux下查看内存使用情况
  2. 检查每个malloc的free(在free前查是否已经变成空指针)
  3. 检查条件,业务逻辑free会被对用到
内存碎片:
  • 已经释放但不能被再次分配使用的内存
    • 频繁的申请,释放内存,导致申请和释放的不协调,一部分内存无法再次被使用。
    • 内存碎片无法杜绝,只能尽量减少
如何减少内存碎片
  1. 尽量减少使用堆内存,栈内存可以解决的尽量使用栈内存
  2. 尽量申请大块的内存
  3. 不要频繁的申请和释放

字符:

  • 字符就是符号,图案,在计算机中以整数形式存储,当需要显示的时候根据ASCII表中的对应关系,显示相应的符号和图案
CHARASCII
'0'48
'a'97
'A'65
'\0'0

字符输入:

scanf("%c",&ch);
ch = getchar();

字符输出:

printf("%c",ch);
putchar(ch);

注意:先输入整数,浮点代数据,会影响后续字符数据的输入(把回车当成字符获取)

串:

  • 是一种数据结构,由若干个相同类型的元素组成,有一个明确的结束标志。

字符串

  • 由字符组成的串行结构,它的结束标志是’\0’
    注意: 所有对字符串的操作都以’\0’作为结束标志
字符串的存在形式

字符数组:char arr[] = {'a','b','c','d'};
注意:要为'\0'预留位置
字符串字面值:"双引号包括的若干个字符,末尾隐藏着\0" 以地址形式存在
注意:存储在只读段,不能被修改否则会产生段错误

  • 一般常用字符串字符值来初始化字符数组,编译器自动把字符串字面值拷贝到字符数组中,包括'\0'之后这个字符串就有了两份,一份在栈中,一份在只读段。
字符串的输出

printf %s 指针
puts(指针) 会自动在末尾增加一个'\n'

字符串的输入 '\0'会自动添加

scanf %s 指针//不接收空格
gets(指针);//可以接收空格
fgets(指针,size,stdin);//只能接收到size-1个字符,为'\0'预留
当输入的字符不足size-1个是,会连\n一起接收
stdin->_IO_read_end = stdin->_IO_read_ptr//清缓冲区

练习1:实现一个计算字符串长度的函数,不包括’\0’
练习2:实现一个字符串输入函数,可以指定字符的数量,但不接收到’\n’

字符串的操作

strlen 计算字符串长度
strcpy(str,"haha") 拷贝字符串,相当于赋值(只有初始化才能用=赋值)
strcat(str,"haha") 连接两个字符串
strcmp() 按字典序比较字符串大小

conditionreturn
str1 == str20
str1 > str21
str1 < str2-1

atoi/atol/atoll("123a") 字符串转整数
strstr 在str1中查找str2,返回str2首次出现的位置,没有NULL

作业1:实现itoa函数
作业2:实现一个函数,用于判断字符串是不是回文串
作业3:实现一个字符串逆序函数
作业4:把字符串中的单词首字母大写,空格替换为“% %”
作业5:把一段文字的单词逆序
作业6:计算字符串2在字符串1中出现的次数
作业7:实现两个大数相加

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值