指针变量,回调函数

指针

定义

  • 指针是一个变量,有自己的地址,而指针所占的空间中存放着地址,此地址指向另外一个变量的地址(即指针中存放的是地址,此地址指向另外一块内存空间,此内存空间中存放着变量值)
  • 获取地址:&
  • int *ptr=&num1;ptr 是一个变量,所属的类型是指针变量,在这块内存空间中存放着一个地址,此地址指向一个int类型的变量,因此,ptr是一个int *类型
  • 解析指针:*ptr:获取ptr指向的值:ptr中存放着变量的地址,*ptr获取此地址,访问地址,得到变量的真实值。
  • 指针存储其他变量地址之前要进行声明:int *p ,char *p1;
  • int *p[4]:定义指针数组p,它由4个指向整型数据的指针元素组成
  • int (*p)[4]:p为指向包含4个元素的一维数组的指针变量

指针的算术运算

p++:每次加的是变量所占的字节数

#include <stdio.h>
const int MAX = 3;//常量
int main(){
    int var[] = {10,100,20};//数组
    int i, *p,*p2;
    p=var;//在p中存放的是var[0]的地址
    for(i=0;i<MAX;i++){
        printf("var[%d]=%p ",i,p);
        printf("var[%d]=%d ",i,*p);
        printf("\n");
        p++;//p++=p+1,p中存放的是地址,p+1,是指p中存放的地址+4个字节(int类型占四个字节)
    }
    p2 = &var[MAX-1];
    for(i=MAX-1;i>=0;i--){
        printf("var[%d]=%d ",i,*p2);
        p2--;
    }
      return 0;
}

指针数组

int *p[3]
在这里插入图片描述

#include <stdio.h>

int main(){
int var[]={1,2,3};
int i, *ptr[3];
for(i=0;i<3;i++){
    ptr[i]=&var[i];//var[i]是一个数,ptr里面存的是地址,所以要取址
}
for(i=0;i<3;i++){
    printf("*ptr[%d]=%d ",i,*ptr[i]);
    printf("指针中的对应地址:*ptr[%d]=%p ",i,ptr[i]);
    printf("对应地址: var[%d]=%p ",i,&var[i]);
    printf("指针中存放地址的空间本身的地址:ptr[%d]=%p ",i,&ptr[i]);
}
return 0;
}

应用

指向字符串的字符数组,存放四大名著,并显示

#include <stdio.h>
int main(){
    char *p1[5]={"三国演义","西游记","红楼梦","水浒传"};
    int i;
    for(i=0;i<4;i++){
        printf("*p1[%d]=%s ", i,p1[i]);//字符串是用%s
    } 
    return 0;
}

