计算机系统中有四个内存区域:1)栈:在栈里面储存一些我们定义的局部变量以及形参;2)字符常量区:主要是储存一些字符常量;3)全局区:在全局区里储存一些全局变量和静态变量;4)堆:堆主要是通过动态分配的储存空间,即以下讲的讲的动态分配内存空间。
定义int型指针int *p,p是用来储存一个地址的值的,我们之所以要为p分配空间是让它有一个明确的指向,比如你现在做好了一个指向方向的路标,但是你并没有让这个路标指向一个确切的方位,这个路标是瞎指的,这样我们就不能够通过它来明确到底哪里是东,哪里是西了。也就是说在计算机的内存里定义了一个指针变量,但是我们并没有让这个变量指示一个确切int类型变量的地址,所以必须要让它有一个明确的指示方向。所以就要通过动态分配内存的方式来认为的规定确定其方向。
当malloc()函数为一个指针变量p分配了地址,当对p结束操作的时候要释放p的内存空间。动态分配的变量时储存在堆里面,但是这个堆的空间不是无限大的,也许当编一个小的程序的时候可能不会产生影响,但是对于那些大的程序,如果不及时释放堆的空间就会发生内存泄露。所谓内存泄露是因为堆的空间被我们动态分配用完了,这样再去使用动态分配堆的空间的时候就没有足够的空间可以使用了,这样就需要占有原来的空间,也就是会把其他的空间来储存我们键入的值,这样会导致原来储存的数据被冲掉,导致了内存泄露。
而且当使用malloc()函数释放完空间,还要将原先的指针变量赋予一个NULL,也就是赋予一个空指针,留着下次的时候使用它。如果不赋予一个空指针这样会导致原来的指针变量变成了一个野指针,就是一个没有明确指向的指针,系统不知道它会指向什么地方,会是系统crash。
注:动态的分配内存。可以做到准确分配空间大小,不浪费资源,而且也不会发生程序不断使用预先分配内存不足。动态分配的内存空间系统不负责自动回收,需要写代码手动释放。
相对于malloc()函数,calloc()函数就不需要我们赋予NULL了,这是因为在每次调用完calloc()函数的时候系统会自动将原先的指针赋予一个空指针。除了malloc()、calloc(),还有realloc()函数,比前两个函数分配更多的空间。
例子
1.指针的初始化
初始化不是指针的动态分配内存,但类似malloc函数,也就是让指针变量有确定的方向或者有确定的地址指向。
1)定义并初始化
int a = 10; int*p = &a;
2)先定义再初始化
int a =10; int*p; p = &a;
实例:
1 #include <stdio.h> 2 3 int main(){ 4 int a = 10, b = 20, c = 30; 5 int *p = &a; //定义指针变量 6 *p = b; //通过指针变量修改内存上的数据 7 c = *p; //通过指针变量获取内存上的数据 8 printf("%d, %d, %d, %d ", a, b, c, *p); 9 return 0; 10 }
运行结果:
20,20,20,20
2.字符串指针
1)定义并赋值
char *pstr = "Hello World!";
2)先定义再赋值
1 char str[] = "Hello World!"; 2 char *pstr; 3 if(str) 4 { 5 *pstr = malloc(strlen(str)+1); 6 strncpy(pstr,str,strlen(str)+1); 7 } 8 else 9 { 10 pstr = NULL; 11 }
用完之后:
1 if(str) 2 { 3 free(pstr); 4 pstr = NULL; 5 }
3 指针作为函数形参实参
C语言中实参变量和形参变量之间的数据传递是单向的“值传递”方式。用指针变量作函数参数同样要遵循这一规则。不可能通过执行调用函数来改变实参指针变量的值,但是可以改变实参指针变量所指向变量的值。
3.1 正确实例代码,以输入的两个整数按大小顺序输出。正确的代码如下:
1 # include<stdio.h> 2 3 int main(void) 4 { 5 void swap1(int * p1, int *p2); 6 int * pointer_1, * pointer_2; //定义两个指针变量 7 int a, b; 8 printf("Please enter two number! "); 9 scanf("%d %d", &a, &b); 10 pointer_1 = &a; //将pointer_1指向变量a 11 pointer_2 = &b; //将pointer_2指向变量b 12 printf("a = %d, b = %d ", a, b); 13 if (a<b) //判断a和b的大小 14 swap1(pointer_1, pointer_2);//会改变指针指向的值 15 printf("max = %d, min = %d ", a, b); 16 17 return 0; 18 } 19 20 void swap1(int * p1, int *p2) //定义swap函数,定义两个指针变量形参p1, p2 21 { //形参和传入实参的类型要一致 22 int temp; 23 temp = * p1; //改变指针变量所指向变量的值 24 *p1 = * p2; 25 *p2 = temp; 26 }
输出结果:
1 Please enter two number! 2 a = 50, b = 60 3 max = 60, min = 50
3.2 错误代码实例:
1 # include<stdio.h> 2 3 int main(void) 4 { 5 void swap2(int * p1, int * p2); 6 int * pointer_1, * pointer_2; 7 int a, b; 8 printf("Please enter two number! "); 9 scanf("%d %d", &a, &b); 10 pointer_1 = &a; 11 pointer_2 = &b; 12 printf("a = %d, b = %d ", a, b); 13 if (a<b) 14 swap2(pointer_1, pointer_2); 15 printf("max = %d, min = %d ", a, b); 16 17 return 0; 18 } 19 20 void swap2(int * p1, int * p2) 21 { 22 int * p; 23 p = p1; 24 p1 = p2; 25 p2 = p; 26 }
输出结果:
1 Please enter two number! 2 a = 50, b = 60 3 max = 50, min = 60
代码差异分析:
两段代码其实只是swap1和swap2函数的不同:
正确代码传入实参,指针变量p1和pointer_1都指向变量a,但改变的是实参指针变量所指向的地址的值(即a和b的值都已经改变),调用swap函数后,指针变量p1和p2都释放掉。
错误代码和正确代码前面相同的,传入实参,指针变量p1和pointer_1都指向变量a,但是改变的只是形参p1和p2的指向的地址,并没有改变实参的任何东西,调用swap函数后,指针变量p1和p2会释放掉,然后并没有改变a和b的值。