Day14、枚举、联合、二级指针、回调函数、动态内存分配

来源:互联网 发布:淘宝联盟api 优惠卷 编辑:程序博客网 时间:2024/06/07 09:47

枚举用来创建新的数据类型

枚举类型存储区就是整数类型存储区,只不过枚举类型存储区只能记录有限的几个整数

声明枚举类型的时候需要提供几个名称

编译器为每个名称指定一个对应的整数,只有这些整数可以记录在枚举类型的存储区里

不同枚举类型存储区里可以存放的数字范围不同

声明枚举类型使用enum关键字

编译器采用从0开始的一组连续的整数和枚举类型中的名称对应

在声明枚举类型时指定用哪个数字对应哪个名称

  1#include<stdio.h>

  2int main(){

 3     enum {spring,summer,autumn,winter};

 4    printf("%d",autumn);

  5 }

联合用来创建新的数据类型,需要先声明后使用,使用union关键字

联合存储区可以用来当做多种不同类型的存储区使用

联合声明中的每个成员变量代表联合存储区一种可能使用类型

联合里所有成员变量存储区的开始地址一样,它们所占的内存是互相重叠的。例:

  1#include<stdio.h>

  2typedef union{

 3     int num;

 4     float fnum;

 5     char buf[4];

  6}tmp;

  7int main(){

 8     tmp tmp1={0};

 9     printf("&(tmp1.num)是%p\n",&(tmp1.num));

 10    printf("&(tmp1.fnum)是%p\n",&(tmp1.fnum));

 11    printf("&(tmp1.buf)是%p\n",&(tmp1.buf));

 12    return 0;

 13 }

&(tmp1.num)是0xbf8734d8

&(tmp1.fnum)是0xbf8734d8

&(tmp1.buf)是0xbf8734d8      

联合变量所占的内存长度等于最长的成员长度

printf("sizeof(tmp)是%d\n",sizeof(tmp));      是4

 

二级指针

用来记录普通变量存储区地址的指针叫一级指针

二级指针用来记录一级指针的地址

声明二级指针时写两个**

  1#include<stdio.h>

  2int main(){

 3     int num=0;

 4     int *p_num=&num;

 5     int **pp_num=&p_num;

 6     //相当于pp_num=&p_num;

 7     **pp_num=10;

 8    printf("num=%d\n",num);

 9     return 0;

 10 }

二级指针变量名称前写**表示捆绑的普通变量

二级变量名称前写*表示捆绑的一级指针

 9     *pp_num=NULL;

 10    printf("p_num是%p\n",p_num); 

输出结果:p_num是(nil) 

窍门:一个*抵消一个p  

指针数组里第一个指针存储区的地址记录到二级指针里,这时可以用二级指针表示整个指针数组

  1#include<stdio.h>

  2int main(int argc,char /* *argv[] */**argv){ //指针形参

 3     int num=0;

 4    for(num=0;num<=argc-1;num++){

 5        printf("%s\n",*(argv+num));       //打印字符串(一级指针地址)

 6     }

  7 }

输入指令:./a.outabc def

结果:./a.out

abc

def

无类型指针有可能实际代表二级指针

二级指针形式参数可以让被调用函数使用调用函数提供的一级指针存储区

  1#include<stdio.h>

  2void set_null(int **pp_num){

 3     *pp_num=NULL;

  4 }

  5int main(){

 6     int num=0;

 7     int *p_num=&num;

 8     set_null(&p_num);

 9     printf("p_num是%p\n",p_num);

 10    return 0;

 11 }

例题:编写程序从键盘得到两个圆的位置

