• 重温C语言十二---指针


    十三、指针

    1.指针的基本介绍

    1)指针是C语言的精华,也是C语言的难点

    2)指针,也就是内存的地址;所谓指针变量,也就是保存了内存地址的变量。

    3)获取变量的地址,用&,比如:int num=10,获取了num的地址:&num

    4)指针类型,指针变量存的是一个地址,这个地址指向的空间存的才是值 比如:

    int *ptr= & num; ptr 就是指向int类型的指针变量,即ptr是 int *类型。

    5)获取指针类型所指向的值,使用: *(取值符号),比如 int * ptr,使用 *ptr 获取ptr指向的值

    2.什么是指针

    指针是一个变量,其值为另一个变量的地址,即内存位置的直接地址。就像其他变量或常量一样,在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:

    int *ip; //一个整形的指针
    double *dp;//一个double型的指针
    float *fp;//一个float型的指针
    char *ch;//一个字符型的指针
    
    • 1
    • 2
    • 3
    • 4

    3.指针的运算

    指针是一个用数值表示的地址。可以对指针执行算术运算。可以对指针进行四种算术运算: ++、–、+、-。

    1),指针递增操作

    案例演示:

    #include 
    const MAX=3;//常量
    int main() {
        int var[]={10,100,200};//int数组
        int i,*ptr;//ptr是一个int*指针
    
        ptr=var;//ptr指向var数组的首地址
       for(i=0;i<MAX;i++){
           printf("var[%d]地址=%p\n",i,ptr);
           printf("var[%d]地址=%d\n",i,ptr);
           printf("存储值: var[%d]=%d\n",i,*ptr);
           ptr++;//ptr的值进行加1(代表的是一个int)的操作(ptr存放的值会加4个字节(int))
       }
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    相关内存图与解析;

    在这里插入图片描述

    小结:

    数组在内存中是连续分布的,当对指针进行++时,指针会按照他指向的数据类型字节数大小增加,比如int * 指针,每++。就增加4个字节

    2)指针的+、-操作

    当可以对指针按照指定的字节数大小进行+或者-的操作,可以快速定位你要的地址

    案例演示:

    #include 
    void main(){
        int var[]={10,100,200};
        int i,*ptr;
        ptr=var;
        ptr+=2;//ptr加2个(int)类型的字节数。
        printf("var[2]=%d\n var[2]的地址=%p\n ptr=%p\n ptr指向的地址的内容=%d\n",var[2],&var[2],ptr,*ptr);
    }
    //运行结果:
    var[2]=200
     var[2]的地址=000000000061FE14
     ptr=000000000061FE14
     ptr指向的地址的内容=200
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3)指针的比较

    指针可以用关系运算符进行比较,如=、<和>。如果p1和p2指向两个变量,比如同一个数组中的不同元素,则可对p1和p2进行大小比较,看下面代码

    #include 
    void main(){
    
        int var[]={10,100,200};
        int *ptr;
        ptr=var;//ptr指向var第一个元素地址
        if(ptr==var[0]){//类型不一样
            printf("ok1");
        }
        if(ptr==&var[0]){//对
            printf("\nok2");
        }
        if(ptr==var){//对(var就是第一个元素的地址)
            printf("\nok3\n");
        }
        if(ptr>=&var[1]){//错
            printf("\nok4");
        }
        printf("var=%d\n",*var);//数组名var就是一个指针
        printf("var=%p",&var[0]);
    
    //    int a=9;
    //    int *c;
    //    c=a;
    //    printf("c=%d\n",c);
    //    printf("c=%p\n",c);
    //    printf("*c=%d\n",*c);
    //
    //    printf("a=%d\n",a);
    //    printf("a=%p\n",&a);
    
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    案例2;

    #include 
    const int Max=3;
    void main(){
        int var[]={10,100,200};
        int i,*ptr;
        ptr=var;
        i=0;
        while(ptr<=&var[Max-2]){//&var[1]
            printf("Address of var[%d]=%x\n",i,ptr);
            printf("Value of var[%d]=%d\n",i,*ptr);
            ptr++;
            i++;
        }
    }
    //运行结果:
    Address of var[0]=61fe04
    Value of var[0]=10
    Address of var[1]=61fe08
    Value of var[1]=100
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    4.指针数组

    1)基本操作

    要让数组的元素指向int或其他数据类型的地址(指针)。可以使用指针数组

    2)指针数组的定义

    数据类型 *指针数组名[大小]
    比如:
    int *ptr[3];
    1)ptr声明为一个指针数组
    2)由3个整数指针组成。因此ptr中的每个元素,都是要指向 int值的指针
    
    • 1
    • 2
    • 3
    • 4
    • 5

    案例:

    #include 
    const MAXs=3;
    void main(){
        int var[]={10,100,200};
        int i,*ptr[3];//*ptr[3]是一个大小为3的指针数组
        for(i=0;i<MAXs;i++){
            ptr[i]=&var[i];//赋值为整数地址
        }
        for(i=0;i<MAXs;i++){
            printf("Value of var[%d]=%d ptr[%d]本身的地址=%p\n",i,*ptr[i],i,ptr[i]);//使用指针数组来获取各个值
        }
    }
    //运行结果:
    Value of var[0]=10 ptr[0]本身的地址=000000000061FE10
    Value of var[1]=100 ptr[1]本身的地址=000000000061FE14
    Value of var[2]=200 ptr[2]本身的地址=000000000061FE18
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    内存图解:

    在这里插入图片描述

    指针数组应用实例

    请编写程序,定义一个指向字符的指针数组来存储字符串列表(四大名著名),并通过遍历该指针数组,显示字符串信息

    #include 
    void maindemo6(){
       //定义一个指针数组,该数组的每个元素,指向的是一个字符串
       char *books[]={
               "三国演义",
               "西游记",
               "红楼梦",
               "水浒传"
              };
       int bb=90;
       int *df=&bb;
       printf("\n*df=%d\n",df);
       char a[]="abc";
       char *f=a;//a就是一个指针  a=&0xdsd
       char *pstr="abc";
       printf("pstr指向的内容: %s",pstr);
     //  printf("\nf=%s",*f);erro
       printf("\nf=%s",f);
       printf("\na=%s",a);
       printf("\na==%d",a==f);
    
       //遍历
       int i,len=4;
       for(i=0;i<4;i++){
           printf("\nbooks[%d]指向的字符串是: %s ",i,books[i]);//books[i]前面不用加*
       }
    }
    //运行结果:
    *df=6421964
    pstr指向的内容: abc
    f=abc
    a=abc
    a==1
    books[0]指向的字符串是: 三国演义 
    books[1]指向的字符串是: 西游记 
    books[2]指向的字符串是: 红楼梦 
    books[3]指向的字符串是: 水浒传 
    Process finished with exit code 32
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

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

    1)基本介绍

    指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置

    2)多重指针(二级)快速入门案例

    -1.一个指向指针的指针变量必须如下声明:

    即在变量名前放置两个星号。例如,下面声明了一个指向int类型指针的指针: int **ptr;

    -2.当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符 **ptr

    #include 
    void main(){
        int var;
        int *ptr;//一重指针
        int **pptr;//二重指针
        int ***ppptr;
        var=3000;
        ptr=&var;//var的地址给ptr
        pptr=&ptr;//将ptr的地址放在pptr
        ppptr=&pptr;//将pptr的地址放在ppptr
        printf("var的地址是: %p var=%d \n",&var,var);
        printf("ptr本身的地址是:%p ptr存放的地址是 %p *ptr=%d\n",&ptr,ptr,*ptr);
        printf("pptr本身的地址是:%p pptr存放的地址是 %p **pptr=%d\n",&pptr,pptr,**pptr);
        printf("ppptr本身的地址是:%p ppptr存放的地址是 %p **ppptr=%d\n",&ppptr,ppptr,***ppptr);
    
    }
    //运行结果
    var的地址是: 000000000061FE1C var=3000 
    ptr本身的地址是:000000000061FE10 ptr存放的地址是 000000000061FE1C *ptr=3000
    pptr本身的地址是:000000000061FE08 pptr存放的地址是 000000000061FE10 **pptr=3000
    ppptr本身的地址是:000000000061FE00 ppptr存放的地址是 000000000061FE08 **ppptr=3000
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3).内存布局图:

    在这里插入图片描述

    6.传递指针(地址)给函数

    当函数的形参是指针类型时,是使用该函数时,需要传递指针,或者地址,或者给数组给该形参,举例

    案例1:传地址或指针给指针变量

    代码

    #include 
    void test2(int *p);
    void main(){
        int i,num=90;
        int *p=&num;
        test2(&num);//传地址
        printf("\nmain中的num=%d",num);//91
        test2(p);//传指针
        printf("\nmain()中的num=%d",num);//92
    }
    void test2(int *p){
        *p+=1;//*p访问到num的值
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    案例2-传数组给指针变量

    数组名本省就代表该数组的首地址,因此传数组的本质就是传地址

    #include 
    //函数声明
    double getAverage(int *arr,int size);
    double getAverage2(int *arr,int size);
    
    void main(){
    
        int balance[5]={1000,2,3,17,50};
        double avg;
        avg=getAverage2(balance,5);
        printf("Avrage value is %f\n",avg);
    }
    //arr是一个指针
    double getAverage(int *arr,int size){
        int i,sum=0;
        double avg;
        for(i=0;i<size;i++){
            //arr[0]=arr+0
            //arr[1]=arr+1个int字节
            //arr[2]=arr+2个int字节
            sum+=arr[i];
        }
        avg=(double)sum/size;
        return avg;
    }
    //传入一个指向数组的地址变量
    double getAverage2(int *arr,int size){
        int i,sum=0;
        double avg;
        for(i=0;i<size;i++){
            sum+=*arr;
            arr++;//地址加一个int类型的大小
            printf("arr=%d\n",arr);
        }
        avg=(double)sum/size;
        return avg;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    6.返回指针的函数

    C语言允许函数的返回值是一个指针(地址),这样的函数称为指针函数

    案例:

    请编写一个函数atrlong(),返回两个字符串中较长的一个

    #include 
    #include 
    //返回指针的函数
    char *strlong(char *str1,char *str2){
        printf("\nstr1的长度%d str2的长度%d",strlen(str1),strlen(str2));
        if(strlen(str1)>=strlen(str2)){
            return str1;
        }else{
            return str2;
        }
    }
    void main(){
      char str1[30],str2[30],*str;//str是指针类型,指向一个字符串
      printf("\n请输入第一个字符串:");
      fflush(stdout);
      gets(str1);
        printf("\n请输入第二个字符串:");
        fflush(stdout);
        gets(str2);
        str=strlong(str1,str2);//返回一个地址
        printf("\nLonger string: %s\n",str);
    }
    //运行结果为:
    请输入第一个字符串:gfdsg
    
    请输入第二个字符串:FRRDGH
    
    str1的长度5 str2的长度6
    Longer string: FRRDGH
    
    Process finished with exit code 23
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    7.指针函数注意事项

    1)用指针作为函数返回值时需要注意,函数运行结束后会销毁在他内部定义的所有局部数据,包括局部变量、局部数组和形式参数,函数返回的指针不能指向这些数据。

    2)函数运行结束后会销毁该函数所有的局部数据,这里所谓的销毁并不是将局部数据所占用的****内存全部清零,而是程序放弃对他的使用权限(就是程序不使用它了,但是它还在),后面的代码可以使用这块内存(就是覆盖掉那块内存)

    #include 
    int *func(){
        int n=100;//局部变量在func返回时就会销毁掉
        return &n;
    }
    void main(){
      int *p=func();//fuunc返回指针
      int n;
      printf("okook~");//可能是使用到局部变量 int n=100占用空间,加了这句话就不能正常输出了
      n=*p;
      printf("the value is =%d\n",n);//思考是否能否输出100? 答案时不一定
    ​
    }
    ​
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3)C语言不支持在调用函数时返回局部变量的地址,如果确实有这样的需求,需要定义局部变量static变量

    #include 
    int *func(){
        //int n=100;//局部变量在func返回时就会销毁掉
        static int n=100;//如果局部变量是static性质的,那么n存放数据的空间在静态存储区
        return &n;
    }
    void main(){
      int *p=func();//fuunc返回指针
      int n;
      printf("okook~");
      n=*p;
      printf("the value is =%d\n",n);//成功输出100 一切ok!!
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    应用实例

    编写一个函数,他会生成10个随机数,并使用表示指针的数组名(第一个数组元素的指针)来返回他们;

    #include 
    #include 
    //编写一个函数,返回一个一维数组
    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=f1();//p指向是在f1生成数组的首地址(即第一个元素的地址)
       int i;
       for(i=0;i<10;i++){
           printf("\n%d",*(p+i));
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    7.函数指针(指向函数的指针)

    -1基本介绍

    1)一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会转换为该函数1所在的内存区域的首地址,这和数组名非常相似

    2)把函数的这个首地址(或称入口地址)赋予一个指针变量。使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针

    -2.函数指针的定义

    returnType (*pointerName)(param list)
    1)returnType为函数返回值类型
    2)pointerName为指针名称
    3)param list为函数参数列表
    4)参数列表中可以同时给出参数的类型和名称,也可以只给出参数的类型,省略参数的名称
    5)注意()的优先级高于*,第一个括号不能省略,如果写作returnType *pointerNmae(param list);就成了函数原形,它表明函数的返回值类型为returnType*
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    -3.应用案例

    用函数指针来实现对函数的调用,返回两个整数中的最大值

    #include 
    /*
     * 说明:
     * 1,max是一个函数
     * 2.接收两个int类型参数,返回较大数
     */
    int max(int a,int b){
        return a>b ?a:b;
    }
    void main(){
        int x,y,maxVal;
    //说明函数指针:
    /*
     * 1.函数指针的名字是pmax
     * 2.int表示该函数的指针指向的函数是返回int类型
     * 3.(int,int)表示该函数的指针指向的函数形参接收两个int
     * 4.在定义函数指针时也可以写上形参名 int *(*pmax)(int a,int b)=max也是正确的写法
     */
        int (*pmax)(int,int)=max;//设置一个指针函数名字叫pmax指向max函数
        //输入两个数
        printf("Input two numbers: ");
        fflush(stdout);
        scanf("%d %d",&x,&y);
        maxVal=(*pmax)(x,y);//将参数赋值与指针函数中
        //调用方式2:maxVal=pmax(x,y)
        printf("Max value: %d\n",maxVal);
        printf("pmax函数的本身的地址=%p\n",&pmax);
        printf("pmax函数所存的地址=%p\n",pmax);
    }
    //运行结果:
    Input two numbers: 67
    78
    Max value: 78
    pmax函数的本身的地址=000000000061FE08
    pmax函数所存的地址=0000000000401DF0
    
    Process finished with exit code 36
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    函数指针内存图:

    在这里插入图片描述

    8.回调函数

    -1.基本介绍

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

    2)简单的讲:回调函数是由别人的函数执行时调用你传入的函数(通过函数指针完成)

    -2.应用实例

    使用回调函数的方式,给一个整型数组int arr[10]赋10个随机数

    #include 
    #include 
    //回调函数
    /*
     * 1.int(*f)(void)
     * 2.f就是函数指针,它可以接收的函数(返回int,没有形参的函数)
     * 3.f在这里被initArray调用,充当了回调函数的角色
     */
    void initArray(int *array,int arraySize,int(*f)(void)){
        int i;
        //循环十次
        for(i=0;i
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    9.指针的注意事项和细节

    1)指针变量存放的是地址,从这个角度看指针的本质就是地址

    2)变量的声明的时候,如果没有确切的地址赋值,为指针变量赋值一个NULL值是最好的编程习惯

    3)赋为NULL值的指针被称为空指针,NULL指针是一个定义在标准库中的值为0的常量 #define NULL 0

    4)指针使用

    #include 
    void main(){
    
        int *p=NULL;//空指针
        int num=34;
        p=&num;
        printf("p=%d",*p);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    [10];
    //说明
    /*
    * 1.调用initArray函数,传入三个参数
    * 2.传入一个函数名 getNextRandomValue(地址:跟数组名差不多),需要使用函数指针来接收
    */
    initArray(myarray,10,getNextRandomValue);
    //输出赋值后的数组元素值:
    for(i=0;i<10;i++){
    printf(“%d “,myarray[i]);
    }
    printf(”\n”);

    }

    
    ## 9.指针的注意事项和细节
    
    1)指针变量存放的是地址,从这个角度看指针的本质就是地址
    
    2)变量的声明的时候,如果没有确切的地址赋值,为指针变量赋值一个NULL值是最好的编程习惯
    
    3)赋为NULL值的指针被称为空指针,NULL指针是一个定义在标准库中的值为0的常量 #define NULL 0
    
    4)指针使用
    
    ```c
    #include 
    void main(){
    
        int *p=NULL;//空指针
        int num=34;
        p=#
        printf("p=%d",*p);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    golang学习笔记——接口interfaces
    内存泄露分析
    自动化测试-架构真题(三十)
    记录:R语言生成热图(非相关性)
    【C++】C++继承——切片、隐藏、默认成员函数、菱形
    前端核武器:开源FrontendBlocks所见即所得编辑器让所有人都能做前端布局
    Dock搭建FTP文件服务器
    如何建设一个安全运营中心(SOC)?
    新唐NUC980使用记录:U-Boot & Linux 编译与烧录(基于SPI NAND)
    详解Postman使用
  • 原文地址:https://blog.csdn.net/weixin_55418082/article/details/126431220