<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      內存模型

      內存模型

      以C語言編譯器的常見實現為例

      內存四區:堆、棧、全局區、代碼區

      • 內存四區

        • 全局區
          • 全局區
          • 常量區
        • 代碼區
        • 示意圖(待補充)

      1、堆

      由程序員使用動態分配函數分配內存,需要頭文件stdlib.h。

      stdlib.h中的函數主要有

      函數名 函數原型 功能 返回值
      calloc void *calloc (unsigned n,unsigned size); 分配n個數據項的內存空間,每個數據項的大小為size個字節 分配內存單元首地址;不成功則返回0
      free void free (void *p); 釋放p所指的內存區
      malloc void *malloc (unsigned size); 分配size個字節的存儲空間 分配內存空間的地址;如不成功返回0
      realloc void *realloc (void *p,unsigned size); 把p所指的內存區的大小改為size個字節 新分配內存空間的地址;如不成功返回0
      rand int rand (void); 產生0~32767的隨機數 返回一個隨機數
      exit void exit (0); 文件打開失敗返回運行環境

      以上表格摘自書本,因此還有一些未去驗證的疑問:

      1. 關于返回值,表格中的void*型函數,不成功時應當是NULL,0和NULL在計算機中或許等價(尚未想到驗證方法);
      2. 關于分配內存,若malloc分配的內存不是該數據類型的大小的整數倍,是否報錯,或引起其他錯誤(未想到全面驗證的方法);
      3. realloc函數,在改寫p所指的內存區的大小后,返回新分配內存空間的地址,那么原內存空間是被覆蓋還是被釋放,是否會產生內存丟失(因為懶,暫未驗證)。

      堆區使用實例:

      #include<stdio.h>
      #include<stdlib.h>
      
      int main()
      {
      //使用malloc函數,在堆區分配20個字節 
      //即分配5個int型變量大小的內存
      	//int *p = (int*)malloc(20); 
      	
      //關于內存分配
      //避免出現上述提問2提及的可能出現的錯誤,建議按下列書寫格式
      	int *p = (int*)malloc(5*sizeof(int));
      	
      	//……對*p使用完后
      	
      	free(p);  //將堆釋放掉
      	p = NULL;  //清零p的指向,避免誤判
      
      	return 0;
      }
      	
      

      2、棧

      存放程序的局部變量,先入后出,由編譯器自動分配內存,出棧的順序基本就是程序執行的順序

      棧的實例:

      #include<stdio.h>
      
      //假設棧開口向下
      //則此時相當于在棧上分配了一個存放main函數的內存空間
      int main()
      {
      //這兩個變量,放在了棧區
      	int num1,num2;
      	
      	printf("請輸入數字1:");
      	scanf("%d",&num1);
      	
      	printf("請輸入數字2:");
      	scanf("%d",&num2);
      	
      	//以下面這條語句為例
      	printf("它們的和為:%d",sum(num1,num2));
      	//printf函數的返回值地址先入棧(反正是函數的某地址啦)
      	//然后printf函數的參數
      	//  "它們的和為:%d" 和 sum(num1,num2) 入棧
      
      	//再sum函數的返回值地址入棧
      	//隨后,sum的參數 numOne 和 numTwo 入棧
      	
      	//(具體到哪個參數先的話。。。應該是從右到左
      	//參考連接  http://www.rzrgm.cn/xkfz007/archive/2012/03/27/2420158.html
      	
      	//隨著函數依次運行
      	//numOne 、numTwo 出棧
      	//sum函數在棧上的內存空間析構
      	//sum返回值地址出棧
      	
      
      	//printf函數的參數出棧
      	//printf函數在棧上的內存空間析構
      	//printf函數返回值地址出棧
      	
      	
      	return 0;
      }
      
      int sum(int numOne,int numTwo)
      {
      	return (numOne+numTwo);
      }
      	
      

      3、全局區

      這里的全局區實際是將“全局區”和"常量區“統稱

      若分開來看的話
      全局區:存放全局變量、靜態變量
      常量區:存放常量、字符串
      (PS:字面量,比如 int b = 123;在這句語句中,123就是字面量,在b入棧之前就存在,此時字面量應該在常量區)

      全局區實例:

      #include<stdio.h>
      
      int main()
      {
      //變量len在棧上,123在常量區
      	int len = 123;  //然后在執行完該語句后,123放入len
      //同理,變量*str在棧上,"I an Chinese"在常量區
      	char *str = "I an Chinese"; //str存放"I an Chinese"的地址
      
      	return 0;
      }
      

      4、代碼區:存放代碼

      目前還沒接觸過需要操作代碼區的地方,不清楚有什么需要了解的特性。

      內存四區的示意圖先咕著,待補充

      數據類型

      在C語言中,數據類型,可以說是不同內存大小的別名,我所知,其數據結構所定義的算法只有四則運算。
      (PS:在數據結構的內容中,有這么一個說法,數據類型是已經實現的數據結構)

      類型名 字節 數值范圍 范圍說明
      char 1 8 -128~127 -27 ~ (27-1)
      unsigned char 1 8 0~255 0 ~ (28-1)
      short 2 16 -32768 ~ 32767 -215 ~ (215-1)
      unsigned short 2 16 0 ~ 65535 0 ~ (216-1)
      int 4 32 -2147483648 ~ 2147483647 -231 ~ (231-1)
      unsigned int 4 32 0 ~ 4294967295 0 ~ (232-1)
      float 4 32 -3.4x1038 ~ 3.4x1038 7位有效數字
      double 8 64 -1.7x10308 ~ 1.7x10308 15位有效數字
      long long 8 64 未計算 -263 ~ (263-1)
      unsigned long long 8 64 未計算 0 ~ (264-1)
      long double 12 96 未計算 不清楚
      1. 以上,均可通過編譯器驗證;
      2. short是short int的縮寫,同理long是long long int的縮寫;
      3. 這些基本數據類型,其差別是內存大?。╢loat、double除外);
      4. float和double其數據存儲形式與其它類型有差別(示意圖待補充)。

      指針:存放地址的數據類型

      存放地址,通過類型,指定指針的步長

      • 一級指針
        • 步長
      • 二級及多重指針
        • 指針數組
        • 指向二維數組的指針(”行式“指針)
      • const型指針
      • 指針與函數
        • 指針作形參
        • 指針作返回值
        • 指向函數的指針
      • 注意事項

      一級指針

      一級指針很好理解,就是在定義時多一個星號

      //如下:
      int *num;  //整型指針  指針本身在棧上占4字節內存  步長4字節
      char *str; //字符型指針  指針本身在棧上占4字節內存  步長1字節
      double *lf; //浮點型指針  指針本身在棧上占4字節內存  步長8字節
      
      //計算指針所占內存的大小
      printf("int型指針的所占內存的大?。?d\n",sizeof(num)); 
      printf("char型指針的所占內存的大小:%d\n",sizeof(str)); 
      printf("double型指針的所占內存的大?。?d\n",sizeof(lf));
      
      //計算步長
      printf("int型指針的步長:%d\n",sizeof(*num));
      printf("char型指針的步長:%d\n",sizeof(*str)); 
      printf("double型指針的步長:%d\n",sizeof(*lf));
      
      步長

      步長是指針的重要概念,與指針的加減運算相關
      (PS:指針的加減運算,實質的指針指向的偏移,故沒有乘除運算)【YY:除非某天出現向量指針甚至張量指針(啊,真是讓人頭禿的假想)】

      //理解步長
      int *num;
      char *str;
      double *lf;
      
      //以下僅為假設示例
      //除非清楚地址(內存標號)所指向的內存內容,不然請勿模仿
      //初始化  指向同一個地址
      num = 0xaaaaa;
      str = 0xaaaaa;
      lf = 0xaaaaa;
      
      //執行+1操作
      num++;
      str++;
      lf++;
      
      //用十六進制顯示
      printf("num存放的地址值:%x\n",num);
      printf("str存放的地址值:%x\n",str);
      printf("lf存放的地址值:%x\n",lf);
      
      輸出:
      num存放的地址值:0xaaaae   //比原來多4字節
      str存放的地址值:0xaaaab   //比原來多1字節
      lf存放的地址值:0xaaab4   //比原來多8字節
      

      二級及多重指針

      1. 從指針來說,無論是幾級指針,都是存放地址
      2. 因為指針的星號操作,所以n級指針,存放(n-1)級指針的地址
      3. 還有步長的區別,我所知,這一點只在指向多維數組的指針中體現
      //理解多級指針
      char ***str_T;
      char **str_O;
      char *str = "I am Chinese"; //指向一個字符串
      
      
      str_O = &str; //指向str
      str_T = &str_O;//指向str_O
      
      str_O = str_O+1;//偏移str_O的指向(一般,此操作無意義)
      *str_O = *str_O+1; //使str存儲的地址值加一個步長
      **str_O = **str_O+1;//報錯 常量區的內容無法更改
      
      printf("打印字符串str:%s\n",str);
      
      str_T = str_T+1;//偏移str_T的指向(一般,此操作無意義)
      *str_T = *str_T+1;//偏移str_O的指向(一般,此操作無意義)
      **str_T = **str_T+1; //使str存儲的地址值加一個步長
      ***str_O = ***str_O+1;//報錯 常量區的內容無法更改
      
      printf("打印字符串str:%s\n",str);
      
      輸出:
      打印字符串str: am Chinese
      打印字符串str:am Chinese
      
      第二個比第一個少輸出一個空格
      因為第一個只移一個步長,第二個共移了兩個步長
      
      指針數組

      顧名思義,以數組的形式,定義多個指針

      //指針數組
      //定義了存放地址的數組 
      int *p[5];  //有5個指針元素
      
      指向二維數組的指針(“行式”指針)

      指向多維數組的指針可以是普通的指針,也可以是“行式”指針
      此處只對”行式“指針進行說明

      //理解”行式“指針
      //定義一個3x4的二維數組
      int numlen[3][4]={1,3,5,7,
      			   9,11,13,15,
      			   17,19,21,23};
      int(*num)[4];  //定義一個”行式“指針 步長為4xsizeof(int)字節
      p = a;  //指向數組a
      
      //以打印元素的方式驗證
      printf("num指向的元素:%d\n",*(*num));
      printf("num+1指向的元素:%d\n",*(*(num+1)));
      
      //以打印地址的方式驗證
      printf("num的地址:%d\n",num); 
      printf("num+1的地址:%d\n",num+1);
      
      //注意 下列書寫依然是打印地址
      printf("仍是存放在num的地址:%d\n",*num);
      printf("仍是存放在num+1的地址:%d\n",*(num+1));
      
      //打印行內的元素
      //打印元素a[0][1]
      printf("打印num指向的行內元素:%d\n",*(*num+1));
      //打印元素a[1][1]
      printf("打印num+1指向的行內元素:%d\n",*(*(num+1)+1));
      
      輸出:
      1
      9
      
      地址根據系統變化,但二者之間,地址值相差16
      
      同上方的地址一樣,二者值同樣相差16
      
      3
      11
      
      1. 從打印行內的元素的方式,可以看出該案例中的“行式”指針是二級指針
      2. “行式”指針,可讀性相對較差,不易維護,很少使用
      3. “行式”指針,幾乎與二維數組共同出現
      4. 三維數組,可以是“頁式”指針

      const型指針

      const型指針有兩種

      //可進行遍歷的只讀指針
      //可以修改p的值,但不能用*p修改a的值
      const int *p = &a;  
      
      //不可進行遍歷的標志指針
      //不能修改p的值
      int * const p = &a;  //與數組首地址作用相同
      //存儲的地址不會變化,可作為函數形參,標識地址
      
      

      指針與函數

      指針除了內存操作外,可以說是專服務于函數

      指針作形參

      指針作形參,就是作為函數的參數,其目的,大多都是為函數提供多個返回值的
      (PS:指針忌指向臨時變量,即在指針使用的過程中,勿指向已析構或即將析構的變量 此點將在注意事項中作示例)

      函數作形參實例:

      //指針作形參
      #include<stdio.h>
      
      //函數聲明
      void swap(int *pt1,int *pt2);
      void exchange(int *p1,int *p2,int *p3);
      
      int main()
      {
      	int num1,num2,num3;
      
      //對需要輸入的數據進行必要的說明
      	printf("請輸入num1:");
      	scanf("%d",&num1);
      	printf("\n");
      	
      	printf("請輸入num2:");
      	scanf("%d",&num1);
      	printf("\n");
      	
      	printf("請輸入num3:");
      	scanf("%d",&num1);
      	printf("\n");
      	
      	//調用排序函數  將變量的地址傳遞給函數的形參指針
      	exchange(&num1,&num2,&num3);  
      	printf("從大到小排序后:%d,%d,%d\n",num1,num2,num3);
      
      	return 0;
      }
      
      void exchange(int *p1,int *p2,int *p3)
      {
      //在判斷為真后,調用換值函數  交換變量中的值
      	if(*p1 < *p2)swap(p1,p2);
      	if(*p1 < *p3)swap(p1,p3);
      	if(*p2 < *p3)swap(p2,p3);
      }
      
      void swap(int *pt1,int *pt2)
      {
      	int temp;
      	temp = *pt1;
      	*pt1 = *pt2;
      	*pt2 = temp;
      }
      
      指針作返回值

      所謂指針作返回值,就是定義函數時,使用指針類型

      //如下
      //定義函數的類型,其實是定義函數返回值的類型
      int* twoSum(int* nums, int numsSize, int target)
      {
          static int a[2]={0};
          
      	for (int i = 0; i < numsSize - 1; i++)
      	{
      		for (int j = i+1; j < numsSize; j++)
      		{
      			if (nums[i] + nums[j] == target)
      			{
      				a[0] = i;
      				a[1] = j;
      				return a;
      			}
      		}
      	}
      	return 0;
      }
      
      指向函數的指針

      指向函數的指針,其實就是通過指針調用函數,就我的學習經歷來說,使用不多

      指針調用函數實例:

      #include<stdio.h>
      
      int main()
      {
      //定義三個存放數據的變量
      	int num1,num2,NumMax;  
      //定義一個可以指向函數的指針
      	int (*p)(int,int);
      	
      //對需要輸入的數據進行必要的說明
      printf("請輸入num1:");
      scanf("%d",num1);
      printf("\n");
      
      printf("請輸入num2:");
      scanf("%d",num2);
      printf("\n");
      
      //指針指向函數
      p = max;
      
      //用指針調用函數
      NumMax = (*p)(num1,num2);
      
      //輸出結果
      printf("num1 = %d\tnum2 = %d\t NumMax = %d\n",num1,num2,NumMax);
      
      return 0;
      }
      
      //定義一個返回兩數中最大數的函數
      int max(int x,int y)
      {
      	return x > y  ?  x : y;
      }
      

      注意事項

      想要安全地使用指針,就必須明確指針指向的內存空間信息
      如:
      這塊內存空間的生命周期有多長
      這塊內存空間能否被操作
      內存空間不再使用時,是否已釋放
      在釋放內存空間后,指針是否已清零

      錯誤的函數示例:

      、//示例  錯誤函數
      int* twoSum(int* nums, int numsSize, int target)
      {
          int a[2]={0};
          
      	for (int i = 0; i < numsSize - 1; i++)
      	{
      		for (int j = i+1; j < numsSize; j++)
      		{
      			if (nums[i] + nums[j] == target)
      			{
      				a[0] = i;
      				a[1] = j;
      //返回值有誤 a是該函數在棧上臨時分配的內存
      //在函數調用結束后,會被析構
      				return a;
      			}
      		}
      	}
      	return 0;
      }
      
      

      錯誤的調用:

      int numlen[]=  {2, 7, 11, 15};
      int *result;
      int size = sizeof(numlen)/sizeof(numlen[0]);
      
      twoSum(numlen,size,9,result);
      
      int* twoSum(int* nums, int numsSize, int target,int *out)
      {
          int a[2]={0};
          
      	for (int i = 0; i < numsSize - 1; i++)
      	{
      		for (int j = i+1; j < numsSize; j++)
      		{
      			if (nums[i] + nums[j] == target)
      			{
      				a[0] = i;
      				a[1] = j;
      //值傳遞有誤  a是本函數中定義的臨時變量
      //函數調用完畢后,會被析構
      //無法通過指針 將內容傳遞出去
      				out = a;
      			}
      		}
      	}
      	return 0;
      }
      

      正確函數書寫:

      int numlen[]=  {2, 7, 11, 15};
      int *result;
      int size = sizeof(numlen)/sizeof(numlen[0]);
      
      twoSum(numlen,size,9,result);
      
      int* twoSum(int* nums, int numsSize, int target,int *out)
      {
      //使用靜態變量  其內存位置在全局區
          static int a[2]={0};
          
      	for (int i = 0; i < numsSize - 1; i++)
      	{
      		for (int j = i+1; j < numsSize; j++)
      		{
      			if (nums[i] + nums[j] == target)
      			{
      				a[0] = i;
      				a[1] = j;
      				out = a;
      			}
      		}
      	}
      	return 0;
      }
      

      指針越界:

      char *str;
      char strlen[4] = {'I',' ','a','m'};
      
      //指針指向字符數組
      str = strlen;
      
      //錯誤操作 字符數組不是字符串 缺少’\0‘
      //因此會越界輸出
      printf("%s",str);
      
      //其它越界
      str = strlen[3];
      
      //指向數組外的未知內存
      str++;
      
      //越界輸出
      printf("%s",str);
      

      操作常量區:

      char *str = "I am Chinese"; //該指針指向常量區的字符串
      
      free(str); //錯誤操作  常量區無法操作
      

      內存丟失:

      char *str1 = "I am Chinese";
      //在堆上分配100字節的內存
      char *str2 = (char *)malloc(100); 
      
      //錯誤操作  str2指針指向了常量區字符串
      str2 = str1;
      //在堆上分配的100字節內存丟失
      

      指針不清零:

      char *str1 = "I am Chinese";
      //在堆上分配100字節的內存
      char *str2 = (char *)malloc(100); 
      
      //假設使用完畢 進行釋放
      if(str2 != NULL)
      {
      	free(str2);
      }
      
      //計劃重新使用
      if(str2 !=NULL)
      {
      //錯誤操作  str2指向的內存已被釋放
      	strcpy(str2,str1);
      }
      
      //因此 釋放指針指向的內存后,指針應當復位清零
      if(str2 != NULL)
      {
      	free(str2);
      	str = NULL;
      }
      
      
      1. 指針忌指向臨時變量
        1. 忌指針型返回值指向該函數內的臨時變量
        2. 忌外部指針指向已調用結束的函數內的臨時變量
      2. 操作不可操作的內存區
      3. 內存丟失和指針清零
      4. 指針越界
      posted @ 2024-10-22 00:53  星夜之章  閱讀(54)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 九九热视频免费在线播放| 国产精品极品美女自在线观看免费| 日本伊人色综合网| 国产片av在线观看国语| 91国在线啪精品一区| 欧美成本人视频免费播放| 亚洲精品中文综合第一页| 久久精品女人的天堂av| 国产一区二区日韩在线| 日韩人妻少妇一区二区三区| 午夜福利影院不卡影院| 亚洲av一本二本三本| 国产成人亚洲一区二区三区| 欧美日本激情| 欧洲美熟女乱又伦免费视频| 亚洲第一区二区三区av| 国产91小视频在线观看| 国产精品自拍视频第一页| 国产视频精品一区 日本| 欧美亚洲另类自拍偷在线拍| 国产美女久久久亚洲综合| 日韩有码中文在线观看| 国产精品一区二区香蕉| 亚洲狠狠狠一区二区三区| 国产激情艳情在线看视频| 亚洲精品无码久久久影院相关影片| 丁香五月网久久综合| 亚洲香蕉网久久综合影视| 吉林市| 国产高清精品在线一区二区 | 特级毛片在线大全免费播放| 久久婷婷五月综合色丁香花| 亚洲熟妇精品一区二区| 盐源县| 大地资源高清免费观看| 亚洲综合色在线视频WWW| 亚洲精品日韩在线丰满| 成人av亚洲男人色丁香| 91精品国产免费人成网站| 一本色道久久加勒比综合| 久久精品国产蜜臀av|