什么是堆內存:
是進程中的一個內存段(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++;}}}