编写函数从这两个圆里找到面积比较大的,并把结果传递给调用函数,不能用返回值

  1#include<stdio.h>

  2typedef struct{

 3     int row,col;

  4}pt;

  5typedef struct{

 6     pt center;

 7     int radius;

  8}circle;

  9void larger(const circle *p_cl1,const circle *p_cl2,circle **pp_cl){

 10    *pp_cl=(circle *)(p_cl1->radius>=p_cl2->radius?p_cl1:p_cl2);

 11 }

 12int main(){

 13    circle cl1={0},cl2={0},*p_cl=NULL;

 14    printf("请输入圆一的位置:");

 15    scanf("%d%d%d",&(cl1.center.row),&(cl1.center.col),&(cl1.radius));

 16    printf("请输入圆二的位置:");

 17    scanf("%d%d%d",&(cl2.center.row),&(cl2.center.col),&(cl2.radius));

 18    larger(&cl1,&cl2,&p_cl);

 19    printf("结果是(%d %d) %d \n",p_cl->center.row,p_cl->center.col,p_cl->radius);

 20    return 0;

 21 }

 

C语言里函数也有地址

函数名称可以用来表示函数地址,函数指针用来记录函数的地址

  1#include<stdio.h>

  2int add(int num,int num1){

 3     return num+num1;

  4 }

  5int main(){

 6     printf("add地址是%p\n",add);

 7     return 0;

  8 }                  执行结果:add地址是0x80483e4

函数指针需要先声明再使用

函数指针的声明根据函数声明变化得到

函数指针有不同类型,不同类型的函数指针适合与不同类型的函数捆绑

函数指针的作用用来调用函数

  1#include<stdio.h>

  2int add(int num,int num1){

 3     return num+num1;

  4 }

  5int main(){

 6     int (*p_func)(int,int)=NULL;    //函数指针的声明来源于函数声明

 7     p_func=add;

 8  //   printf("%d\n",add(3,5));    add(3,5) 是函数的地址(入口),打印返回值

 9     printf("%d\n",p_func(3,5));    p_func( )是函数的地址

 10    return 0;

 11 }

 

  /*  8    int num=add(3,5);

 9     printf("%d\n",num);   */  也可以出结果 8

回调函数

可以当做实际参数使用的函数叫做回调参数

将一个函数的地址作为实参传递给另一个调用函数

例:打印函数      

(函数有专用部分,有通用部分,就像螺丝刀有螺丝手柄和螺丝刀头,跟不同的头捆绑,可以拧不同的螺丝)

原函数是:

  1#include<stdio.h>

  2void print(const int *p_num,int size){

 3     int num=0;

 4     for(num=0;num<=size-1;num++){

 5         printf("%d",*(p_num+num));

 6     }

 7     printf("\n");

  8 }

  9int main(){

 10    int arr[]={1,2,3,4,5};

 11    print(arr,5);

 12    return 0;

 13 }

