(1)函数指针 先看段代码 采用传统的写法,直接调用函数
#include "stdafx.h" #include "Windows.h" void showMessage(){ //弹窗 MessageBoxA(0,"content","消息标题",0); } int _tmain(int argc, _TCHAR* argv[]) { //传统写法,直接调用 showMessage(); getchar(); return 0; }看下运行效果
下面我们通过指针来调用函数
#include "stdafx.h" #include "Windows.h" void showMessage(){ //弹窗 MessageBoxA(0,"消息内容","消息标题",0); } int _tmain(int argc, _TCHAR* argv[]) { //传统写法,直接调用 void(*p)()=showMessage; p(); getchar(); return 0; }下面我们来看下有返回值,有参数的函数如何通过指针来调用
#include "stdafx.h" #include "Windows.h" double add(double a,double b){ return a+b; } int _tmain(int argc, _TCHAR* argv[]) { double(*p)(double a,double b)=add; double temp=p(2,3); printf("%lf",temp); getchar(); return 0; }结论:函数指针不仅仅是地址,必须明确的指定函数的指针类型以及返回值和参数列表 回想Java语言:方法的重载(方法名相同,参数列表不同或者参数类型不同,与返回值类型无关),c语言也有类似的方法重载
#include "stdafx.h" #include "Windows.h" double add(double a,double b){ return a+b; } void add(int a,int b){ } int _tmain(int argc, _TCHAR* argv[]) { double(*p)(double a,double b)=add; double temp=p(2,3); printf("%lf",temp); getchar(); return 0; }(2)函数指针–案例
#include "stdafx.h" #include "Windows.h" #include <time.h> int* getMin(int a[],int len){ int i=0; //选择法进行判断筛选,假设数组中第一个元素是最小值 int min=a[0]; int *p=&a[0]; for(;i<len;i++){ if(a[i]<min){ min=a[i]; p=&a[i]; } } return p; } int _tmain(int argc, _TCHAR* argv[]) { //要让每一次都不一样 //初始化随机数发生器 //time_t实际上就是一个时间--长整型 time_t t; //srand传入的参数不相同,那么就会生成新的随机数 //time:相当于java中的data,1970年一月一日0分0秒 srand((unsigned)time(&t)); int a[5]; //通过随机数动态赋值 int i=0; for(;i<5;i++){ //随机数范围在50以内 a[i]=rand()%50; printf("%d\n",a[i]); } //获取数组长度 int len=sizeof(a)/sizeof(int); int* min=getMin(a,len); printf("%d",*min); getchar(); return 0; }看下运行结果
本案例中注意随机数的产生,与java有所不同 (3)动态分配内存栈区
void main(){ //定义一个40MB(int类型是4个字节)的数组 int a[1024*1024*10]; getchar(); }运行会报错————栈内存溢出 stack overflow 数组的数据默认保存在栈内存中;栈内存的大小和操作系统、c库版本有关; 联想数据结构:栈是先进后出 在c语言中,内存分配分为栈区,堆区,全局区和静态区,程序代码区,字符常量区 默认情况下定义的变量都放在栈区, 我们下面看下栈区内存会不会自动回收
void stackMethod(){ int a[5]; printf("%#x\n",a); int i=0; for(;i<5;i++){ a[i]=i+1; } printf("\n"); } void main(){ //循环创建数组 while(true){ stackMethod(); //线程睡觉 Sleep(2000); } }我们在main方法中的stackMethod()处打断点运行,会发下内存地址a对应的值有时候是1,有时候没有,这说明了数组在栈内存中被回收掉了。 随意我们得出结论,栈区的内存是自动分配 ,自动释放;栈的内存主要存放的是函数的参数值和局部变量;上面的例子中int a[]就是局部变量; (4)动态分配内存堆区 常用的放在栈区,不常用的放在堆区; 大家看下面的代码
void heapMethod(){ //在堆内存开辟10MB的内存空间 int* p=(int*)malloc(1024*1024*10); printf("\n"); } void main(){ //循环创建数组 while(true){ heapMethod(); //线程睡觉 Sleep(2000); printf("开辟成功"); } }看下运行效果
大家在看下这张截图: 我鼠标所在的那个数字在不断的增长,每次都会增加1024*1024*10; 会不断的开辟新的内存,内存不会回收;
总结:堆区内存需要自己回收释放内存,系统不会做这些事情 (5) 全局区和静态区 存放全局变量和静态变量(系统自动管理,当我们的应用程序结束的时候释放这些全局变量和静态变量占据的内存) 程序代码区: 存放我们的程序代码 字符常量区: 存放常量,字符串,系统自动管理,当我们的应用程序结束的时候释放 (6)堆内存的回收, 堆内存的回收需要用到一句代码—— free(p); 我们在看下前面运行过的一个程序:
void heapMethod(){ //在堆内存开辟40MB的内存空间 int* p=(int*)malloc(1024*1024*10); //回收内存 free(p); printf("\n"); } void main(){ //循环创建数组 while(true){ heapMethod(); //线程睡觉 Sleep(2000); printf("开辟成功"); } }运行程序大家看下系统的进程
大家看下,一直是376K没有变化,因为我们开辟后又把他回收掉了 (7)malloc方法 创建一个数组,动态的指定数组的大小
void main(){ int len; printf("请输入数组的长度:"); scanf_s("%d",&len); //动态指定数组的大小(用于存储int类型的数据) //p是该内存空间的首地址指针 //该内存空间用来存数组 int*p=(int*)malloc(len*sizeof(int)); int i=0; for(;i<len;i++){ p[i]=rand()P; printf("%d %#x\n",p[i],&p[i]); } system("pause"); }看下运行效果 我们可以联想java中的集合; malloc是把内存开在了堆内存区中;返回类型void*;在c语言中,void*方法指针可以指向任何类型的方法指针(联想java中的Object); (8)重新分配内存
void main(){ int len; printf("请输入数组的长度:"); scanf("%d", &len); //int* p = (int*)malloc(5 * sizeof(int)); //第一个参数:数组长度 //第二个参数:每个元素大小 //更方便 int* p = (int*)calloc(5,sizeof(int)); int i = 0; for (; i < len; i++){ p[i] = rand() % 50; printf("%d %#x\n", p[i], &p[i]); } //改变原始内存区域的大小(增加-减小) int addLen; printf("请输入数组的增加长度:"); scanf("%d", &addLen); //重新输出数组的数据---需要从新分配内存 //注意:新的=老的+增加的 //realloc更改已经配置的内存空间 //缩小:会导致一部分数据丢失 //扩大(连续不断-线性排列) //情况一:如果当前的内存段后面有需要的内存空间,就会直接追加(注意:返回原指针) //情况二:如果当前内存段后面空闲的字节空间不够 //那么就会重新再堆内存中寻找能够容纳该数据大小的内存区域(注意:返回值新的内存地址,原来的被释放) //情况三:如果没有容身之处,申请内存失败,将返回NULL,而且原来的指针有效 int* p1 = (int*)realloc(p,sizeof(int)*(len+addLen)); i = len; for (; i < len+addLen; i++){ p1[i] = rand() % 50; } //输出 i = 0; for (; i < len + addLen; i++){ printf("%d %#x\n", p1[i], &p1[i]); } system("pause"); }(9)内存分配的注意事项
1、不能够多次释放内存
free(p); free(p); 这样写会报错2、释放内存之后,给原来的指针设置NULL
void main(){ int len; printf("请输入数组的长度:"); scanf("%d", &len); int* p = (int*)calloc(5, sizeof(int)); int i = 0; for (; i < len; i++){ p[i] = rand() % 50; printf("%d %#x\n", p[i], &p[i]); } //释放内存 free(p); //标记内存已经被释放 p = NULL; free(p); system("pause"); }3、内存泄漏
void heapMethod(){ //在堆区开辟了一块40M的内存空间 int* p = (int*)malloc(1024 * 1024 * 10 * 4); p = NULL; //回收内存 free(p); printf("\n"); } void main(){ //循环创建数组 while (1) { heapMethod(); Sleep(6000); } }