请求分页内存管理的模拟 c++代码_C开发实战-内存管理
作用域和變量
作用域就是作用的范圍,當定義變量時不同作用域的變量位于不同的內存空間。
變量按照不同的作用域可以分為局部變量和全局變量。而局部變量和全局變量都可以使用static修飾,static修飾的局部變量叫靜態局部變量,static修飾的全局變量叫靜態全局變量。當定義一個變量時,除了要考慮初始化賦值以外,還要注意變量的作用域和生命周期。
局部變量
局部變量定義在{}內,其作用域在{}內部有效,出了{}變量就無法使用。
局部變量如果沒有初始化賦值,系統會給局部變量隨機賦垃圾數值。
生命周期表示變量從開辟內存空間(生)、釋放內存空間(死)的過程。
通常局部變量的生命周期在定義時開辟內存空間,在所在的函數執行結束后釋放內存空間
#define _CRT_SECURE_NO_WARNINGS#include #include /*在自定義方法中定義和使用局部變量*/void print_local_variable_value() {printf("execute print_local_vairbale_value() method ");//局部變量 ,作用域在定義時{}之內int age = 29;//靜態局部變量static double dbl = 3.14;printf("局部變量 age =%d", age);printf("靜態局部變量 dbl =%lf", dbl);//定義局部變量時開辟內存空間,函數結束之后釋放內存空間int number = 10;int* p = NULL;if (number) {//局部變量在函數結束之后釋放空間int data = 20;p = &data;printf("number = %d ", number);}//超過了data的作用域,無法訪問data變量//data = 100;//通過指針獲取局部變量的值printf("*p=%d ", *p);//通過指針修改局部變量的值*p = 30;printf("*p=%d ", *p);}/*局部變量定義和使用注意事項@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){printf("execute main method ");//無法在main()函數中使用print_local_variable_value()方法定義的局部變量//printf("局部變量 age =%d",age);//printf("靜態局部變量 dbl =%lf",dbl);print_local_variable_value();system("pause");return 0;}程序運行結果
在使用局部變量時不應該將局部變量的地址用作函數的返回值,否則會引發程序異常。
#define _CRT_SECURE_NO_WARNINGS#include #include #include /*自定義方法返回定義的局部變量地址*/int* return_local_variable_address() {int x = 100;Sleep(5000);//返回局部變量x的地址return &x;}/*局部變量的返回值問題@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){int* p = return_local_variable_address();printf("return_local_variable_address()方法的返回值是%d", *p);*p = 800; // p所指向的空間已經被釋放,不能操作這塊內存printf("*p = %d", *p);system("pause");return 0;}形參的本質就是局部變量,基本類型作為形參時不能返回形參的地址,而指針類型的形參可以作為返回值
#define _CRT_SECURE_NO_WARNINGS#include #include /*形參的本質就是局部變量*/int* change_value(int data) {data += 100;return &data; //不可以返回形參的地址}int* change_pointer(int *p) {int i = 10;*p = *p + i;return p; //返回地址 }/*返回指針的地址@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){int value = 200; change_value(value);int* p=change_pointer(&value);system("pause");return 0;}靜態局部變量
靜態局部變量是使用static修飾的局部變量,如果在定義靜態局部變量時沒有賦值,如果變量類型是整數類型默認初始化值為0,其作用域和局部變量一樣,也是在其定義的{}之內有效。
靜態局部變量在main函數運行之前就已經開辟空間,而且靜態全局變量只會有唯一一份內存空間,在程序結束之后釋放內存空間。
當在main函數中多次調用print_static_local_variable_value()方法修改靜態全局變量時不會再為靜態局部變量number開辟內存空間,即static int number=1;在整個程序的生命周期只會執行一次。
程序運行結果
由于靜態據變量的聲明周期是在程序退出時才會釋放內存空間,因此靜態局部變量可以作為函數的返回值。
#define _CRT_SECURE_NO_WARNINGS#include #include /*自定義方法返回定義的局部變量地址*/int* return_static_local_variable_address() {static int x = 100;printf("x = %d",x);//返回靜態局部變量x的地址return &x;}/* 靜態局部變量作為函數的返回值@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){int* p = return_static_local_variable_address();printf("return_static_local_variable_address()方法的返回值是%d", *p);//修改變量x的值*p = 800;printf("*p = %d", *p);return_static_local_variable_address();system("pause");return 0;}程序運行結果
全局變量
全局變量即定義在函數外部的變量,如果全局變量類型是int,聲明全局變量時默認初始化值為0。全局變量的生命周期是在main函數執行之前開辟空間,在程序結束后釋放內存空間。
由于全局變量在程序結束運行時才會釋放內存空間,因此在日常開發中盡量少用全局變量。
#define _CRT_SECURE_NO_WARNINGS#include #include //全局變量定義在函數外部,作用域在整個項目//執行main函數之前開辟內存空間,程序結束釋放內存空間int x;void changex() {printf("execute changex()method");printf("初始化全局變量x = %d ",x);//在自定義函數修改全局變量的值x = 20;printf("修改全局變量x之后x = %d ", x);}/*全局變量的定義和使用@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){printf("inside method()");//在不同的函數之間訪問全局變量xchangex();printf("main method x = %d ",x);system("pause");return 0;}全局變量的作用域在整個項目內都可以訪問,例如在global_variable.c中定義了全局變量int x;在同一個項目的另外一個文件global_variable_acces.c中訪問。不過在使用之前需要使用extern關鍵字顯示聲明 extern int x;
#define _CRT_SECURE_NO_WARNINGS#include #include /*不同文件的全局變量的訪問@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*///顯示聲明整型變量x,即告知編譯器x在其他地方定義過,此處通過編譯extern int x;int main(int argc, char* argv[]){//修改全局變量x的值為100x = 100;printf("全局變量x = %d",x);system("pause");return 0;}程序運行結果
靜態全局變量
靜態全局變量就是使用static修飾的全局變量,靜態全局變量的作用域在當前文件有效,而不能像全局變量一樣跨文件使用。也不能使用extern關鍵字顯示聲明。
靜態全局變量的聲明周期也是在執行main()函數之前開辟內存空間,在程序結束時釋放內存空間。靜態局部變量聲明時如果是int類型,那么初始化默認值也是0。如果是double類型初始化默認值為0.0;
程序運行結果
靜態成員變量的注意事項
#define _CRT_SECURE_NO_WARNINGS#include #include // 不能使用extern顯示聲明靜態全局變量//extern double dbl;/*靜態全局變量的使用注意事項@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){// 不能跨文件使用靜態全局變量//dbl = 3.14;//printf("靜態全局變量dbl = %.2lf", dbl);system("pause");return 0;}作用域和變量最佳實踐
在多文件編程時,通常將變量、函數的定義放在源文件中(例如這里的global_variable.c),將變量、函數的聲明放在和源文件同名的頭文件(例如這里的global_variable_access.h)中。
#pragma once#pragma once//顯示聲明整型變量x,即告知編譯器x在其他地方定義過,此處通過編譯extern int x;//顯示聲明函數extern void changex();然后在源文件global_variable_access.c中包含頭文件global_variable_access.h,就可以使用聲明的全局變量和函數
##define _CRT_SECURE_NO_WARNINGS#include #include #include "global_variable_access.h"/*不同文件的全局變量的訪問@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){//修改全局變量x的值為100x = 100;printf("全局變量x = %d",x);changex();system("pause");return 0;}程序運行結果
不同作用域的變量名相同是可以共存的,但是需要注意的是相同作用域的變量名重名是不行滴。
在源文件diff_scope_static_global_variable.c中定義靜態全局變量并在方法中打印初始值
#include static int number = 10;void print_static_global_variable_number() {printf("static global variable number = %d",number);}在頭文件diff_scope_variable_duplication.h中顯示聲明print_static_global_variable_number()函數。
#pragma onceextern void print_static_global_variable_number();在源文件diff_scope_variale_duplication.c中定義和使用全局變量,局部變量
#define _CRT_SECURE_NO_WARNINGS#include #include #include "diff_scope_variable_duplication.h"//定義全局變量 初始化賦值為10int data = 10;int get_value() {int data = 100;{int data = 200;}//此時打印輸出data =10 printf("get_value() method local variable data= %d",data);return data;} /*不同作用域的變量重名@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){print_static_global_variable_number();int value = get_value();printf("value = %d",value);printf("global variable data= %d",data);system("pause");return 0;}程序運行結果
局部變量的地址不能作為方法的返回值,因為局部變量的內存空間在所在的函數結束后就會被釋放,而靜態局部變量,全局變量和靜態全局變量的地址可以作為函數的返回值。因為程序結束之前靜態局部變量,全局變量,和靜態全局變量在程序結束之前內存空間不會被釋放。
靜態函數
默認定義的函數就是全局函數,其作用域可以在整個工程中使用,如果是跨文件使用,在頭文件中使用extern顯示聲明即可,沒有局部函數。
而使用static修飾的函數是靜態函數,其作用域是只能在當前源文件中使用。
當前文件中定義和使用靜態函數是ok的
#define _CRT_SECURE_NO_WARNINGS#include #include /*定義靜態函數*/static double get_dbl() {double dbl = 3.14;return dbl;}/*靜態函數的定義和使用@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){double dbl = get_dbl();printf("dbl = %.2lf",dbl);system("pause");return 0;}但是跨文件使用時,系統會提示無法解析的外部符號_get_dbl
內存的結構
當程序編譯、鏈接完成時,編譯器已經規劃好了內存空間,拿出當年寫的helloworld程序
#include int main(){ printf("Hello c with Windows10 1903 & Visual Studio Code & MinGW7.3.0 "); return 0;}在Windows上使用size helloworld.exe查看內存,單位大小以字節(Byte)為單位
- text表示代碼區
- data表示數據區,用于存放全局變量,
- bss表示未初始化的數據區
- dec表示 text+data+bss的大小
當在helloworld源程序中定義一個全局變量時,編譯器會重新規劃數據區的大小
#include int number=100; //定義全局變量,初始化賦值為100int main(){ printf("Hello c with Windows10 1903 & Visual Studio Code & MinGW7.3.0 "); return 0;}data
當在helloworld源程序匯中聲明一個全局變量時,未初始化數據區也會發生變化。
#include int number=100; //定義全局變量,初始化賦值為100int age;int main(){ printf("Hello c with Windows10 1903 & Visual Studio Code & MinGW7.3.0 "); return 0;}bss
而局部變量時在程序運行時分配空間,內存由棧區、堆區、靜態全局區、代碼區組成。
內存結構
定義不同類型的變量
- 未初始化的全局變量a,未初始化的靜態全局變量b,g位于未初始化的靜態全局區
- 初始化的全局變量c,初始化的靜態全局變量d,h位于已初始化的靜態全局區
- 未初始化的局部變量e,i和初始化的局部變量f,j位于棧區
獲取變量的地址時,相同類型的存儲區域的地址編號是相近的
程序運行結果
內存處理函數
內存清零函數memset()
memeset()函數用于將指定的內存清零
#define _CRT_SECURE_NO_WARNINGS#include #include #include /*memset()函數的使用@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){int number = 10;//等價于 number = 0;memset(&number, 0, sizeof(number));printf("number = %d ",number);char buf[10]="";//清空bufmemset(buf, 0, sizeof(buf));for (int i = 0; i < sizeof(buf) / sizeof(buf[0]);i++) {printf("buf[%d]=%s ",i,buf[i]);}printf("將buf數組的前九個元素置為a");//將buf數組的前九個元素置為amemset(buf,'a',sizeof(buf)-sizeof(buf[0]));for (int i = 0; i < (sizeof(buf) / sizeof(buf[0]))-1; i++) {printf("buf[%d]=%c ", i, buf[i]);}system("pause");return 0;}程序運行結果
內存拷貝memcpy()函數
memcpy()函數實現整數數組指定大小的元素拷貝
#define _CRT_SECURE_NO_WARNINGS#include #include #include /*memcpy()函數拷貝整數數組@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){int source[10] = {1,2,3,4,5,6,7,8,9,10};int target[10] = {0};//將source數組的元素拷貝10個到targetmemcpy(target, source, sizeof(source));printf("遍歷拷貝之前source數組的元素");for (int i = 0; i < sizeof(source) / sizeof(source[0]); i++) {printf("target[%d] = %d ", i, source[i]);}printf("遍歷拷貝之后target數組的元素");for (int i = 0;i程序運行結果
memcpy()函數實現指定字符串的拷貝,相比于strncpy()函數,memcpy()函數在拷貝字符串時遇到了'0'也不會停止拷貝
#define _CRT_SECURE_NO_WARNINGS#include #include #include /*memcpy()函數拷貝字符數組memcpy()函數對比strncpy()函數@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){char str1[128] = "";char str2[128] = "hello0wo0rld";printf("使用memcpy()函數將str2數組的10個元素拷貝到str1數組中");memcpy(str1, str2, 10 * sizeof(char));printf("遍歷拷貝之后的str1數組的元素列表");for (int i = 0; i < 10;i++) {printf("%d",str1[i]);}printf("");//相對于strnpy()函數,memcpy()遇到'0'不會停止拷貝printf("使用memset()函數將str1數組的元素清空");memset(str1, 0, sizeof(str1));printf("使用strncpy()函數將str2數組的10個元素拷貝到str1數組中");strncpy(str1, str2, 10);printf("遍歷拷貝之后的str1數組的元素列表");for (int i = 0; i < strlen(str1); i++) {printf("%d", str1[i]);}printf("");system("pause");return 0;}內存比較memcmp()函數
memcmp函數用于實現內存的比較,相較于strcmp()函數在遇到‘0’時不會停止比較
#define _CRT_SECURE_NO_WARNINGS#include #include #include /*內存比較memcmp()函數@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){int source[10] = {1,2,3,4,5,6,7,8,9,10};int target[10] = {1,2,3,4,5,6,7,8,9,10};int result =memcmp(source, target, 10 * sizeof(int));printf("source和target兩個整數數組比較的結果是%d",result);//清空target數組的內容memset(target, 0, sizeof(target));result = memcmp(source, target, 10 * sizeof(int));printf("清空target數組的內容后source和target兩個整數數組比較的結果是%d", result);//memcmp函數和strncmp函數的比較char str1[128] = "hello0agian";char str2[128] = "hello0seeyouagain";//strcmp函數遇到0會結束比較printf("使用strcmp函數比較str1和str2大小的結果是%d",strcmp(str1,str2));//memcmp函數遇到0不會結束比較printf("使用memcmp函數比較str1和str2大小的結果是%d",memcmp(str1,str2,128*sizeof(char)));system("pause");return 0;}程序運行結果
因此得出結論:內存操作函數遇到0或者0都不會結束處理,而字符串處理函數遇到0結束處理。
堆內存申請和釋放
當處理的數據比較大,可以向堆申請空間,C語言提供了malloc()函數來申請堆內存空間。
malloc()函數需要的參數是字節數量,返回類型為void *即萬能指針,如果申請的空間存放的數據是字符,void * 可以轉換為char *,如果申請的空間存放的是整數,void *可以轉換為int *。
如果malloc()函數申請的堆內存空間不再使用,可以使用free()函數來釋放堆內存空間,不能釋放棧空間,需要注意的是只能申請一次,釋放一次,而且是同一內存地址。如果釋放了多次會造成系統異常。
#define _CRT_SECURE_NO_WARNINGS#include #include #include /*堆內存申請和釋放malloc()函數:申請堆內存空間,返回內存首地址,返回類型為void *,參數為字節的數量@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){//申請4個字節的堆內存空間,返回一個地址 void *// 需要將void *強制類型轉換為int* int * p=(int *)malloc(sizeof(int));//操作堆內存*p = 100;printf("堆內存:*p = %d",*p);// 申請一個數組,數組有10個元素,元素的類型為intint *p_array=(int*)malloc(sizeof(int[10]));//給第六個元素賦值為200*(p_array + 5) = 200; //給最后一個元素賦值100p_array[9] = 100;for (int i = 0; i < sizeof(int[10]) / sizeof(int);i++) {printf("*p_array[%d] = %d",i,*(p_array+i));}// 申請一個字符數組 存儲1024個元素char* p_char =(char*)malloc(1024);//空間清零memset(p_char, 0, 1024);// 空間釋放,malloc申請的空間只能釋放一次free(p_char);system("pause");return 0;}程序運行結果
生產環境的都是7*24小時不間斷運行,如果程序一直在申請內存空間,而不釋放內存空間,導致程序使用的內存空間一直增長,就會造成內存泄漏。程序退出后,程序使用的所有內存才會釋放。日常開發中應該盡量避免內存泄漏,除此以外還有內存污染的問題,即沒有申請空間,就往空間中寫數據。
堆內存地址可以作為函數的返回值,因為堆內存在函數結束時不會被釋放空間,但是在操作堆內存時需要注意一些問題
#define _CRT_SECURE_NO_WARNINGS#include #include char* malloc_heap() {//申請128個字節的堆內存char* q = malloc(128);return q; //堆區的地址是可以返回,因為函數結束不會釋放堆內存}/*返回堆內存的地址@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){char* p = malloc_heap();//將hello字符串拷貝到p指向的堆內存空間strcpy(p, "hello");//釋放堆內存free(p);/*********錯誤的堆操作*********///此時p指向了文字常量區//p = "hello";//此時釋放的不是堆內存//free(p);system("pause");return 0;}通過函數的值傳遞不能改變變量的值,即實參的值不會改變形參的值,但是傳遞實參的地址可以調用函數改變實參的值。
能不能返回地址,需要看地址指向的空間有沒有被釋放。
如果要解決該問題,將q的指針返回即可。
#define _CRT_SECURE_NO_WARNINGS#include #include #include char* malloc_q(char *q ) {// q指向堆內存q = malloc(1024);return q;}/*實參作為一級指針地址@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){//實參是一級指針char* p = NULL;p=malloc_q(p);strcpy(p,"helloworld");printf("p = %s",p);system("pause");return 0;}程序運行結果
另外一種解決方法
#define _CRT_SECURE_NO_WARNINGS#include #include #include char* malloc_q(char **q ) {// q指向堆內存*q = malloc(1024);return q;}/*實參作為一級指針地址@author liuguanglei 18601767221@163.com@wechat 18601767221@website ittimeline.net@version 2020/11/27*/int main(int argc, char* argv[]){//實參是一級指針char* p = NULL;malloc_q(&p);//bug 因為p指向NULL,無法實現字符串復制strcpy(p,"helloworld");printf("p = %s ",p);system("pause");return 0;}程序運行結果
總結
以上是生活随笔為你收集整理的请求分页内存管理的模拟 c++代码_C开发实战-内存管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python建立回归模型_简单线性回归的
- 下一篇: html表格数据循环展示,MVC在Vie