什么是堆内存:
是进程中的一个内存段(text\data\bss\heap\stack),由程序猿手动控制。
特点是足够大,缺点是使用麻烦
为什么要使用堆内存:
1、随着程序的复杂,数据会越来越多。
2、其他的内存段的申请和释放不受控制,堆内存的申请释放受程序猿控制
如何使用堆内存:
注意:在C语言中没有控制堆内存的语句,只能使用C标准库中的函数void *malloc(size_t size);malloc(4);
功能:从堆内存中申请size个字节的内存,申请的内存中存储的内容不确定
size: 表示要申请的字节数大小
返回值:成功时返回成功申请到的内存的首地址,失败时返回NULLvoid free(void *ptr);
功能:释放一块内存,NULL可以释放,但是不能连续释放和释放非法地址
ptr:要释放的堆内存的首地址
注意:释放的只是使用权,不会专门去清理全部的数据void *calloc(size_t nmemb,size_t size)
功能:从堆内存中申请nmemb块大小为size字节的内存,申请到的内容块会被初始化为0
注意:申请到的依然是一块连续的内存void *realloc(void *ptr,size_t size)
功能:改变已有的内存的大小,在原来内存大小的基础上调大或调小
ptr:想要改变大小的内存的首地址
size:表示调整后的大小
返回值:是调整后的内存块的首地址,一定要重新接收返回值,因为可能不是在原来内存块的基础上调整的。如果无法在原来的基础上进行调整:1、申请一块新的符合大小要求的内存块2、把原内存块中的数据拷贝到新内存块中3、把新内存块的首地址返回
malloc内存管理机制:
当首次使用malloc申请内存时,malloc会向操作系统申请内存,操作系统会直接给malloc分配33页(1页=4096字节)内存交给malloc管理。
但是不意味着你可以越界访问,因为malloc可能会把使用权分配给“其他人”,这就会导致脏数据。每个内存块之间都会有一些空隙(4~12个字节),这些空隙一些是为了内存数据对齐,其中一定会有4个字节是用于记录malloc维护信息,
这些维护信息决定了malloc下次分配内存的位置,以及借助这个维护信息计算出每个内存块的大小,当这些信息被破坏时,会影响下一次和freemalloc函数的调用
使用堆内存要注意的问题:
内存泄露:内存无法再次使用,也无法被释放,而再次使用时只能重新申请,然后重复以上操作,最后导致日积月累后系统中可以使用的内存越来越少注意:程序一旦结束后,属于该程序的所有资源都会被操作系统回收如何尽量避免内存泄漏:谁申请谁释放,谁知道该释放谁释放如何判断定位内存泄漏:1、查看内存使用情况windows下看任务管理器,Linux下使用ps -aux命令查看内存使用情况)2、使用代码分析工具来检查malloc和free的调用情况3、包装malloc和free函数,记录申请释放内存信息到日志中
内存碎片:
已经释放但也无法继续使用的内存叫做内存碎片,是由于申请和释放的时间不协调导致的,是无法避免只能尽量减少如何减少内存碎片:1、尽量使用栈内存2、不要频繁的申请和释放内存3、尽量申请大块内存自己管理s
内存清理函数:
#include<strings.h>
void bzero(void *s,size_t n);
功能:把内存块按字节设置为0
s:内存块的首地址
n: 要清理的内存块的字节数#include<string.h>
void *memset(void *s,int c,size_t n);
功能:把内存块按字节设置为字符c
s:内存块的首地址
c:xiangyao设置的字符的ASCII码值
n: 要清理的内存块的字节数
返回值:返回设置成功后内存块的首地址
堆内存定义二维数组:
指针数组:定义n*m的二维数组类型* arr[n];for(int i=0;i<n;i++){arr[i] = malloc(sizeof(类型)*m);}注意:每一行m值可以不同,所以可以定义不规则的二维数组数组指针:定义m*n的二维数组类型(*arrp)[n] = malloc(sizeof(类型)*n*m);注意:所谓的多维数组都是用一维数组来模拟的
练习1:计算出100~10000之间的所有素数,结果要存储再堆内存中,不能浪费内存
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<stdbool.h>bool is_ss(int n)
{for(int i=2;i<=sqrt(n);i++){if(n%i==0)return false;}return true;
}int main(int argc,const char* argv[])
{int* p=NULL;int cnt=0;for(int i=100;i<=10000;i++){if(is_ss(i)){p = realloc(p,(cnt+1)*sizeof(int));p[cnt++] = i;}}for(int i=0;i<cnt;i++){printf("%d ",p[i]);}free(p);p=NULL;return 0;
}
字符串
字符:在计算机中,字符是以整数形式存储再内存中,当需要显示为字符时,会根据ASCII码表中的对应关系来显示出相应的符号与图案。'\0' 0'0' 48'A' 65'a' 97字符的输入:scanf("%c",&ch);ch = getchar();字符的输出:printf("%c",ch);putchar(ch);
串:是一种数据结构,是由一组连续的若干个相同类型的数据组成,末尾有一个结束标志。对于这种数据结构的处理都是批量性的,从开头位置开始直到结束标志为止。字符串:由字符组成的串型结构,结束标志是'\0'字符串的输入:scanf %s 地址注意: 不能接受空格char *gets(char *s);功能:输入字符串,并可以接收空格返回值:链式调用(把一个函数的返回值当作另一个函数的参数)char *fgets(char *s,int size,FILE *stream);功能:可以设置输入的字符串的长度为size-1,超出部分不接受,会为'\0'预留位置。注意:如果输入的字符数不足size-1个,,最后的'\n'会一起接受char str[20]={};fgets(str,20,stdin);字符串的输出:printf %s int puts(const char *s);功能:输出一个字符串,会在末尾自动添加一个\n返回成功输出的字符个数
字符串的存在形式:
字符数组: char str[10] = 由char类型组成的数组,要为'\0'预留位置使用的是栈内存,所以数据可以修改
字符串字面值:“Hello World!”"由双引号包含的若干个字符",会在末尾隐藏一个\0字符串字面值是以地址形式存在的,数据是存储再代码段,如果修改则会产生段错误const char* p = “字符串字面值”;sizeof("strstr");结果 = 字符个数+两个一摸一样的字符串字面值在代码段中只存储一份常用方式: 字符数组[]="字符串字面值";会自动为'\0'预留位置注意:在赋值完成后字符串就存在了两份,一份存储在代码段,一份存储在栈内存(可修改)
练习2:实现一个函数,判断字符串是否是回文数
#include<stdio.h>
#include<string.h>
#include<stdbool.h>bool is_hw(const char* n)
{int i,j;size_t len = strlen(n)-1;for(i=0;i<len/2;i++){if(n[i] != n[len-i])return false;}return true;
}int main(int argc,const char* argv[])
{char num[255]={};gets(num);if(is_hw(num))printf("yes");else printf("no");
}
练习3:实现一个函数,把由数字字符组成的字符串转换为整数
#include<stdio.h>
#include<string.h>
void to_num(char* n,int l)
{for(int i=0;i<=l;i++){n[i] -= '0';}
}int main(int argc,const char* argv[])
{char num[10]={};gets(num);int len =strlen(num)-1;to_num(num,len);for(int i=0;i<=len;i++){printf("%d ",num[i]);}
}
练习4:实现一个函数,把字符串逆序、
#include<stdio.h>
#include<string.h>void change(char* n,int l)
{int i,j;for(i=0,j=l;j>i;i++,j--){char temp=n[j];n[j]=n[i];n[i]=temp;}
}int main(int argc,const char* argv[])
{char str[100]={};gets(str);int len=strlen(str)-1;change(str,len);puts(str);
}
练习5:实现一个抽奖函数,10人名单,按随即顺序列出人名
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>int main(int argc,const char* argv[])
{srand(time(NULL));char* name[10]={"张三","李四","王五","赵六","刘七","周八","srh","baba","erzi","yeye"};for(int i=0;i<10;){int index = rand()%10;if(NULL != name[index]){printf("第%d名:%s\n",i+1,name[index]);name[index]=NULL;i++;}}}