用函数指针修改使之成为回调函数:

  1#include<stdio.h>

  2/*void print(const int *p_num,int size){  //通用

 3     int num=0;

 4    for(num=0;num<=size-1;num++){

 5         printf("%d",*(p_num+num));    //专用

 6     }

 7     printf("\n");

  8}*/

  9void print_cb(int *p_num){

 10    printf("%d",*p_num);

 11 }

 12void for_each(int *p_num,int size,void (*p_func)(int *)){

 13    int num=0;

 14    for(num=0;num<=size-1;num++){

 15        p_func(p_num+num);

 16     }

 17 }

 18int main(){

 19    int arr[]={1,2,3,4,5};

 20//    print(arr,5);

 21    for_each(arr,5,print_cb);

 22    printf("\n");

 23    return 0;

 

练习:利用for_each函数,把数组里每个存储区的内容变成相反数

  1#include<stdio.h>

  2void print_cb(int *p_num){

 3     printf("%d",*p_num);

  4 }

  5void neg_cb(int *p_num){

 6     *p_num=0-*p_num;

  7 }

  8void for_each(int *p_num,int size,void (*p_func)(int *)){

 9     int num=0;

 10    for(num=0;num<=size-1;num++){

 11        p_func(p_num+num);

 12     }

 13 }

 14int main(){

 15    int arr[]={1,2,3,4,5};

 16    for_each(arr,5,neg_cb);

 17    for_each(arr,5,print_cb);

 18    printf("\n");

 19     return 0;

 20 }

动态存储方式和静态存储方式

在程序运行时可以临时决定需要分配多少内存(存储区),这种分配存储区的方法叫做动态内存分配。

为了管理动态分配内存,需要使用一组标准函数,使用这组标准函数需要包含stdlib.h头文件

malloc函数可以动态分配一组连续的字节,需要一个整数类型参数表示希望分配的字节个数

这个返回值是分配好的第一个字节的地址,如果分配失败,则返回值是NUILL(空地址)

函数把返回值记录在无类型指针存储区里,使用前必须进行强制类型转换

动态分配内存使用完后必须释放

Free函数用来释放动态分配内存,需要一个参数表示动态分配的首地址

一起分配的内存必须一起释放

如果用指针作为参数调用free函数,就必须在函数结束后把指针设置成空指针

动态内存分配 例:框架:

  1#include<stdio.h>

  2#include<stdlib.h>

  3int main(){

 4     int *p_num=(int*)malloc(5*sizeof(int));

 5     //(int *)是无类型字节强制转换成整型类型的指针

 6     if(p_num){   //如果分配成功

 7         //.....使用动态分配内存

 8         free(p_num);

   9         p_num=NULL;

  10     }

11     return 0;

  12 }     

  1#include<stdio.h>

  2#include<stdlib.h>

  3int main(){

 4     int *p_num=(int*)malloc(5*sizeof(int));

5    if(!p_num){ //如果分配失败

  6        return 0;

  7     }

8     //使用动态分配内存

  9    free(p_num);

 10     p_num=NULL;

 11     return 0;

 12 }

例题:编写程序生成一张彩票,在动态分配内存里记录彩票中的数字

  1#include<stdio.h>

  2#include<time.h>

  3#include<stdlib.h>

  4int main(){

 5     int *p_num=NULL,num=0;

 6     srand(time(0));

 7     p_num=(int*)malloc(7*sizeof(int));

 8     if(p_num){

 9        for(num=0;num<=6;num++){

 10            *(p_num+num)=rand()%36+1;

 11        }

 12        for(num=0;num<=6;num++){

 13            printf("%d",*(p_num+num));

 14        }

 15        printf("\n");

 16        free(p_num);

 17        p_num=NULL;

 18     }

 19    return 0;

 20 }

调用函数可以使用被调用函数动态分配的内存

  1#include<stdio.h>

  2#include<stdlib.h>

  3#include<time.h>

  4int *creat(void){

 5     int num=0;

 6     int *p_num=(int*)malloc(7*sizeof(int));

 7     if(p_num){  //若分配成功

 8        for(num=0;num<=6;num++){

 9            *(p_num+num)=rand()%36+1;

 10        }

 11        return p_num;

 12     }

 13 }

 14int main(){

 15     int num=0;

 16    int *p_num=NULL;

 17    srand(time(0));

 18    p_num=creat();

 19    if(p_num){    //若分配成功

 20        for(num=0;num<=6;num++){

 21            printf("%d ",*(p_num+num));

 22        }

 23        free(p_num);

24         p_num=NULL;

 25     }

 26    printf("\n");

 27    return 0;

 28 }

练习:编写函数计算两个已知点的中间点位置,并把结果传递给调用函数,用动态分配内存记录结果

  1#include<stdio.h>

  2#include<stdlib.h>

  3typedef struct{

 4     int row,col;

  5}pt;

  6pt *midpt(const pt *p_pt1 , const pt *p_pt2){

  7     pt *p_pt=(pt *)malloc(sizeof(pt));

 8     if(p_pt){

 9        p_pt->row=(p_pt1->row+p_pt2->row)/2;

 10        p_pt->col=(p_pt1->col+p_pt2->col)/2;

 11     }

 12        return p_pt;

 13 }

 14int main(){

 15    pt pt1={0},pt2={0};

 16    pt *p_pt=NULL;

 17    printf("请输入点一的位置: ");

 18    scanf("%d%d",&(pt1.row),&(pt1.col));

 19    printf("请输入点二的位置: ");

 20    scanf("%d%d",&(pt2.row),&(pt2.col));

 21    p_pt=midpt(&pt1,&pt2);

 22    if(p_pt){

 23    printf("中点位置是(%d %d)\n",p_pt->row,p_pt->col);

24     free(p_pt);

 25    p_pt=NULL;

 26     }

 27    return 0;

 28 }

0 0