內存管理程序員
8.1 做用域數組
C語言變量的做用域分爲:函數
l 代碼塊做用域(代碼塊是{}之間的一段代碼)佈局
l 函數做用域測試
l 文件做用域spa
8.1.1 局部變量操作系統
局部變量也叫auto自動變量(auto可寫可不寫),通常狀況下代碼塊{}內部定義的變量都是自動變量,它有以下特色:指針
l 在一個函數內定義,只在函數範圍內有效code
l 在複合語句中定義,只在複合語句中有效對象
l 隨着函數調用的結束或複合語句的結束局部變量的聲明聲明週期也結束
l 若是沒有賦初值,內容爲隨機
1 #include <stdio.h> 2 3 4 5 void test() 6 7 { 8 9 //auto寫不寫是同樣的 10 11 //auto只能出如今{}內部 12 13 auto int b = 10; 14 15 } 16 17 18 19 int main(void) 20 21 { 22 23 //b = 100; //err, 在main做用域中沒有b 24 25 26 27 if (1) 28 29 { 30 31 //在複合語句中定義,只在複合語句中有效 32 33 int a = 10; 34 35 printf("a = %d\n", a); 36 37 } 38 39 40 41 //a = 10; //err離開if()的複合語句,a已經不存在 42 43 44 45 return 0; 46 47 } 48 49
8.1.2 靜態(static)局部變量
l static局部變量的做用域也是在定義的函數內有效
l static局部變量的生命週期和程序運行週期同樣,同時staitc局部變量的值只初始化一次,但能夠賦值屢次
l static局部變量若未賦以初值,則由系統自動賦值:數值型變量自動賦初值0,字符型變量賦空字符
1 #include <stdio.h> 2 3 4 5 void fun1() 6 7 { 8 9 int i = 0; 10 11 i++; 12 13 printf("i = %d\n", i); 14 15 } 16 17 18 19 void fun2() 20 21 { 22 23 //靜態局部變量,沒有賦值,系統賦值爲0,並且只會初始化一次 24 25 static int a; 26 27 a++; 28 29 printf("a = %d\n", a); 30 31 } 32 33 34 35 int main(void) 36 37 { 38 39 fun1(); 40 41 fun1(); 42 43 fun2(); 44 45 fun2(); 46 47 48 49 return 0; 50 51 }
8.1.3 全局變量
l 在函數外定義,可被本文件及其它文件中的函數所共用,若其它文件中的函數調用此變量,須用extern聲明
l 全局變量的生命週期和程序運行週期同樣
l 不一樣文件的全局變量不可重名
8.1.4 靜態(static)全局變量
l 在函數外定義,做用範圍被限制在所定義的文件中
l 不一樣文件靜態全局變量能夠重名,但做用域不衝突
l static全局變量的生命週期和程序運行週期同樣,同時staitc全局變量的值只初始化一次
8.1.5 extern全局變量聲明
extern int a;聲明一個變量,這個變量在別的文件中已經定義了,這裏只是聲明,而不是定義。
8.1.6 全局函數和靜態函數
在C語言中函數默認都是全局的,使用關鍵字static能夠將函數聲明爲靜態,函數定義爲static就意味着這個函數只能在定義這個函數的文件中使用,在其餘文件中不能調用,即便在其餘文件中聲明這個函數都沒用。
對於不一樣文件中的staitc函數名字能夠相同。
注意:
l 容許在不一樣的函數中使用相同的變量名,它們表明不一樣的對象,分配不一樣的單元,互不干擾。
l 同一源文件中,容許全局變量和局部變量同名,在局部變量的做用域內,全局變量不起做用。
l 全部的函數默認都是全局的,意味着全部的函數都不能重名,但若是是staitc函數,那麼做用域是文件級的,因此不一樣的文件static函數名是能夠相同的。
8.1.7 總結
類型 |
做用域 |
生命週期 |
auto變量 |
一對{}內 |
當前函數 |
static局部變量 |
一對{}內 |
整個程序運行期 |
extern變量 |
整個程序 |
整個程序運行期 |
static全局變量 |
當前文件 |
整個程序運行期 |
extern函數 |
整個程序 |
整個程序運行期 |
static函數 |
當前文件 |
整個程序運行期 |
register變量 |
一對{}內 |
當前函數 |
8.2 內存佈局
8.2.1 內存分區
C代碼通過預處理、編譯、彙編、連接4步後生成一個可執行程序。
在 Linux 下,程序是一個普通的可執行文件,如下列出一個二進制可執行文件的基本狀況:
經過上圖能夠得知,在沒有運行程序前,也就是說程序沒有加載到內存前,可執行程序內部已經分好3段信息,分別爲代碼區(text)、數據區(data)和未初始化數據區(bss)3 個部分(有些人直接把data和bss合起來叫作靜態區或全局區)。
l 代碼區
存放 CPU 執行的機器指令。一般代碼區是可共享的(即另外的執行程序能夠調用它),使其可共享的目的是對於頻繁被執行的程序,只須要在內存中有一份代碼便可。代碼區一般是隻讀的,使其只讀的緣由是防止程序意外地修改了它的指令。另外,代碼區還規劃了局部變量的相關信息。
l 全局初始化數據區/靜態數據區(data段)
該區包含了在程序中明確被初始化的全局變量、已經初始化的靜態變量(包括全局靜態變量和局部靜態變量)和常量數據(如字符串常量)。
l 未初始化數據區(又叫 bss 區)
存入的是全局未初始化變量和未初始化靜態變量。未初始化數據區的數據在程序開始執行以前被內核初始化爲 0 或者空(NULL)。
程序在加載到內存前,代碼區和全局區(data和bss)的大小就是固定的,程序運行期間不能改變。而後,運行可執行程序,系統把程序加載到內存,除了根據可執行程序的信息分出代碼區(text)、數據區(data)和未初始化數據區(bss)以外,還額外增長了棧區、堆區。
l 代碼區(text segment)
加載的是可執行文件代碼段,全部的可執行代碼都加載到代碼區,這塊內存是不能夠在運行期間修改的。
l 未初始化數據區(BSS)
加載的是可執行文件BSS段,位置能夠分開亦能夠緊靠數據段,存儲於數據段的數據(全局未初始化,靜態未初始化數據)的生存週期爲整個程序運行過程。
l 全局初始化數據區/靜態數據區(data segment)
加載的是可執行文件數據段,存儲於數據段(全局初始化,靜態初始化數據,文字常量(只讀))的數據的生存週期爲整個程序運行過程。
l 棧區(stack)
棧是一種先進後出的內存結構,由編譯器自動分配釋放,存放函數的參數值、返回值、局部變量等。在程序運行過程當中實時加載和釋放,所以,局部變量的生存週期爲申請到釋放該段棧空間。
l 堆區(heap)
堆是一個大容器,它的容量要遠遠大於棧,但沒有棧那樣先進後出的順序。用於動態內存分配。堆在內存中位於BSS區和棧區之間。通常由程序員分配和釋放,若程序員不釋放,程序結束時由操做系統回收。
8.2.2 存儲類型總結
類型 |
做用域 |
生命週期 |
存儲位置 |
auto變量 |
一對{}內 |
當前函數 |
棧區 |
static局部變量 |
一對{}內 |
整個程序運行期 |
初始化在data段,未初始化在BSS段 |
extern變量 |
整個程序 |
整個程序運行期 |
初始化在data段,未初始化在BSS段 |
static全局變量 |
當前文件 |
整個程序運行期 |
初始化在data段,未初始化在BSS段 |
extern函數 |
整個程序 |
整個程序運行期 |
代碼區 |
static函數 |
當前文件 |
整個程序運行期 |
代碼區 |
register變量 |
一對{}內 |
當前函數 |
運行時存儲在CPU寄存器 |
字符串常量 |
當前文件 |
整個程序運行期 |
data段 |
1 #include <stdio.h> 2 3 #include <stdlib.h> 4 5 6 7 int e; 8 9 static int f; 10 11 int g = 10; 12 13 static int h = 10; 14 15 int main() 16 17 { 18 19 int a; 20 21 int b = 10; 22 23 static int c; 24 25 static int d = 10; 26 27 char *i = "test"; 28 29 char *k = NULL; 30 31 32 33 printf("&a\t %p\t //局部未初始化變量\n", &a); 34 35 printf("&b\t %p\t //局部初始化變量\n", &b); 36 37 38 39 printf("&c\t %p\t //靜態局部未初始化變量\n", &c); 40 41 printf("&d\t %p\t //靜態局部初始化變量\n", &d); 42 43 44 45 printf("&e\t %p\t //全局未初始化變量\n", &e); 46 47 printf("&f\t %p\t //全局靜態未初始化變量\n", &f); 48 49 50 51 printf("&g\t %p\t //全局初始化變量\n", &g); 52 53 printf("&h\t %p\t //全局靜態初始化變量\n", &h); 54 55 56 57 printf("i\t %p\t //只讀數據(文字常量區)\n", i); 58 59 60 61 k = (char *)malloc(10); 62 63 printf("k\t %p\t //動態分配的內存\n", k); 64 65 66 67 return 0; 68 69 } 70 71
8.2.3 存儲類型總結內存操做函數
1) memset()
#include <string.h>
void *memset(void *s, intc, size_tn);
功能:將s的內存區域的前n個字節以參數c填入
參數:
s:須要操做內存s的首地址
c:填充的字符,c雖然參數爲int,但必須是unsigned char , 範圍爲0~255
n:指定須要設置的大小
返回值:s的首地址
1 int a[10]; 2 3 4 5 memset(a, 0, sizeof(a)); 6 7 memset(a, 97, sizeof(a)); 8 9 int i = 0; 10 11 for (i = 0; i < 10; i++) 12 13 { 14 15 printf("%c\n", a[i]); 16 17 }
2) memcpy()
#include <string.h>
void *memcpy(void *dest, constvoid *src, size_tn);
功能:拷貝src所指的內存內容的前n個字節到dest所值的內存地址上。
參數:
dest:目的內存首地址
src:源內存首地址,注意:dest和src所指的內存空間不可重疊
n:須要拷貝的字節數
返回值:dest的首地址
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int b[10];
memcpy(b, a, sizeof(a));
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d, ", b[i]);
}
printf("\n");
//memcpy(&a[3], a, 5 * sizeof(int)); //err, 內存重疊
3) memmove()
memmove()功能用法和memcpy()同樣,區別在於:dest和src所指的內存空間重疊時,memmove()仍然能處理,不過執行效率比memcpy()低些。
4) memcmp()
#include <string.h>
intmemcmp(constvoid *s1, constvoid *s2, size_tn);
功能:比較s1和s2所指向內存區域的前n個字節
參數:
s1:內存首地址1
s2:內存首地址2
n:需比較的前n個字節
返回值:
相等:=0
大於:>0
小於:<0
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int b[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int flag = memcmp(a, b, sizeof(a));
printf("flag = %d\n", flag);
8.2.4 堆區內存分配和釋放
1)malloc()
#include <stdlib.h>
void *malloc(size_tsize);
功能:在內存的動態存儲區(堆區)中分配一塊長度爲size字節的連續區域,用來存放類型說明符指定的類型。分配的內存空間內容不肯定,通常使用memset初始化。
參數:
size:須要分配內存大小(單位:字節)
返回值:
成功:分配空間的起始地址
失敗:NULL
1 #include <stdlib.h> 2 3 #include <stdio.h> 4 5 #include <string.h> 6 7 8 9 int main() 10 11 { 12 13 int count, *array, n; 14 15 printf("請輸入要申請數組的個數:\n"); 16 17 scanf("%d", &n); 18 19 20 21 array = (int *)malloc(n * sizeof (int)); 22 23 if (array == NULL) 24 25 { 26 27 printf("申請空間失敗!\n"); 28 29 return -1; 30 31 } 32 33 //將申請到空間清0 34 35 memset(array, 0, sizeof(int)*n); 36 37 38 39 for (count = 0; count < n; count++) /*給數組賦值*/ 40 41 array[count] = count; 42 43 44 45 for (count = 0; count < n; count++) /*打印數組元素*/ 46 47 printf("%2d", array[count]); 48 49 50 51 free(array); 52 53 54 55 return 0; 56 57 } 58 59
2)free()
#include <stdlib.h>
voidfree(void *ptr);
功能:釋放ptr所指向的一塊內存空間,ptr是一個任意類型的指針變量,指向被釋放區域的首地址。對同一內存空間屢次釋放會出錯。
參數:
ptr:須要釋放空間的首地址,被釋放區應是由malloc函數所分配的區域。
返回值:無
8.3 內存分區代碼分析(在Linux下測試)
1) 返回棧區地址
1 #include <stdio.h> 2 3 int a = 10; 4 5 int *fun() 6 7 { 8 9 10 11 return &a;//函數調用完畢,a釋放 12 13 } 14 15 16 17 int main(int argc, char *argv[]) 18 19 { 20 21 int *p = NULL; 22 23 p = fun(); 24 25 *p = 100; //操做野指針指向的內存,err 26 27 28 29 return 0; 30 31 } 32 33 34 35 2) 返回data區地址 36 37 #include <stdio.h> 38 39 40 41 int *fun() 42 43 { 44 45 static int a = 10; 46 47 return &a; //函數調用完畢,a不釋放 48 49 } 50 51 52 53 int main(int argc, char *argv[]) 54 55 { 56 57 int *p = NULL; 58 59 p = fun(); 60 61 *p = 100; //ok 62 63 printf("*p = %d\n", *p); 64 65 66 67 return 0; 68 69 }
3) 值傳遞1
1 #include <stdio.h> 2 3 #include <stdlib.h> 4 5 6 7 void fun(int *tmp) 8 9 { 10 11 tmp = (int *)malloc(sizeof(int)); 12 13 *tmp = 100; 14 15 } 16 17 18 19 int main(int argc, char *argv[]) 20 21 { 22 23 int *p = NULL; 24 25 fun(p); //值傳遞,形參修改不會影響實參 26 27 printf("*p = %d\n", *p);//err,操做空指針指向的內存 28 29 30 31 return 0; 32 33 } 34 35 36 37 4) 值傳遞2 38 39 #include <stdio.h> 40 41 #include <stdlib.h> 42 43 44 45 void fun(int *tmp) 46 47 { 48 49 *tmp = 100; 50 51 } 52 53 54 55 int main(int argc, char *argv[]) 56 57 { 58 59 int *p = NULL; 60 61 p = (int *)malloc(sizeof(int)); 62 63 64 65 fun(p); //值傳遞 66 67 printf("*p = %d\n", *p); //ok,*p爲100 68 69 70 71 return 0; 72 73 }
5) 返回堆區地址
1 #include <stdio.h> 2 3 #include <stdlib.h> 4 5 6 7 int *fun() 8 9 { 10 11 int *tmp = NULL; 12 13 tmp = (int *)malloc(sizeof(int)); 14 15 *tmp = 100; 16 17 return tmp;//返回堆區地址,函數調用完畢,不釋放 18 19 } 20 21 22 23 int main(int argc, char *argv[]) 24 25 { 26 27 int *p = NULL; 28 29 p = fun(); 30 31 printf("*p = %d\n", *p);//ok 32 33 34 35 //堆區空間,使用完畢,手動釋放 36 37 if (p != NULL) 38 39 { 40 41 free(p); 42 43 p = NULL; 44 45 } 46 47 48 49 return 0; 50 51 }
內存操做函數
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<stdio.h> 3 #include<string.h> 4 #include<stdlib.h> 5 6 int main1501() 7 { 8 //int * p = (int *)malloc(sizeof(int) * 10); 9 ////參數:目標 值 字節大小 10 //memset(p, 0, 40); 11 //for (int i = 0; i < 10; i++) 12 //{ 13 // printf("%d\n", p[i]); 14 //} 15 //char * p = malloc(sizeof(char) * 10); 16 //memset(p, 0, 10); 17 //printf("%s\n", p); 18 19 20 int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; 21 memset(arr, 0, 40); 22 for (int i = 0; i < 10; i++) 23 { 24 printf("%d\n", arr[i]); 25 } 26 27 //free(p); 28 //system("pause"); 29 return EXIT_SUCCESS; 30 } 31 int main1502() 32 { 33 int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; 34 //int *p = malloc(sizeof(int) * 10); 35 //memcpy(p, arr, 40); 36 //for (int i = 0; i < 10; i++) 37 //{ 38 // printf("%d\n", p[i]); 39 //} 40 41 //free(p); 42 43 memcpy(&arr[2], arr, 20); 44 for (int i = 0; i < 10; i++) 45 { 46 printf("%d\n", arr[i]); 47 } 48 char arr1[] = { 'h','e','l','l','o' }; 49 char * p = malloc(100); 50 memset(p, 0, 100); 51 //一、函數參數不一樣 52 //二、strcpy拷貝字符串 memcpy 能夠拷貝一塊內存 53 //三、拷貝結束標誌不一樣 strcpy \0 memcpy 以個數爲結尾 54 strcpy(p, arr1); 55 memcpy(p, arr1, 5); 56 printf("%s\n", p); 57 free(p); 58 return 0; 59 } 60 61 int main1503() 62 { 63 int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; 64 //memmove拷貝重疊內存地址不會出現問題 可是效率比較低 若是拷貝源和拷貝目標沒有重疊 兩個函數效率同樣 65 memmove(&arr[2], arr, 20); 66 67 for (int i = 0; i < 10; i++) 68 { 69 printf("%d\n", arr[i]); 70 } 71 } 72 int main() 73 { 74 //int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; 75 //int arr2[5] = { 1,2,3,4 }; 76 77 //int val = memcmp(arr1, arr2, 20); 78 //if (!memcmp(arr1, arr2, 8)) 79 //{ 80 // printf("前兩個數組元素內容相同"); 81 //} 82 //printf("%d\n", val); 83 84 //int * p1 = malloc(sizeof(int) * 10); 85 //char * p2 = malloc(sizeof(char) * 40); 86 87 //memcpy(p1, "hello", 6); 88 //memcpy(p2, "hello", 6); 89 //if (!memcmp(p1, p2, 6)) 90 //{ 91 // printf("內容相同\n"); 92 //} 93 //else 94 //{ 95 // printf("內容不相同\n"); 96 97 //} 98 99 int a = 0xffff; 100 char b = 0xffff; 101 //printf("%d\n", b); 102 if (!memcmp(&a, &b, 2)) 103 { 104 printf("內容相同\n"); 105 } 106 else 107 { 108 printf("內容不相同\n"); 109 110 } 111 112 //free(p1); 113 //free(p2); 114 //練習 求出三名學生 三門功課成績 並排序 經過堆空間來實現 arr[3][3]; 115 int ** p = (int **)malloc(sizeof(int *) * 3); 116 p[0] = (int *)malloc(sizeof(int) * 3); 117 p[1] = (int *)malloc(sizeof(int) * 3); 118 p[2] = (int *)malloc(sizeof(int) * 3); 119 120 p[0][0] = 90; 121 p[0][1] = 80; 122 p[0][2] = 70; 123 124 //排序 125 126 free(p[0]); 127 free(p[1]); 128 free(p[2]); 129 130 free(p); 131 132 133 return 0; 134 }
堆空間開闢數組冒泡排序
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<stdio.h> 3 #include<string.h> 4 #include<stdlib.h> 5 #include <time.h> 6 7 #define MAX 10 8 9 int main() 10 { 11 srand((unsigned int)time(NULL)); 12 int * p = (int *)malloc(sizeof(int) * MAX); 13 //int len = sizeof(p) 14 for (int i = 0; i < MAX; i++) 15 { 16 //*(p + i) = rand() % 50; 17 p[i] = rand() % 50; 18 //*p++ = rand() % 50; 19 } 20 for (int i = 0; i < MAX; i++) 21 { 22 printf("排序前:\n"); 23 printf("%d\n", p[i]); 24 } 25 for (int i = 0; i < MAX - 1; i++) 26 { 27 for (int j = 0; j < MAX - i - 1; j++) 28 { 29 if (p[j] > p[j + 1]) 30 { 31 int temp = p[j]; 32 p[j] = p[j + 1]; 33 p[j + 1] = temp; 34 } 35 } 36 } 37 printf("排序前:\n"); 38 39 for (int i = 0; i < MAX; i++) 40 { 41 printf("%d\n", p[i]); 42 } 43 free(p); 44 45 46 system("pause"); 47 return EXIT_SUCCESS; 48 }