字符串前不加*,比如char P={"凉“},P就能够显示凉,而不是P,因为是直接赋值,直接存在了指针中,而不需要解析

#include <stdio.h>
int main(){
    int *p=3;
    int i=3;
    int *p1=&i;
    printf("直接赋值情况:p=%d",p);
    printf("间接取址赋值:p1=%d",*p1);
    return 0;
}

指针里存的不一定只是地址,存的是地址时需要*p解析访问,而直接赋值时不需要,p就可以输出,要取得指针本身的地址,需要&p

指向指针的指针(多重指针)

一个指向指针的变量在声明时必须:int ** ptr;在解析时也必须 ** ptr

#include <stdio.h>
void main(){
    int var;
    int *p1;
    int **p2;
    var=3;
    p1=&var;
    p2=&p1;
    printf("var本身的地址:%p ",&var);
    printf("var本身的地址:%p ",p1);
    printf("p1本身的地址:%p ",&p1); 
    printf("p2保存的地址:%p ",p2);
    printf("p2的值:%d",**p2);
}

传递指针给函数

实际上我们传入地址给函数,我们操作的是此地址对应的值,所以在函数体中需要用*p操作,如果用指针的地址操作,没有用p;

#include <stdio.h>
void test(int *p);

int main(){
    int a[]={1,2,3};
    int *p1;
    p1=a;
    int *p2=a;
    printf("%p\n",a);
    //test(a);//不能直接给数组名赋值,不会改变,数组名的地址不会改变。传的是地址
    test(p1);//+1后的值,没用,因为传递的是地址,操作是对地址操作,站在利用完之后依然会释放
    p2=p2+1;//直接对地址的操作,有用
    printf("%d ",*p1);
    printf("%p\n",p1);
    printf("%d ",*(p2+1));//+1后的值
    printf("%p\n",p2+1);
    int i,num=90;
    int *p3;
    p3=&num;
    printf("%p ",&num);
    test2(&num);
    printf("%p ",&num);//地址不会改变 
    printf("%d",num);
    return 0;
}
void test(int *p){
    p+=1;  
}
void test2(int *p){
    *p+=1;  
}

返回指针的函数

C语言允许函数返回值是一个指针
返回两个字符串中较长的字符串

#include <stdio.h>
#include <string.h>
char *strlong(char *str1, char *str2){
        return strlen(str1)>strlen(str2)?str1:str2;
}

int main(){
    
    char str1[30],str2[30],*str;
    printf("请输入str1字符串");
    scanf("%s",&str1);
    printf("请输入str2字符串");
    scanf("%s",&str2);
    printf("str1地址%p ",str1);
    printf("str2地址%p ",str2);
    str=strlong(str1,str2);//这样赋值就是取的地址,因为函数是一个指针类型的
    printf("%s ",str);//这里不用加取址符
    printf("str地址%p",str);
    return 0;

}

注意事项

函数返回的指针不能指向函数内部的局部变量,函数内部的局部变量会被销毁,这里的销毁并不是将局部数据所占用的内存全部清零,而是程序放弃对它专用的使用权限,也就是这块内存可以被其他人使用,所以如果调用后的程序有可能输出正确的值
如果想要返回返回函数中局部变量的地址,那么需要用static修饰,因为static修饰后所占用的不是栈空间,而是静态存储空间。

#include <stdio.h>
#include <stdlib.h>
int *f1(){
    static int arr[10];//必须加static让arr的空间在静态存储区存储
    int i=0;
    for(i=0;i<10;i++){
        arr[i]=rand();
    }
    return arr;
}
void main(){
    int *p;
    p=f1();
    int i;
    for(i=0;i<10;i++){
        printf("元素值:[%d] ", *(p+i));
    }
}

函数指针

函数名的本质是一个地址,需要使用函数指针来接收
函数名可以被转换为函数的首地址,是指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数,这种指针就是函数指针
returnType (*pointName) (param list)
cichudepointName为函数名称,*pointName必须加括号
内存分为:栈区,堆区,静态区(全局区),代码区
函数一开始被放在了代码区,当执行到指针函数,指针函数只想代码区的函数

#include <stdio.h>
int  max(int a,int b){
    return a>b?a:b;
}
int (*pmax) (int a,int b);
int main(){
    int x,y,maxV;
    int(*pmax)(a,b)=max;//函数指针名字pmax,int表明该函数指针指向的函数返回int类型
    printf("请输入两个数:");
    scanf("%d %d",&x,&y);
    maxV=(*pmax)(x,y);//通过*pmax去调用函数
   //maxV=pmax(x,y);也可以调用
    printf("maxV=%d",maxV);
    return 0;
}

回调函数

函数指针变量可以作为某个函数的参数来使用,回调函数就是一个通过函数指针调用的函数

#include <stdio.h>
#include <stdio.h>
//回调函数,(*f)(void)是一个返回值为int,没有形参的函数指针
//f被intitArray函数回调
void intitArray(int *array ,int arraySize,int(*f)(void)){
    int i;
    for (i=0;i<arraySize;i++){
        array[i]=f();//通过函数指针调用了getRand函数
    }
}
int getRand(void){
    return rand();
}
int main(){
    int myarray[10],i ;//未初始化
    intitArray(myarray,10,getRand);//getRand是传入的函数名,函数名的本质是一个地址,需要使用函数指针来接收
    for(i=0;i<10;i++){
        printf("myarray[%d]=%d",i,myarray[i]);
    }
   return 0;
}

定义空指针

作用:一开始没有确切的地址赋值
NULL在库stdio.h中

#include <stdio.h>
void main(){
int *p=NULLint num=34;
p=&num;
printf("*p=%d",*p)

动态内存分配

全局变量–内存中的静态存储区
非静态的局部变量–栈区
临时使用的数据–建立动态内存存储区,需要使用,不需要释放
根据需要向系统申请所需要的大小空间,不能通过变量名或数组名直接访问,需要通过堆去访问
所需头文件:stdlib
malloc(100):在堆区分配长度为size的连续空间开辟100个空间,返回值是第一个字节的地址
calloc(n,size):开辟n个长度为size的连续空间,空间较大,分配数组
calloc(50,4)开辟50*4个字节的临时空间
free§:释放空间
realloc(p,size): 重新分配malloc和calloc所获得动态空间大小,将p指向的动态空间大小改变为size,p的值不变,首地址不变。需求是将空间扩大缩小。
void *p就是一个纯地址,不指向任何对象,malloc calloc realloc都是void
int a=3;
int *p=&a;
void *p2;
p2=(void )p;把p指针存的地址赋给p2,但是p指针不改变
p3=(void
)&a;
*p3是不对的,对于void类型的指针变量,不能进行解析

#include <stdio.h>
#include <stdlib.h>
int main(){
    void check(int *);//函数声明
    int *p,i;
    p=(int *)malloc(5*sizeof(int));
    printf("分配的首地址为:%p",p);
    printf("请输入成绩为:");
    for(i=0;i<5;i++){
        scanf("%d",p+i);
    }
    check(p);
    free(p);
    return 0;
}
void check(int *p){
    int i;
    printf("低于80:");
    for(i=0;i<5;i++){
        if(*(p+i)<80){
            printf("%[d]尚需努力 ",i);
        }
    }
}

基本原则:

  • 尽量避免分配多个很小的内存块
  • 及时释放,否则出现内存泄漏
  • 释放的时间
  • 循环分配内存可能出现覆盖
  • 22
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值